Compare commits
73 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84300e00ff | ||
|
|
fbee95a668 | ||
|
|
afcf5c0ee0 | ||
|
|
0c30768288 | ||
|
|
80295f335b | ||
|
|
465943adf7 | ||
|
|
38eed292e4 | ||
|
|
883e650a35 | ||
|
|
eb1b5e5454 | ||
|
|
8e558a3458 | ||
|
|
4e4643aa5d | ||
|
|
b19258a243 | ||
|
|
7fc42f8f85 | ||
|
|
babe1eb7be | ||
|
|
608b2191aa | ||
|
|
3939c2dbf7 | ||
|
|
20a9252e92 | ||
|
|
a0e3347e43 | ||
|
|
9e704a7c63 | ||
|
|
c9da98e0b7 | ||
|
|
5377d24507 | ||
|
|
db8e4500ee | ||
|
|
bd2de5624e | ||
|
|
3a81f893cc | ||
|
|
fd6dc2a343 | ||
|
|
8693236f9e | ||
|
|
44e2b6208a | ||
|
|
16c81f75c2 | ||
|
|
e1d6ac3265 | ||
|
|
3aec1100f5 | ||
|
|
c00df647e1 | ||
|
|
f012877be1 | ||
|
|
ff6defc988 | ||
|
|
67ca50e9f2 | ||
|
|
6cc160bc2b | ||
|
|
4bdf506d80 | ||
|
|
4af2353ef9 | ||
|
|
6072edf5bf | ||
|
|
4061eeeb32 | ||
|
|
bea6deb0c3 | ||
|
|
81db00a3c4 | ||
|
|
cf56955ba6 | ||
|
|
8a8939afd8 | ||
|
|
6acf2accc6 | ||
|
|
ec0c7647ab | ||
|
|
045229630e | ||
|
|
c600991905 | ||
|
|
f6a93a4c3d | ||
|
|
de54ff114e | ||
|
|
64b398c72b | ||
|
|
c99bd3fa60 | ||
|
|
8ac930f886 | ||
|
|
ad80fdc2cd | ||
|
|
a0ea8fe22f | ||
|
|
3c3da8a88c | ||
|
|
16e79c8db6 | ||
|
|
8f6d8e215c | ||
|
|
8993baab01 | ||
|
|
2568627c4c | ||
|
|
9603a024b3 | ||
|
|
a122d95ef5 | ||
|
|
6ddfe50ac4 | ||
|
|
26901a78c9 | ||
|
|
6649225167 | ||
|
|
9e3083aa2c | ||
|
|
6d11ff3822 | ||
|
|
6cf770a692 | ||
|
|
3534e370e1 | ||
|
|
dbcab5128c | ||
|
|
3810250bb6 | ||
|
|
3c1c1e1dd3 | ||
|
|
5b7bd93b91 | ||
|
|
9e096b4a4c |
19
.github/workflows/ci.yaml
vendored
19
.github/workflows/ci.yaml
vendored
@@ -70,7 +70,7 @@ jobs:
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: nightly-2022-11-01
|
||||
toolchain: 1.65.0
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- run: cargo install cargo-insta
|
||||
@@ -84,6 +84,21 @@ jobs:
|
||||
# Setting RUSTDOCFLAGS because `cargo doc --check` isn't yet implemented (https://github.com/rust-lang/cargo/issues/10025).
|
||||
- run: RUSTDOCFLAGS="-D warnings" cargo doc --all --no-deps
|
||||
|
||||
scripts:
|
||||
name: "test scripts"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- run: ./scripts/add_rule.py --name DoTheThing --code PLC999 --linter pylint
|
||||
- run: cargo check
|
||||
- run: ./scripts/add_plugin.py test --url https://pypi.org/project/-test/0.1.0/
|
||||
- run: cargo check
|
||||
|
||||
# TODO(charlie): Re-enable the `wasm-pack` tests.
|
||||
# See: https://github.com/charliermarsh/ruff/issues/1425
|
||||
# wasm-pack-test:
|
||||
@@ -122,7 +137,7 @@ jobs:
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: nightly-2022-11-01
|
||||
toolchain: 1.65.0
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- uses: actions/setup-python@v4
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.226
|
||||
rev: v0.0.229
|
||||
hooks:
|
||||
- id: ruff
|
||||
|
||||
|
||||
@@ -37,13 +37,16 @@ After cloning the repository, run Ruff locally with:
|
||||
cargo run resources/test/fixtures --no-cache
|
||||
```
|
||||
|
||||
Prior to opening a pull request, ensure that your code has been auto-formatted, and that it passes
|
||||
both the lint and test validation checks:
|
||||
Prior to opening a pull request, ensure that your code has been auto-formatted,
|
||||
and that it passes both the lint and test validation checks.
|
||||
|
||||
For rustfmt and Clippy, we use [nightly Rust][nightly], as it is stricter than stable Rust.
|
||||
(However, tests and builds use stable Rust.)
|
||||
|
||||
```shell
|
||||
cargo +nightly fmt --all # Auto-formatting...
|
||||
cargo +nightly clippy --all # Linting...
|
||||
cargo +nightly test --all # Testing...
|
||||
cargo +nightly clippy --fix --workspace --all-targets --all-features -- -W clippy::pedantic # Linting...
|
||||
cargo test --all # Testing...
|
||||
```
|
||||
|
||||
These checks will run on GitHub Actions when you open your Pull Request, but running them locally
|
||||
@@ -54,18 +57,22 @@ prior to merging.
|
||||
|
||||
### Example: Adding a new lint rule
|
||||
|
||||
There are four phases to adding a new lint rule:
|
||||
At a high level, the steps involved in adding a new lint rule are as follows:
|
||||
|
||||
1. Define the violation struct in `src/violations.rs` (e.g., `ModuleImportNotAtTopOfFile`).
|
||||
2. Map the violation struct to a rule code in `src/registry.rs` (e.g., `E402`).
|
||||
3. Define the logic for triggering the violation in `src/checkers/ast.rs` (for AST-based checks),
|
||||
`src/checkers/tokens.rs` (for token-based checks), `src/checkers/lines.rs` (for text-based checks) or `src/checkers/filesystem.rs` (for filesystem-based checks).
|
||||
4. Add a test fixture.
|
||||
5. Update the generated files (documentation and generated code).
|
||||
1. Create a file for your rule (e.g., `src/rules/flake8_bugbear/rules/abstract_base_class.rs`).
|
||||
2. In that file, define a violation struct. You can grep for `define_violation!` to see examples.
|
||||
3. Map the violation struct to a rule code in `src/registry.rs` (e.g., `E402`).
|
||||
4. Define the logic for triggering the violation in `src/checkers/ast.rs` (for AST-based checks),
|
||||
`src/checkers/tokens.rs` (for token-based checks), `src/checkers/lines.rs` (for text-based
|
||||
checks), or `src/checkers/filesystem.rs` (for filesystem-based checks).
|
||||
5. Add a test fixture.
|
||||
6. Update the generated files (documentation and generated code).
|
||||
|
||||
To define the violation, open up `src/violations.rs`, and define a new struct using the
|
||||
`define_violation!` macro. There are plenty of examples in that file, so feel free to pattern-match
|
||||
against the existing structs.
|
||||
To define the violation, start by creating a dedicated file for your rule under the appropriate
|
||||
rule linter (e.g., `src/rules/flake8_bugbear/rules/abstract_base_class.rs`). That file should
|
||||
contain a struct defined via `define_violation!`, along with a function that creates the violation
|
||||
based on any required inputs. (Many of the existing examples live in `src/violations.rs`, but we're
|
||||
looking to place new rules in their own files.)
|
||||
|
||||
To trigger the violation, you'll likely want to augment the logic in `src/checkers/ast.rs`, which
|
||||
defines the Python AST visitor, responsible for iterating over the abstract syntax tree and
|
||||
@@ -74,7 +81,7 @@ collecting diagnostics as it goes.
|
||||
If you need to inspect the AST, you can run `cargo +nightly dev print-ast` with a Python file. Grep
|
||||
for the `Check::new` invocations to understand how other, similar rules are implemented.
|
||||
|
||||
To add a test fixture, create a file under `resources/test/fixtures/[origin]`, named to match
|
||||
To add a test fixture, create a file under `resources/test/fixtures/[linter]`, named to match
|
||||
the code you defined earlier (e.g., `resources/test/fixtures/pycodestyle/E402.py`). This file should
|
||||
contain a variety of violations and non-violations designed to evaluate and demonstrate the behavior
|
||||
of your lint rule.
|
||||
@@ -83,7 +90,7 @@ Run `cargo +nightly dev generate-all` to generate the code for your new fixture.
|
||||
locally with (e.g.) `cargo run resources/test/fixtures/pycodestyle/E402.py --no-cache --select E402`.
|
||||
|
||||
Once you're satisfied with the output, codify the behavior as a snapshot test by adding a new
|
||||
`test_case` macro in the relevant `src/[origin]/mod.rs` file. Then, run `cargo test --all`.
|
||||
`test_case` macro in the relevant `src/[linter]/mod.rs` file. Then, run `cargo test --all`.
|
||||
Your test will fail, but you'll be prompted to follow-up with `cargo insta review`. Accept the
|
||||
generated snapshot, then commit the snapshot file alongside the rest of your changes.
|
||||
|
||||
@@ -123,3 +130,5 @@ them to [PyPI](https://pypi.org/project/ruff/).
|
||||
|
||||
Ruff follows the [semver](https://semver.org/) versioning standard. However, as pre-1.0 software,
|
||||
even patch releases may contain [non-backwards-compatible changes](https://semver.org/#spec-item-4).
|
||||
|
||||
[nightly]: https://rust-lang.github.io/rustup/concepts/channels.html#working-with-nightly-rust
|
||||
|
||||
339
Cargo.lock
generated
339
Cargo.lock
generated
@@ -14,7 +14,7 @@ version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||
dependencies = [
|
||||
"getrandom 0.2.8",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
@@ -197,12 +197,6 @@ version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
@@ -413,7 +407,7 @@ version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
@@ -439,7 +433,7 @@ version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -484,7 +478,7 @@ version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
@@ -494,7 +488,7 @@ version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
@@ -506,7 +500,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"memoffset",
|
||||
"scopeguard",
|
||||
@@ -518,7 +512,7 @@ version = "0.8.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -592,16 +586,6 @@ dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "4.0.0"
|
||||
@@ -617,7 +601,7 @@ version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"dirs-sys-next",
|
||||
]
|
||||
|
||||
@@ -721,7 +705,7 @@ version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"windows-sys",
|
||||
@@ -735,7 +719,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.226"
|
||||
version = "0.0.229"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.32",
|
||||
@@ -786,24 +770,13 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"wasi 0.9.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
@@ -926,6 +899,16 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imperative"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f92123bf2fe0d9f1b5df1964727b970ca3b2d0203d47cf97fb1f36d856b6398"
|
||||
dependencies = [
|
||||
"phf 0.11.1",
|
||||
"rust-stemmers",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.2"
|
||||
@@ -976,7 +959,7 @@ version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1186,7 +1169,7 @@ version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1265,7 +1248,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46a58d1d356c6597d08cde02c2f09d785b09e28711837b1ed667dc652c08a694"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"static_assertions",
|
||||
]
|
||||
@@ -1419,7 +1402,7 @@ version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
@@ -1493,15 +1476,6 @@ dependencies = [
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
|
||||
dependencies = [
|
||||
"phf_shared 0.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.10.1"
|
||||
@@ -1512,13 +1486,12 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.8.0"
|
||||
name = "phf"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815"
|
||||
checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c"
|
||||
dependencies = [
|
||||
"phf_generator 0.8.0",
|
||||
"phf_shared 0.8.0",
|
||||
"phf_shared 0.11.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1532,13 +1505,13 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.8.0"
|
||||
name = "phf_codegen"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526"
|
||||
checksum = "a56ac890c5e3ca598bbdeaa99964edb5b0258a583a9eb6ef4e89fc85d9224770"
|
||||
dependencies = [
|
||||
"phf_shared 0.8.0",
|
||||
"rand 0.7.3",
|
||||
"phf_generator 0.11.1",
|
||||
"phf_shared 0.11.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1548,16 +1521,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
|
||||
dependencies = [
|
||||
"phf_shared 0.10.0",
|
||||
"rand 0.8.5",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.8.0"
|
||||
name = "phf_generator"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
|
||||
checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
"phf_shared 0.11.1",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1569,6 +1543,15 @@ dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pico-args"
|
||||
version = "0.4.2"
|
||||
@@ -1724,20 +1707,6 @@ version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
||||
dependencies = [
|
||||
"getrandom 0.1.16",
|
||||
"libc",
|
||||
"rand_chacha 0.2.2",
|
||||
"rand_core 0.5.1",
|
||||
"rand_hc",
|
||||
"rand_pcg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
@@ -1745,18 +1714,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha 0.3.1",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.5.1",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1766,16 +1725,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||
dependencies = [
|
||||
"getrandom 0.1.16",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1784,25 +1734,7 @@ version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom 0.2.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
|
||||
dependencies = [
|
||||
"rand_core 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_pcg"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
|
||||
dependencies = [
|
||||
"rand_core 0.5.1",
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1842,7 +1774,7 @@ version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
|
||||
dependencies = [
|
||||
"getrandom 0.2.8",
|
||||
"getrandom",
|
||||
"redox_syscall",
|
||||
"thiserror",
|
||||
]
|
||||
@@ -1906,23 +1838,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.226"
|
||||
version = "0.0.229"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"chrono",
|
||||
"clap 4.0.32",
|
||||
"colored",
|
||||
"console_error_panic_hook",
|
||||
"console_log",
|
||||
"criterion",
|
||||
"dirs 4.0.0",
|
||||
"dirs",
|
||||
"fern",
|
||||
"getrandom 0.2.8",
|
||||
"getrandom",
|
||||
"glob",
|
||||
"globset",
|
||||
"ignore",
|
||||
"imperative",
|
||||
"insta",
|
||||
"itertools",
|
||||
"js-sys",
|
||||
@@ -1938,9 +1871,9 @@ dependencies = [
|
||||
"ropey",
|
||||
"ruff_macros",
|
||||
"rustc-hash",
|
||||
"rustpython-ast",
|
||||
"rustpython-common",
|
||||
"rustpython-parser",
|
||||
"rustpython-ast 0.2.0 (git+https://github.com/RustPython/RustPython.git?rev=62aa942bf506ea3d41ed0503b947b84141fdaa3c)",
|
||||
"rustpython-common 0.2.0 (git+https://github.com/RustPython/RustPython.git?rev=62aa942bf506ea3d41ed0503b947b84141fdaa3c)",
|
||||
"rustpython-parser 0.2.0 (git+https://github.com/RustPython/RustPython.git?rev=62aa942bf506ea3d41ed0503b947b84141fdaa3c)",
|
||||
"schemars",
|
||||
"semver",
|
||||
"serde",
|
||||
@@ -1951,6 +1884,7 @@ dependencies = [
|
||||
"strum_macros",
|
||||
"test-case",
|
||||
"textwrap",
|
||||
"thiserror",
|
||||
"titlecase",
|
||||
"toml_edit",
|
||||
"wasm-bindgen",
|
||||
@@ -1959,7 +1893,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_cli"
|
||||
version = "0.0.226"
|
||||
version = "0.0.229"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
@@ -1996,7 +1930,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.226"
|
||||
version = "0.0.229"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.32",
|
||||
@@ -2005,9 +1939,9 @@ dependencies = [
|
||||
"once_cell",
|
||||
"ruff",
|
||||
"ruff_cli",
|
||||
"rustpython-ast",
|
||||
"rustpython-common",
|
||||
"rustpython-parser",
|
||||
"rustpython-ast 0.2.0 (git+https://github.com/RustPython/RustPython.git?rev=ff90fe52eea578c8ebdd9d95e078cc041a5959fa)",
|
||||
"rustpython-common 0.2.0 (git+https://github.com/RustPython/RustPython.git?rev=ff90fe52eea578c8ebdd9d95e078cc041a5959fa)",
|
||||
"rustpython-parser 0.2.0 (git+https://github.com/RustPython/RustPython.git?rev=ff90fe52eea578c8ebdd9d95e078cc041a5959fa)",
|
||||
"schemars",
|
||||
"serde_json",
|
||||
"strum",
|
||||
@@ -2017,7 +1951,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.226"
|
||||
version = "0.0.229"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
@@ -2026,6 +1960,16 @@ dependencies = [
|
||||
"textwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-stemmers"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e46a2036019fdb888131db7a4c847a1063a7493f971ed94ea82c67eada63ca54"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
@@ -2061,21 +2005,31 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-ast"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=acbc517b55406c76da83d7b2711941d8d3f65b87#acbc517b55406c76da83d7b2711941d8d3f65b87"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=62aa942bf506ea3d41ed0503b947b84141fdaa3c#62aa942bf506ea3d41ed0503b947b84141fdaa3c"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"rustpython-common",
|
||||
"rustpython-compiler-core",
|
||||
"rustpython-common 0.2.0 (git+https://github.com/RustPython/RustPython.git?rev=62aa942bf506ea3d41ed0503b947b84141fdaa3c)",
|
||||
"rustpython-compiler-core 0.2.0 (git+https://github.com/RustPython/RustPython.git?rev=62aa942bf506ea3d41ed0503b947b84141fdaa3c)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustpython-ast"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=ff90fe52eea578c8ebdd9d95e078cc041a5959fa#ff90fe52eea578c8ebdd9d95e078cc041a5959fa"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"rustpython-common 0.2.0 (git+https://github.com/RustPython/RustPython.git?rev=ff90fe52eea578c8ebdd9d95e078cc041a5959fa)",
|
||||
"rustpython-compiler-core 0.2.0 (git+https://github.com/RustPython/RustPython.git?rev=ff90fe52eea578c8ebdd9d95e078cc041a5959fa)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustpython-common"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=acbc517b55406c76da83d7b2711941d8d3f65b87#acbc517b55406c76da83d7b2711941d8d3f65b87"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=62aa942bf506ea3d41ed0503b947b84141fdaa3c#62aa942bf506ea3d41ed0503b947b84141fdaa3c"
|
||||
dependencies = [
|
||||
"ascii",
|
||||
"bitflags",
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"hexf-parse",
|
||||
"itertools",
|
||||
"lexical-parse-float",
|
||||
@@ -2086,7 +2040,32 @@ dependencies = [
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"radium",
|
||||
"rand 0.8.5",
|
||||
"rand",
|
||||
"siphasher",
|
||||
"unic-ucd-category",
|
||||
"volatile",
|
||||
"widestring",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustpython-common"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=ff90fe52eea578c8ebdd9d95e078cc041a5959fa#ff90fe52eea578c8ebdd9d95e078cc041a5959fa"
|
||||
dependencies = [
|
||||
"ascii",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"hexf-parse",
|
||||
"itertools",
|
||||
"lexical-parse-float",
|
||||
"libc",
|
||||
"lock_api",
|
||||
"num-bigint",
|
||||
"num-complex",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"radium",
|
||||
"rand",
|
||||
"siphasher",
|
||||
"unic-ucd-category",
|
||||
"volatile",
|
||||
@@ -2096,7 +2075,24 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-compiler-core"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=acbc517b55406c76da83d7b2711941d8d3f65b87#acbc517b55406c76da83d7b2711941d8d3f65b87"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=62aa942bf506ea3d41ed0503b947b84141fdaa3c#62aa942bf506ea3d41ed0503b947b84141fdaa3c"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"bitflags",
|
||||
"bstr 0.2.17",
|
||||
"itertools",
|
||||
"lz4_flex",
|
||||
"num-bigint",
|
||||
"num-complex",
|
||||
"num_enum",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustpython-compiler-core"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=ff90fe52eea578c8ebdd9d95e078cc041a5959fa#ff90fe52eea578c8ebdd9d95e078cc041a5959fa"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"bitflags",
|
||||
@@ -2113,7 +2109,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-parser"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=acbc517b55406c76da83d7b2711941d8d3f65b87#acbc517b55406c76da83d7b2711941d8d3f65b87"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=62aa942bf506ea3d41ed0503b947b84141fdaa3c#62aa942bf506ea3d41ed0503b947b84141fdaa3c"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"anyhow",
|
||||
@@ -2126,8 +2122,33 @@ dependencies = [
|
||||
"phf 0.10.1",
|
||||
"phf_codegen 0.10.0",
|
||||
"rustc-hash",
|
||||
"rustpython-ast",
|
||||
"rustpython-compiler-core",
|
||||
"rustpython-ast 0.2.0 (git+https://github.com/RustPython/RustPython.git?rev=62aa942bf506ea3d41ed0503b947b84141fdaa3c)",
|
||||
"rustpython-compiler-core 0.2.0 (git+https://github.com/RustPython/RustPython.git?rev=62aa942bf506ea3d41ed0503b947b84141fdaa3c)",
|
||||
"thiserror",
|
||||
"tiny-keccak",
|
||||
"unic-emoji-char",
|
||||
"unic-ucd-ident",
|
||||
"unicode_names2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustpython-parser"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=ff90fe52eea578c8ebdd9d95e078cc041a5959fa#ff90fe52eea578c8ebdd9d95e078cc041a5959fa"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"anyhow",
|
||||
"itertools",
|
||||
"lalrpop",
|
||||
"lalrpop-util",
|
||||
"log",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"phf 0.10.1",
|
||||
"phf_codegen 0.10.0",
|
||||
"rustc-hash",
|
||||
"rustpython-ast 0.2.0 (git+https://github.com/RustPython/RustPython.git?rev=ff90fe52eea578c8ebdd9d95e078cc041a5959fa)",
|
||||
"rustpython-compiler-core 0.2.0 (git+https://github.com/RustPython/RustPython.git?rev=ff90fe52eea578c8ebdd9d95e078cc041a5959fa)",
|
||||
"thiserror",
|
||||
"tiny-keccak",
|
||||
"unic-emoji-char",
|
||||
@@ -2273,7 +2294,7 @@ version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd1c7ddea665294d484c39fd0c0d2b7e35bbfe10035c5fe1854741a57f6880e1"
|
||||
dependencies = [
|
||||
"dirs 4.0.0",
|
||||
"dirs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2376,7 +2397,7 @@ version = "3.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
@@ -2416,15 +2437,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "terminfo"
|
||||
version = "0.7.3"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76971977e6121664ec1b960d1313aacfa75642adc93b9d4d53b247bd4cb1747e"
|
||||
checksum = "da31aef70da0f6352dbcb462683eb4dd2bfad01cf3fc96cf204547b9a839a585"
|
||||
dependencies = [
|
||||
"dirs 2.0.2",
|
||||
"dirs",
|
||||
"fnv",
|
||||
"nom",
|
||||
"phf 0.8.0",
|
||||
"phf_codegen 0.8.0",
|
||||
"phf 0.11.1",
|
||||
"phf_codegen 0.11.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2448,7 +2469,7 @@ version = "2.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e45b7bf6e19353ddd832745c8fcf77a17a93171df7151187f26623f2b75b5b26"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2588,7 +2609,7 @@ version = "1.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
@@ -2792,12 +2813,6 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
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"
|
||||
@@ -2816,7 +2831,7 @@ version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
@@ -2841,7 +2856,7 @@ version = "0.4.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
|
||||
12
Cargo.toml
12
Cargo.toml
@@ -8,7 +8,7 @@ default-members = [".", "ruff_cli"]
|
||||
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.226"
|
||||
version = "0.0.229"
|
||||
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.65.0"
|
||||
@@ -35,6 +35,7 @@ fern = { version = "0.6.1" }
|
||||
glob = { version = "0.3.0" }
|
||||
globset = { version = "0.4.9" }
|
||||
ignore = { version = "0.4.18" }
|
||||
imperative = { version = "1.0.3" }
|
||||
itertools = { version = "0.10.5" }
|
||||
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a8725d161fe8b3ed73a6758b21e177" }
|
||||
log = { version = "0.4.17" }
|
||||
@@ -46,11 +47,11 @@ once_cell = { version = "1.16.0" }
|
||||
path-absolutize = { version = "3.0.14", features = ["once_cell_cache", "use_unix_paths_on_wasm"] }
|
||||
regex = { version = "1.6.0" }
|
||||
ropey = { version = "1.5.0", features = ["cr_lines", "simd"], default-features = false }
|
||||
ruff_macros = { version = "0.0.226", path = "ruff_macros" }
|
||||
ruff_macros = { version = "0.0.229", path = "ruff_macros" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "acbc517b55406c76da83d7b2711941d8d3f65b87" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "acbc517b55406c76da83d7b2711941d8d3f65b87" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "acbc517b55406c76da83d7b2711941d8d3f65b87" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "62aa942bf506ea3d41ed0503b947b84141fdaa3c" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "62aa942bf506ea3d41ed0503b947b84141fdaa3c" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "62aa942bf506ea3d41ed0503b947b84141fdaa3c" }
|
||||
schemars = { version = "0.8.11" }
|
||||
semver = { version = "1.0.16" }
|
||||
serde = { version = "1.0.147", features = ["derive"] }
|
||||
@@ -59,6 +60,7 @@ smallvec = { version = "1.10.0" }
|
||||
strum = { version = "0.24.1", features = ["strum_macros"] }
|
||||
strum_macros = { version = "0.24.3" }
|
||||
textwrap = { version = "0.16.0" }
|
||||
thiserror = { version = "1.0" }
|
||||
titlecase = { version = "2.2.1" }
|
||||
toml_edit = { version = "0.17.1", features = ["easy"] }
|
||||
|
||||
|
||||
18
build.rs
18
build.rs
@@ -4,7 +4,7 @@ use std::path::{Path, PathBuf};
|
||||
|
||||
fn main() {
|
||||
let out_dir = PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
|
||||
generate_origin_name_and_url(&out_dir);
|
||||
generate_linter_name_and_url(&out_dir);
|
||||
}
|
||||
|
||||
const RULES_SUBMODULE_DOC_PREFIX: &str = "//! Rules from ";
|
||||
@@ -15,13 +15,13 @@ const RULES_SUBMODULE_DOC_PREFIX: &str = "//! Rules from ";
|
||||
/// //! Rules from [Pyflakes](https://pypi.org/project/pyflakes/2.5.0/).
|
||||
///
|
||||
/// This function extracts the link label and url from these comments and
|
||||
/// generates the `name` and `url` functions for the `RuleOrigin` enum
|
||||
/// generates the `name` and `url` functions for the `Linter` enum
|
||||
/// accordingly, so that they can be used by `ruff_dev::generate_rules_table`.
|
||||
fn generate_origin_name_and_url(out_dir: &Path) {
|
||||
fn generate_linter_name_and_url(out_dir: &Path) {
|
||||
println!("cargo:rerun-if-changed=src/rules/");
|
||||
|
||||
let mut name_match_arms: String = r#"RuleOrigin::Ruff => "Ruff-specific rules","#.into();
|
||||
let mut url_match_arms: String = r#"RuleOrigin::Ruff => None,"#.into();
|
||||
let mut name_match_arms: String = r#"Linter::Ruff => "Ruff-specific rules","#.into();
|
||||
let mut url_match_arms: String = r#"Linter::Ruff => None,"#.into();
|
||||
|
||||
for file in fs::read_dir("src/rules/")
|
||||
.unwrap()
|
||||
@@ -62,14 +62,14 @@ fn generate_origin_name_and_url(out_dir: &Path) {
|
||||
})
|
||||
.collect::<String>();
|
||||
|
||||
name_match_arms.push_str(&format!(r#"RuleOrigin::{variant_name} => "{name}","#));
|
||||
url_match_arms.push_str(&format!(r#"RuleOrigin::{variant_name} => Some("{url}"),"#));
|
||||
name_match_arms.push_str(&format!(r#"Linter::{variant_name} => "{name}","#));
|
||||
url_match_arms.push_str(&format!(r#"Linter::{variant_name} => Some("{url}"),"#));
|
||||
}
|
||||
|
||||
write!(
|
||||
BufWriter::new(fs::File::create(out_dir.join("origin.rs")).unwrap()),
|
||||
BufWriter::new(fs::File::create(out_dir.join("linter.rs")).unwrap()),
|
||||
"
|
||||
impl RuleOrigin {{
|
||||
impl Linter {{
|
||||
pub fn name(&self) -> &'static str {{
|
||||
match self {{ {name_match_arms} }}
|
||||
}}
|
||||
|
||||
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.226"
|
||||
version = "0.0.229"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -1975,7 +1975,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.226"
|
||||
version = "0.0.229"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.226"
|
||||
version = "0.0.229"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -7,7 +7,7 @@ build-backend = "maturin"
|
||||
|
||||
[project]
|
||||
name = "ruff"
|
||||
version = "0.0.226"
|
||||
version = "0.0.229"
|
||||
description = "An extremely fast Python linter, written in Rust."
|
||||
authors = [
|
||||
{ name = "Charlie Marsh", email = "charlie.r.marsh@gmail.com" },
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import some as sum
|
||||
from some import other as int
|
||||
from directory import new as dir
|
||||
|
||||
print = 1
|
||||
copyright: 'annotation' = 2
|
||||
@@ -7,6 +8,8 @@ copyright: 'annotation' = 2
|
||||
float = object = 4
|
||||
min, max = 5, 6
|
||||
|
||||
id = 4
|
||||
|
||||
def bytes():
|
||||
pass
|
||||
|
||||
|
||||
@@ -5,5 +5,7 @@ def func1(str, /, type, *complex, Exception, **getattr):
|
||||
async def func2(bytes):
|
||||
pass
|
||||
|
||||
async def func3(id, dir):
|
||||
pass
|
||||
|
||||
map([], lambda float: ...)
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
class MyClass:
|
||||
ImportError = 4
|
||||
id = 5
|
||||
dir = "/"
|
||||
|
||||
def __init__(self):
|
||||
self.float = 5 # is fine
|
||||
self.id = 10
|
||||
self.dir = "."
|
||||
|
||||
def str(self):
|
||||
pass
|
||||
|
||||
@@ -383,7 +383,7 @@ s = (
|
||||
|
||||
s2 = (
|
||||
'this'
|
||||
'is a also a string'
|
||||
'is also a string'
|
||||
)
|
||||
|
||||
t = (
|
||||
|
||||
2
resources/test/fixtures/flake8_executable/EXE003.py
vendored
Normal file
2
resources/test/fixtures/flake8_executable/EXE003.py
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/usr/bin/bash
|
||||
print("hello world")
|
||||
1
resources/test/fixtures/flake8_executable/EXE004_1.py
vendored
Normal file
1
resources/test/fixtures/flake8_executable/EXE004_1.py
vendored
Normal file
@@ -0,0 +1 @@
|
||||
#!/usr/bin/python
|
||||
2
resources/test/fixtures/flake8_executable/EXE004_2.py
vendored
Normal file
2
resources/test/fixtures/flake8_executable/EXE004_2.py
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
print(" #!/usr/bin/python")
|
||||
# shebang outside of comments should be ignored
|
||||
2
resources/test/fixtures/flake8_executable/EXE004_3.py
vendored
Normal file
2
resources/test/fixtures/flake8_executable/EXE004_3.py
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
pass #!/usr/bin/env python
|
||||
3
resources/test/fixtures/flake8_executable/EXE005_1.py
vendored
Normal file
3
resources/test/fixtures/flake8_executable/EXE005_1.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
|
||||
# A python comment
|
||||
#!/usr/bin/python
|
||||
4
resources/test/fixtures/flake8_executable/EXE005_2.py
vendored
Normal file
4
resources/test/fixtures/flake8_executable/EXE005_2.py
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
print("code prior to shebang")
|
||||
|
||||
# A python comment
|
||||
#!/usr/bin/python
|
||||
6
resources/test/fixtures/flake8_executable/EXE005_3.py
vendored
Normal file
6
resources/test/fixtures/flake8_executable/EXE005_3.py
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
"""
|
||||
With a docstring
|
||||
print("commented out code")
|
||||
"""
|
||||
# A python comment
|
||||
#!/usr/bin/python
|
||||
@@ -53,3 +53,25 @@ def test_list_of_tuples(param1, param2):
|
||||
)
|
||||
def test_list_of_lists(param1, param2):
|
||||
...
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param1,param2",
|
||||
[
|
||||
[1, 2],
|
||||
[3, 4],
|
||||
],
|
||||
)
|
||||
def test_csv_name_list_of_lists(param1, param2):
|
||||
...
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param",
|
||||
[
|
||||
[1, 2],
|
||||
[3, 4],
|
||||
],
|
||||
)
|
||||
def test_single_list_of_lists(param):
|
||||
...
|
||||
|
||||
@@ -3,6 +3,12 @@ if a:
|
||||
if b:
|
||||
c
|
||||
|
||||
# SIM102
|
||||
if a:
|
||||
if b:
|
||||
if c:
|
||||
d
|
||||
|
||||
# SIM102
|
||||
if a:
|
||||
pass
|
||||
|
||||
@@ -42,3 +42,11 @@ def f():
|
||||
return "foo"
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def f():
|
||||
# SIM103 (but not fixable)
|
||||
if a:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@@ -1,7 +1,31 @@
|
||||
# Bad
|
||||
# SIM109
|
||||
if a == b or a == c:
|
||||
d
|
||||
|
||||
# Good
|
||||
# SIM109
|
||||
if (a == b or a == c) and None:
|
||||
d
|
||||
|
||||
# SIM109
|
||||
if a == b or a == c or None:
|
||||
d
|
||||
|
||||
# SIM109
|
||||
if a == b or None or a == c:
|
||||
d
|
||||
|
||||
# OK
|
||||
if a in (b, c):
|
||||
d
|
||||
d
|
||||
|
||||
# OK
|
||||
if a == b or a == c():
|
||||
d
|
||||
|
||||
# OK
|
||||
if (
|
||||
a == b
|
||||
# This comment prevents us from raising SIM109
|
||||
or a == c
|
||||
):
|
||||
d
|
||||
|
||||
@@ -1,17 +1,27 @@
|
||||
if not a == b: # SIM201
|
||||
# SIM201
|
||||
if not a == b:
|
||||
pass
|
||||
|
||||
if not a == (b + c): # SIM201
|
||||
# SIM201
|
||||
if not a == (b + c):
|
||||
pass
|
||||
|
||||
if not (a + b) == c: # SIM201
|
||||
# SIM201
|
||||
if not (a + b) == c:
|
||||
pass
|
||||
|
||||
if not a != b: # OK
|
||||
# OK
|
||||
if not a != b:
|
||||
pass
|
||||
|
||||
if a == b: # OK
|
||||
# OK
|
||||
if a == b:
|
||||
pass
|
||||
|
||||
if not a == b: # OK
|
||||
# OK
|
||||
if not a == b:
|
||||
raise ValueError()
|
||||
|
||||
# OK
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
@@ -1,14 +1,27 @@
|
||||
if not a != b: # SIM202
|
||||
# SIM202
|
||||
if not a != b:
|
||||
pass
|
||||
|
||||
if not a != (b + c): # SIM202
|
||||
# SIM202
|
||||
if not a != (b + c):
|
||||
pass
|
||||
|
||||
if not (a + b) != c: # SIM202
|
||||
# SIM202
|
||||
if not (a + b) != c:
|
||||
pass
|
||||
|
||||
if not a == b: # OK
|
||||
# OK
|
||||
if not a == b:
|
||||
pass
|
||||
|
||||
if a != b: # OK
|
||||
# OK
|
||||
if a != b:
|
||||
pass
|
||||
|
||||
# OK
|
||||
if not a != b:
|
||||
raise ValueError()
|
||||
|
||||
# OK
|
||||
def __eq__(self, other):
|
||||
return not self != other
|
||||
|
||||
@@ -91,3 +91,20 @@ if key in a_dict:
|
||||
var = a_dict[key]
|
||||
else:
|
||||
var = a_dict["fallback"]
|
||||
|
||||
# OK (false negative for elif)
|
||||
if foo():
|
||||
pass
|
||||
elif key in a_dict:
|
||||
vars[idx] = a_dict[key]
|
||||
else:
|
||||
vars[idx] = "default"
|
||||
|
||||
# OK (false negative for nested else)
|
||||
if foo():
|
||||
pass
|
||||
else:
|
||||
if key in a_dict:
|
||||
vars[idx] = a_dict[key]
|
||||
else:
|
||||
vars[idx] = "default"
|
||||
|
||||
25
resources/test/fixtures/flake8_type_checking/TYP005.py
vendored
Normal file
25
resources/test/fixtures/flake8_type_checking/TYP005.py
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
from typing import TYPE_CHECKING, List
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass # TYP005
|
||||
|
||||
|
||||
def example():
|
||||
if TYPE_CHECKING:
|
||||
pass # TYP005
|
||||
return
|
||||
|
||||
|
||||
class Test:
|
||||
if TYPE_CHECKING:
|
||||
pass # TYP005
|
||||
x = 2
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
if 2:
|
||||
pass
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
x: List
|
||||
19
resources/test/fixtures/pycodestyle/E101.py
vendored
Normal file
19
resources/test/fixtures/pycodestyle/E101.py
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
def func_all_spaces():
|
||||
# No error
|
||||
print("spaces")
|
||||
|
||||
def func_tabs():
|
||||
# No error
|
||||
print("tabs")
|
||||
|
||||
def func_mixed_start_with_tab():
|
||||
# E101
|
||||
print("mixed starts with tab")
|
||||
|
||||
def func_mixed_start_with_space():
|
||||
# E101
|
||||
print("mixed starts with space")
|
||||
|
||||
def xyz():
|
||||
# E101
|
||||
print("xyz");
|
||||
46
resources/test/fixtures/pydocstyle/D.py
vendored
46
resources/test/fixtures/pydocstyle/D.py
vendored
@@ -588,3 +588,49 @@ def asdfljdjgf24():
|
||||
"""Summary.
|
||||
|
||||
Description. """
|
||||
|
||||
|
||||
@expect('D200: One-line docstring should fit on one line with quotes '
|
||||
'(found 3)')
|
||||
@expect('D212: Multi-line docstring summary should start at the first line')
|
||||
def one_liner():
|
||||
"""
|
||||
|
||||
Wrong."""
|
||||
|
||||
|
||||
@expect('D200: One-line docstring should fit on one line with quotes '
|
||||
'(found 3)')
|
||||
@expect('D212: Multi-line docstring summary should start at the first line')
|
||||
def one_liner():
|
||||
r"""Wrong.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
@expect('D200: One-line docstring should fit on one line with quotes '
|
||||
'(found 3)')
|
||||
@expect('D212: Multi-line docstring summary should start at the first line')
|
||||
def one_liner():
|
||||
"""Wrong."
|
||||
|
||||
"""
|
||||
|
||||
|
||||
@expect('D200: One-line docstring should fit on one line with quotes '
|
||||
'(found 3)')
|
||||
@expect('D212: Multi-line docstring summary should start at the first line')
|
||||
def one_liner():
|
||||
"""
|
||||
|
||||
"Wrong."""
|
||||
|
||||
|
||||
@expect('D404: First word of the docstring should not be "This"')
|
||||
def starts_with_this():
|
||||
"""This is a docstring."""
|
||||
|
||||
|
||||
@expect('D404: First word of the docstring should not be "This"')
|
||||
def starts_with_space_then_this():
|
||||
""" This is a docstring that starts with a space.""" # noqa: D210
|
||||
|
||||
43
resources/test/fixtures/pydocstyle/D401.py
vendored
Normal file
43
resources/test/fixtures/pydocstyle/D401.py
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
# Bad examples
|
||||
|
||||
def bad_liouiwnlkjl():
|
||||
"""Returns foo."""
|
||||
|
||||
|
||||
def bad_sdgfsdg23245():
|
||||
"""Constructor for a foo."""
|
||||
|
||||
|
||||
def bad_sdgfsdg23245777():
|
||||
"""
|
||||
|
||||
Constructor for a boa.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def bad_run_something():
|
||||
"""Runs something"""
|
||||
pass
|
||||
|
||||
|
||||
def multi_line():
|
||||
"""Writes a logical line that
|
||||
extends to two physical lines.
|
||||
"""
|
||||
|
||||
|
||||
# Good examples
|
||||
|
||||
def good_run_something():
|
||||
"""Run away."""
|
||||
|
||||
|
||||
def good_construct():
|
||||
"""Construct a beautiful house."""
|
||||
|
||||
|
||||
def good_multi_line():
|
||||
"""Write a logical line that
|
||||
extends to two physical lines.
|
||||
"""
|
||||
@@ -1,51 +1,51 @@
|
||||
|
||||
if True:
|
||||
import foo; x = 1
|
||||
import foo; x = 1
|
||||
import foo1; x = 1
|
||||
import foo2; x = 1
|
||||
|
||||
if True:
|
||||
import foo; \
|
||||
import foo3; \
|
||||
x = 1
|
||||
|
||||
if True:
|
||||
import foo \
|
||||
import foo4 \
|
||||
; x = 1
|
||||
|
||||
|
||||
if True:
|
||||
x = 1; import foo
|
||||
x = 1; import foo5
|
||||
|
||||
|
||||
if True:
|
||||
x = 1; \
|
||||
import foo
|
||||
import foo6
|
||||
|
||||
|
||||
if True:
|
||||
x = 1 \
|
||||
; import foo
|
||||
; import foo7
|
||||
|
||||
|
||||
if True:
|
||||
x = 1; import foo; x = 1
|
||||
x = 1; import foo; x = 1
|
||||
x = 1; import foo8; x = 1
|
||||
x = 1; import foo9; x = 1
|
||||
|
||||
if True:
|
||||
x = 1; \
|
||||
import foo; \
|
||||
import foo10; \
|
||||
x = 1
|
||||
|
||||
if True:
|
||||
x = 1 \
|
||||
;import foo \
|
||||
;import foo11 \
|
||||
;x = 1
|
||||
|
||||
|
||||
# Continuation, but not as the last content in the file.
|
||||
x = 1; \
|
||||
import foo
|
||||
import foo12
|
||||
|
||||
# Continuation, followed by end-of-file. (Removing `import foo` would cause a syntax
|
||||
# error.)
|
||||
x = 1; \
|
||||
import foo
|
||||
import foo13
|
||||
|
||||
3
resources/test/fixtures/pyproject.toml
vendored
3
resources/test/fixtures/pyproject.toml
vendored
@@ -12,6 +12,9 @@ per-file-ignores = { "__init__.py" = ["F401"] }
|
||||
[tool.ruff.flake8-bugbear]
|
||||
extend-immutable-calls = ["fastapi.Depends", "fastapi.Query"]
|
||||
|
||||
[tool.ruff.flake8-builtins]
|
||||
builtins-ignorelist = ["id", "dir"]
|
||||
|
||||
[tool.ruff.flake8-quotes]
|
||||
inline-quotes = "single"
|
||||
multiline-quotes = "double"
|
||||
|
||||
69
resources/test/fixtures/pyupgrade/UP031_0.py
vendored
Normal file
69
resources/test/fixtures/pyupgrade/UP031_0.py
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
a, b, x, y = 1, 2, 3, 4
|
||||
|
||||
# UP031
|
||||
print('%s %s' % (a, b))
|
||||
|
||||
print('%s%s' % (a, b))
|
||||
|
||||
print("trivial" % ())
|
||||
|
||||
print("%s" % ("simple",))
|
||||
|
||||
print("%s" % ("%s" % ("nested",),))
|
||||
|
||||
print("%s%% percent" % (15,))
|
||||
|
||||
print("%f" % (15,))
|
||||
|
||||
print("%.f" % (15,))
|
||||
|
||||
print("%.3f" % (15,))
|
||||
|
||||
print("%3f" % (15,))
|
||||
|
||||
print("%-5f" % (5,))
|
||||
|
||||
print("%9f" % (5,))
|
||||
|
||||
print("%#o" % (123,))
|
||||
|
||||
print("brace {} %s" % (1,))
|
||||
|
||||
print(
|
||||
"%s" % (
|
||||
"trailing comma",
|
||||
)
|
||||
)
|
||||
|
||||
print("foo %s " % (x,))
|
||||
|
||||
print("%(k)s" % {"k": "v"})
|
||||
|
||||
print("%(k)s" % {
|
||||
"k": "v",
|
||||
"i": "j"
|
||||
})
|
||||
|
||||
print("%(to_list)s" % {"to_list": []})
|
||||
|
||||
print("%(k)s" % {"k": "v", "i": 1, "j": []})
|
||||
|
||||
print("%(ab)s" % {"a" "b": 1})
|
||||
|
||||
print("%(a)s" % {"a" : 1})
|
||||
|
||||
print((
|
||||
"foo %s "
|
||||
"bar %s" % (x, y)
|
||||
))
|
||||
|
||||
print(
|
||||
"foo %(foo)s "
|
||||
"bar %(bar)s" % {"foo": x, "bar": y}
|
||||
)
|
||||
|
||||
print("%s \N{snowman}" % (a,))
|
||||
|
||||
print("%(foo)s \N{snowman}" % {"foo": 1})
|
||||
|
||||
print(("foo %s " "bar %s") % (x, y))
|
||||
59
resources/test/fixtures/pyupgrade/UP031_1.py
vendored
Normal file
59
resources/test/fixtures/pyupgrade/UP031_1.py
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
# OK
|
||||
"%s" % unknown_type
|
||||
|
||||
b"%s" % (b"bytestring",)
|
||||
|
||||
"%*s" % (5, "hi")
|
||||
|
||||
"%d" % (flt,)
|
||||
|
||||
"%c" % (some_string,)
|
||||
|
||||
"%4%" % ()
|
||||
|
||||
"%.2r" % (1.25)
|
||||
|
||||
i % 3
|
||||
|
||||
"%.*s" % (5, "hi")
|
||||
|
||||
"%i" % (flt,)
|
||||
|
||||
"%()s" % {"": "empty"}
|
||||
|
||||
"%s" % {"k": "v"}
|
||||
|
||||
"%(1)s" % {"1": "bar"}
|
||||
|
||||
"%(a)s" % {"a": 1, "a": 2}
|
||||
|
||||
pytest.param('"%8s" % (None,)', id="unsafe width-string conversion"),
|
||||
|
||||
"%()s" % {"": "bar"}
|
||||
|
||||
"%(1)s" % {1: 2, "1": 2}
|
||||
|
||||
"%(and)s" % {"and": 2}
|
||||
|
||||
# OK (arguably false negatives)
|
||||
(
|
||||
"foo %s "
|
||||
"bar %s"
|
||||
) % (x, y)
|
||||
|
||||
(
|
||||
"foo %(foo)s "
|
||||
"bar %(bar)s"
|
||||
) % {"foo": x, "bar": y}
|
||||
|
||||
(
|
||||
"""foo %s"""
|
||||
% (x,)
|
||||
)
|
||||
|
||||
(
|
||||
"""
|
||||
foo %s
|
||||
"""
|
||||
% (x,)
|
||||
)
|
||||
61
resources/test/fixtures/pyupgrade/UP034.py
vendored
Normal file
61
resources/test/fixtures/pyupgrade/UP034.py
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
# UP034
|
||||
print(("foo"))
|
||||
|
||||
# UP034
|
||||
print(("hell((goodybe))o"))
|
||||
|
||||
# UP034
|
||||
print((("foo")))
|
||||
|
||||
# UP034
|
||||
print((((1))))
|
||||
|
||||
# UP034
|
||||
print(("foo{}".format(1)))
|
||||
|
||||
# UP034
|
||||
print(
|
||||
("foo{}".format(1))
|
||||
)
|
||||
|
||||
# UP034
|
||||
print(
|
||||
(
|
||||
"foo"
|
||||
)
|
||||
)
|
||||
|
||||
# UP034
|
||||
def f():
|
||||
x = int(((yield 1)))
|
||||
|
||||
# UP034
|
||||
if True:
|
||||
print(
|
||||
("foo{}".format(1))
|
||||
)
|
||||
|
||||
# UP034
|
||||
print((x for x in range(3)))
|
||||
|
||||
# OK
|
||||
print("foo")
|
||||
|
||||
# OK
|
||||
print((1, 2, 3))
|
||||
|
||||
# OK
|
||||
print(())
|
||||
|
||||
# OK
|
||||
print((1,))
|
||||
|
||||
# OK
|
||||
sum((block.code for block in blocks), [])
|
||||
|
||||
# OK
|
||||
def f():
|
||||
x = int((yield 1))
|
||||
|
||||
# OK
|
||||
sum((i for i in range(3)), [])
|
||||
39
resources/test/fixtures/ruff/RUF005.py
vendored
Normal file
39
resources/test/fixtures/ruff/RUF005.py
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
class Fun:
|
||||
words = ("how", "fun!")
|
||||
|
||||
def yay(self):
|
||||
return self.words
|
||||
|
||||
yay = Fun().yay
|
||||
|
||||
foo = [4, 5, 6]
|
||||
bar = [1, 2, 3] + foo
|
||||
zoob = tuple(bar)
|
||||
quux = (7, 8, 9) + zoob
|
||||
spam = quux + (10, 11, 12)
|
||||
spom = list(spam)
|
||||
eggs = spom + [13, 14, 15]
|
||||
elatement = ("we all say", ) + yay()
|
||||
excitement = ("we all think", ) + Fun().yay()
|
||||
astonishment = ("we all feel", ) + Fun.words
|
||||
|
||||
chain = ['a', 'b', 'c'] + eggs + list(('yes', 'no', 'pants') + zoob)
|
||||
|
||||
baz = () + zoob
|
||||
|
||||
first = [
|
||||
# The order
|
||||
1, # here
|
||||
2, # is
|
||||
# extremely
|
||||
3, # critical
|
||||
# to preserve
|
||||
]
|
||||
second = first + [
|
||||
# please
|
||||
4,
|
||||
# don't
|
||||
5,
|
||||
# touch
|
||||
6,
|
||||
]
|
||||
129
resources/test/fixtures/ruff/RUF100_1.py
vendored
129
resources/test/fixtures/ruff/RUF100_1.py
vendored
@@ -1,64 +1,89 @@
|
||||
# This should ignore both errors.
|
||||
from typing import ( # noqa: F401
|
||||
List,
|
||||
Sequence,
|
||||
)
|
||||
def f():
|
||||
# This should ignore both errors.
|
||||
from typing import ( # noqa: F401
|
||||
List,
|
||||
Sequence,
|
||||
)
|
||||
|
||||
# This should ignore both errors.
|
||||
from typing import ( # noqa
|
||||
List,
|
||||
Sequence,
|
||||
)
|
||||
|
||||
# This should ignore both errors.
|
||||
from typing import (
|
||||
List, # noqa: F401
|
||||
Sequence, # noqa: F401
|
||||
)
|
||||
def f():
|
||||
# This should ignore both errors.
|
||||
from typing import ( # noqa
|
||||
List,
|
||||
Sequence,
|
||||
)
|
||||
|
||||
# This should ignore both errors.
|
||||
from typing import (
|
||||
List, # noqa
|
||||
Sequence, # noqa
|
||||
)
|
||||
|
||||
# This should ignore the first error.
|
||||
from typing import (
|
||||
Mapping, # noqa: F401
|
||||
Union,
|
||||
)
|
||||
def f():
|
||||
# This should ignore both errors.
|
||||
from typing import (
|
||||
List, # noqa: F401
|
||||
Sequence, # noqa: F401
|
||||
)
|
||||
|
||||
# This should ignore both errors.
|
||||
from typing import ( # noqa
|
||||
List,
|
||||
Sequence,
|
||||
)
|
||||
|
||||
# This should ignore the error, but the inner noqa should be marked as unused.
|
||||
from typing import ( # noqa: F401
|
||||
Optional, # noqa: F401
|
||||
)
|
||||
def f():
|
||||
# This should ignore both errors.
|
||||
from typing import (
|
||||
List, # noqa
|
||||
Sequence, # noqa
|
||||
)
|
||||
|
||||
# This should ignore the error, but the inner noqa should be marked as unused.
|
||||
from typing import ( # noqa
|
||||
Optional, # noqa: F401
|
||||
)
|
||||
|
||||
# This should ignore the error, but mark F501 as unused.
|
||||
from typing import ( # noqa: F401
|
||||
Dict, # noqa: F501
|
||||
)
|
||||
def f():
|
||||
# This should ignore the first error.
|
||||
from typing import (
|
||||
Mapping, # noqa: F401
|
||||
Union,
|
||||
)
|
||||
|
||||
# This should ignore the error, but mark F501 as unused.
|
||||
from typing import ( # noqa: F501
|
||||
Tuple, # noqa: F401
|
||||
)
|
||||
|
||||
# This should ignore both errors.
|
||||
from typing import Any, AnyStr # noqa: F401
|
||||
def f():
|
||||
# This should ignore both errors.
|
||||
from typing import ( # noqa
|
||||
List,
|
||||
Sequence,
|
||||
)
|
||||
|
||||
# This should ignore both errors.
|
||||
from typing import AsyncIterable, AsyncGenerator # noqa
|
||||
|
||||
# This should mark F501 as unused.
|
||||
from typing import Awaitable, AwaitableGenerator # noqa: F501
|
||||
def f():
|
||||
# This should ignore the error, but the inner noqa should be marked as unused.
|
||||
from typing import ( # noqa: F401
|
||||
Optional, # noqa: F401
|
||||
)
|
||||
|
||||
|
||||
def f():
|
||||
# This should ignore the error, but the inner noqa should be marked as unused.
|
||||
from typing import ( # noqa
|
||||
Optional, # noqa: F401
|
||||
)
|
||||
|
||||
|
||||
def f():
|
||||
# This should ignore the error, but mark F501 as unused.
|
||||
from typing import ( # noqa: F401
|
||||
Dict, # noqa: F501
|
||||
)
|
||||
|
||||
|
||||
def f():
|
||||
# This should ignore the error, but mark F501 as unused.
|
||||
from typing import ( # noqa: F501
|
||||
Tuple, # noqa: F401
|
||||
)
|
||||
|
||||
|
||||
def f():
|
||||
# This should ignore both errors.
|
||||
from typing import Any, AnyStr # noqa: F401
|
||||
|
||||
|
||||
def f():
|
||||
# This should ignore both errors.
|
||||
from typing import AsyncIterable, AsyncGenerator # noqa
|
||||
|
||||
|
||||
def f():
|
||||
# This should mark F501 as unused.
|
||||
from typing import Awaitable, AwaitableGenerator # noqa: F501
|
||||
|
||||
47
resources/test/fixtures/tryceratops/TRY300.py
vendored
Normal file
47
resources/test/fixtures/tryceratops/TRY300.py
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
"""
|
||||
Violation:
|
||||
|
||||
Returning a final value inside a try block may indicate you could use an else block
|
||||
instead to outline the success scenario
|
||||
"""
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MyException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def bad():
|
||||
try:
|
||||
a = 1
|
||||
b = process()
|
||||
return b
|
||||
except MyException:
|
||||
logger.exception("process failed")
|
||||
|
||||
|
||||
def good():
|
||||
try:
|
||||
a = 1
|
||||
b = process()
|
||||
except MyException:
|
||||
logger.exception("process failed")
|
||||
else:
|
||||
return b
|
||||
|
||||
|
||||
def noreturn():
|
||||
try:
|
||||
a = 1
|
||||
b = process()
|
||||
except MyException:
|
||||
logger.exception("process failed")
|
||||
|
||||
|
||||
def still_good():
|
||||
try:
|
||||
return process()
|
||||
except MyException:
|
||||
logger.exception("process failed")
|
||||
@@ -73,7 +73,7 @@
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/definitions/RuleCodePrefix"
|
||||
"$ref": "#/definitions/RuleSelector"
|
||||
}
|
||||
},
|
||||
"extend-select": {
|
||||
@@ -83,7 +83,7 @@
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/definitions/RuleCodePrefix"
|
||||
"$ref": "#/definitions/RuleSelector"
|
||||
}
|
||||
},
|
||||
"external": {
|
||||
@@ -117,7 +117,7 @@
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/definitions/RuleCodePrefix"
|
||||
"$ref": "#/definitions/RuleSelector"
|
||||
}
|
||||
},
|
||||
"flake8-annotations": {
|
||||
@@ -153,6 +153,17 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"flake8-builtins": {
|
||||
"description": "Options for the `flake8-builtins` plugin.",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Flake8BuiltinsOptions"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"flake8-errmsg": {
|
||||
"description": "Options for the `flake8-errmsg` plugin.",
|
||||
"anyOf": [
|
||||
@@ -227,7 +238,7 @@
|
||||
]
|
||||
},
|
||||
"format": {
|
||||
"description": "The style in which violation messages should be formatted: `\"text\"` (default), `\"grouped\"` (group messages by file), `\"json\"` (machine-readable), `\"junit\"` (machine-readable XML), `\"github\"` (GitHub Actions annotations) or `\"gitlab\"` (GitLab CI code quality report).",
|
||||
"description": "The style in which violation messages should be formatted: `\"text\"` (default), `\"grouped\"` (group messages by file), `\"json\"` (machine-readable), `\"junit\"` (machine-readable XML), `\"github\"` (GitHub Actions annotations), `\"gitlab\"` (GitLab CI code quality report), or `\"pylint\"` (Pylint text format).",
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/SerializationFormat"
|
||||
@@ -244,7 +255,7 @@
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/definitions/RuleCodePrefix"
|
||||
"$ref": "#/definitions/RuleSelector"
|
||||
}
|
||||
},
|
||||
"ignore-init-module-imports": {
|
||||
@@ -315,7 +326,7 @@
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/RuleCodePrefix"
|
||||
"$ref": "#/definitions/RuleSelector"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -388,7 +399,7 @@
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/definitions/RuleCodePrefix"
|
||||
"$ref": "#/definitions/RuleSelector"
|
||||
}
|
||||
},
|
||||
"show-source": {
|
||||
@@ -446,7 +457,7 @@
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/definitions/RuleCodePrefix"
|
||||
"$ref": "#/definitions/RuleSelector"
|
||||
}
|
||||
},
|
||||
"update-check": {
|
||||
@@ -584,6 +595,22 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Flake8BuiltinsOptions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"builtins-ignorelist": {
|
||||
"description": "Ignore list of builtins.",
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Flake8ErrMsgOptions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -1134,7 +1161,7 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"RuleCodePrefix": {
|
||||
"RuleSelector": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"A",
|
||||
@@ -1277,6 +1304,7 @@
|
||||
"D4",
|
||||
"D40",
|
||||
"D400",
|
||||
"D401",
|
||||
"D402",
|
||||
"D403",
|
||||
"D404",
|
||||
@@ -1310,6 +1338,9 @@
|
||||
"DTZ011",
|
||||
"DTZ012",
|
||||
"E",
|
||||
"E1",
|
||||
"E10",
|
||||
"E101",
|
||||
"E4",
|
||||
"E40",
|
||||
"E401",
|
||||
@@ -1347,6 +1378,12 @@
|
||||
"ERA0",
|
||||
"ERA00",
|
||||
"ERA001",
|
||||
"EXE",
|
||||
"EXE0",
|
||||
"EXE00",
|
||||
"EXE003",
|
||||
"EXE004",
|
||||
"EXE005",
|
||||
"F",
|
||||
"F4",
|
||||
"F40",
|
||||
@@ -1631,6 +1668,7 @@
|
||||
"RUF002",
|
||||
"RUF003",
|
||||
"RUF004",
|
||||
"RUF005",
|
||||
"RUF1",
|
||||
"RUF10",
|
||||
"RUF100",
|
||||
@@ -1709,6 +1747,14 @@
|
||||
"TID25",
|
||||
"TID251",
|
||||
"TID252",
|
||||
"TRY",
|
||||
"TRY3",
|
||||
"TRY30",
|
||||
"TRY300",
|
||||
"TYP",
|
||||
"TYP0",
|
||||
"TYP00",
|
||||
"TYP005",
|
||||
"U",
|
||||
"U0",
|
||||
"U00",
|
||||
@@ -1765,8 +1811,10 @@
|
||||
"UP029",
|
||||
"UP03",
|
||||
"UP030",
|
||||
"UP031",
|
||||
"UP032",
|
||||
"UP033",
|
||||
"UP034",
|
||||
"W",
|
||||
"W2",
|
||||
"W29",
|
||||
@@ -1804,7 +1852,8 @@
|
||||
"junit",
|
||||
"grouped",
|
||||
"github",
|
||||
"gitlab"
|
||||
"gitlab",
|
||||
"pylint"
|
||||
]
|
||||
},
|
||||
"Strictness": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_cli"
|
||||
version = "0.0.226"
|
||||
version = "0.0.229"
|
||||
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.65.0"
|
||||
|
||||
@@ -4,7 +4,7 @@ use clap::{command, Parser};
|
||||
use regex::Regex;
|
||||
use ruff::fs;
|
||||
use ruff::logging::LogLevel;
|
||||
use ruff::registry::{RuleCode, RuleCodePrefix};
|
||||
use ruff::registry::{Rule, RuleSelector};
|
||||
use ruff::resolver::ConfigProcessor;
|
||||
use ruff::settings::types::{
|
||||
FilePattern, PatternPrefixPair, PerFileIgnore, PythonVersion, SerializationFormat,
|
||||
@@ -66,18 +66,18 @@ pub struct Cli {
|
||||
/// Comma-separated list of rule codes to enable (or ALL, to enable all
|
||||
/// rules).
|
||||
#[arg(long, value_delimiter = ',', value_name = "RULE_CODE")]
|
||||
pub select: Option<Vec<RuleCodePrefix>>,
|
||||
pub select: Option<Vec<RuleSelector>>,
|
||||
/// Like --select, but adds additional rule codes on top of the selected
|
||||
/// ones.
|
||||
#[arg(long, value_delimiter = ',', value_name = "RULE_CODE")]
|
||||
pub extend_select: Option<Vec<RuleCodePrefix>>,
|
||||
pub extend_select: Option<Vec<RuleSelector>>,
|
||||
/// Comma-separated list of rule codes to disable.
|
||||
#[arg(long, value_delimiter = ',', value_name = "RULE_CODE")]
|
||||
pub ignore: Option<Vec<RuleCodePrefix>>,
|
||||
pub ignore: Option<Vec<RuleSelector>>,
|
||||
/// Like --ignore, but adds additional rule codes on top of the ignored
|
||||
/// ones.
|
||||
#[arg(long, value_delimiter = ',', value_name = "RULE_CODE")]
|
||||
pub extend_ignore: Option<Vec<RuleCodePrefix>>,
|
||||
pub extend_ignore: Option<Vec<RuleSelector>>,
|
||||
/// List of paths, used to omit files and/or directories from analysis.
|
||||
#[arg(long, value_delimiter = ',', value_name = "FILE_PATTERN")]
|
||||
pub exclude: Option<Vec<FilePattern>>,
|
||||
@@ -88,11 +88,11 @@ pub struct Cli {
|
||||
/// List of rule codes to treat as eligible for autofix. Only applicable
|
||||
/// when autofix itself is enabled (e.g., via `--fix`).
|
||||
#[arg(long, value_delimiter = ',', value_name = "RULE_CODE")]
|
||||
pub fixable: Option<Vec<RuleCodePrefix>>,
|
||||
pub fixable: Option<Vec<RuleSelector>>,
|
||||
/// List of rule codes to treat as ineligible for autofix. Only applicable
|
||||
/// when autofix itself is enabled (e.g., via `--fix`).
|
||||
#[arg(long, value_delimiter = ',', value_name = "RULE_CODE")]
|
||||
pub unfixable: Option<Vec<RuleCodePrefix>>,
|
||||
pub unfixable: Option<Vec<RuleSelector>>,
|
||||
/// List of mappings from file pattern to code to exclude
|
||||
#[arg(long, value_delimiter = ',')]
|
||||
pub per_file_ignores: Option<Vec<PatternPrefixPair>>,
|
||||
@@ -169,6 +169,7 @@ pub struct Cli {
|
||||
/// Explain a rule.
|
||||
#[arg(
|
||||
long,
|
||||
value_parser=Rule::from_code,
|
||||
// Fake subcommands.
|
||||
conflicts_with = "add_noqa",
|
||||
conflicts_with = "clean",
|
||||
@@ -180,7 +181,7 @@ pub struct Cli {
|
||||
conflicts_with = "stdin_filename",
|
||||
conflicts_with = "watch",
|
||||
)]
|
||||
pub explain: Option<RuleCode>,
|
||||
pub explain: Option<&'static Rule>,
|
||||
/// Generate shell completion
|
||||
#[arg(
|
||||
long,
|
||||
@@ -302,7 +303,7 @@ pub struct Arguments {
|
||||
pub config: Option<PathBuf>,
|
||||
pub diff: bool,
|
||||
pub exit_zero: bool,
|
||||
pub explain: Option<RuleCode>,
|
||||
pub explain: Option<&'static Rule>,
|
||||
pub files: Vec<PathBuf>,
|
||||
pub generate_shell_completion: Option<clap_complete_command::Shell>,
|
||||
pub isolated: bool,
|
||||
@@ -323,17 +324,17 @@ pub struct Overrides {
|
||||
pub dummy_variable_rgx: Option<Regex>,
|
||||
pub exclude: Option<Vec<FilePattern>>,
|
||||
pub extend_exclude: Option<Vec<FilePattern>>,
|
||||
pub extend_ignore: Option<Vec<RuleCodePrefix>>,
|
||||
pub extend_select: Option<Vec<RuleCodePrefix>>,
|
||||
pub fixable: Option<Vec<RuleCodePrefix>>,
|
||||
pub ignore: Option<Vec<RuleCodePrefix>>,
|
||||
pub extend_ignore: Option<Vec<RuleSelector>>,
|
||||
pub extend_select: Option<Vec<RuleSelector>>,
|
||||
pub fixable: Option<Vec<RuleSelector>>,
|
||||
pub ignore: Option<Vec<RuleSelector>>,
|
||||
pub line_length: Option<usize>,
|
||||
pub per_file_ignores: Option<Vec<PatternPrefixPair>>,
|
||||
pub respect_gitignore: Option<bool>,
|
||||
pub select: Option<Vec<RuleCodePrefix>>,
|
||||
pub select: Option<Vec<RuleSelector>>,
|
||||
pub show_source: Option<bool>,
|
||||
pub target_version: Option<PythonVersion>,
|
||||
pub unfixable: Option<Vec<RuleCodePrefix>>,
|
||||
pub unfixable: Option<Vec<RuleSelector>>,
|
||||
// TODO(charlie): Captured in pyproject.toml as a default, but not part of `Settings`.
|
||||
pub cache_dir: Option<PathBuf>,
|
||||
pub fix: Option<bool>,
|
||||
@@ -434,7 +435,7 @@ pub fn extract_log_level(cli: &Arguments) -> LogLevel {
|
||||
|
||||
/// Convert a list of `PatternPrefixPair` structs to `PerFileIgnore`.
|
||||
pub fn collect_per_file_ignores(pairs: Vec<PatternPrefixPair>) -> Vec<PerFileIgnore> {
|
||||
let mut per_file_ignores: FxHashMap<String, Vec<RuleCodePrefix>> = FxHashMap::default();
|
||||
let mut per_file_ignores: FxHashMap<String, Vec<RuleSelector>> = FxHashMap::default();
|
||||
for pair in pairs {
|
||||
per_file_ignores
|
||||
.entry(pair.pattern)
|
||||
|
||||
@@ -15,11 +15,11 @@ use ruff::cache::CACHE_DIR_NAME;
|
||||
use ruff::linter::add_noqa_to_path;
|
||||
use ruff::logging::LogLevel;
|
||||
use ruff::message::{Location, Message};
|
||||
use ruff::registry::RuleCode;
|
||||
use ruff::registry::{Linter, ParseCode, Rule};
|
||||
use ruff::resolver::{FileDiscovery, PyprojectDiscovery};
|
||||
use ruff::settings::flags;
|
||||
use ruff::settings::types::SerializationFormat;
|
||||
use ruff::{fix, fs, packaging, resolver, warn_user_once, IOError};
|
||||
use ruff::{fix, fs, packaging, resolver, warn_user_once, AutofixAvailability, IOError};
|
||||
use serde::Serialize;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
@@ -114,7 +114,7 @@ pub fn run(
|
||||
.unwrap_or_else(|(path, message)| {
|
||||
if let Some(path) = &path {
|
||||
let settings = resolver.resolve(path, pyproject_strategy);
|
||||
if settings.rules.enabled(&RuleCode::E902) {
|
||||
if settings.rules.enabled(&Rule::IOError) {
|
||||
Diagnostics::new(vec![Message {
|
||||
kind: IOError(message).into(),
|
||||
location: Location::default(),
|
||||
@@ -285,28 +285,39 @@ pub fn show_files(
|
||||
#[derive(Serialize)]
|
||||
struct Explanation<'a> {
|
||||
code: &'a str,
|
||||
origin: &'a str,
|
||||
linter: &'a str,
|
||||
summary: &'a str,
|
||||
}
|
||||
|
||||
/// Explain a `RuleCode` to the user.
|
||||
pub fn explain(code: &RuleCode, format: SerializationFormat) -> Result<()> {
|
||||
/// Explain a `Rule` to the user.
|
||||
pub fn explain(rule: &Rule, format: SerializationFormat) -> Result<()> {
|
||||
let (linter, _) = Linter::parse_code(rule.code()).unwrap();
|
||||
match format {
|
||||
SerializationFormat::Text | SerializationFormat::Grouped => {
|
||||
println!(
|
||||
"{} ({}): {}",
|
||||
code.as_ref(),
|
||||
code.origin().name(),
|
||||
code.kind().summary()
|
||||
);
|
||||
println!("{}\n", rule.as_ref());
|
||||
println!("Code: {} ({})\n", rule.code(), linter.name());
|
||||
|
||||
if let Some(autofix) = rule.autofixable() {
|
||||
println!(
|
||||
"{}",
|
||||
match autofix.available {
|
||||
AutofixAvailability::Sometimes => "Autofix is sometimes available.\n",
|
||||
AutofixAvailability::Always => "Autofix is always available.\n",
|
||||
}
|
||||
);
|
||||
}
|
||||
println!("Message formats:\n");
|
||||
for format in rule.message_formats() {
|
||||
println!("* {format}");
|
||||
}
|
||||
}
|
||||
SerializationFormat::Json => {
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&Explanation {
|
||||
code: code.as_ref(),
|
||||
origin: code.origin().name(),
|
||||
summary: &code.kind().summary(),
|
||||
code: rule.code(),
|
||||
linter: linter.name(),
|
||||
summary: rule.message_formats()[0],
|
||||
})?
|
||||
);
|
||||
}
|
||||
@@ -319,6 +330,9 @@ pub fn explain(code: &RuleCode, format: SerializationFormat) -> Result<()> {
|
||||
SerializationFormat::Gitlab => {
|
||||
bail!("`--explain` does not support GitLab format")
|
||||
}
|
||||
SerializationFormat::Pylint => {
|
||||
bail!("`--explain` does not support pylint format")
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -157,8 +157,8 @@ quoting the executed command, along with the relevant file contents and `pyproje
|
||||
PyprojectDiscovery::Hierarchical(settings) => settings.cli.clone(),
|
||||
};
|
||||
|
||||
if let Some(code) = cli.explain {
|
||||
commands::explain(&code, format)?;
|
||||
if let Some(rule) = cli.explain {
|
||||
commands::explain(rule, format)?;
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
if cli.show_settings {
|
||||
|
||||
@@ -11,7 +11,7 @@ use itertools::iterate;
|
||||
use ruff::fs::relativize_path;
|
||||
use ruff::logging::LogLevel;
|
||||
use ruff::message::{Location, Message};
|
||||
use ruff::registry::RuleCode;
|
||||
use ruff::registry::Rule;
|
||||
use ruff::settings::types::SerializationFormat;
|
||||
use ruff::{fix, notify_user};
|
||||
use serde::Serialize;
|
||||
@@ -35,7 +35,7 @@ struct ExpandedFix<'a> {
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ExpandedMessage<'a> {
|
||||
code: &'a RuleCode,
|
||||
code: SerializeRuleAsCode<'a>,
|
||||
message: String,
|
||||
fix: Option<ExpandedFix<'a>>,
|
||||
location: Location,
|
||||
@@ -43,6 +43,23 @@ struct ExpandedMessage<'a> {
|
||||
filename: &'a str,
|
||||
}
|
||||
|
||||
struct SerializeRuleAsCode<'a>(&'a Rule);
|
||||
|
||||
impl Serialize for SerializeRuleAsCode<'_> {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_str(self.0.code())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Rule> for SerializeRuleAsCode<'a> {
|
||||
fn from(rule: &'a Rule) -> Self {
|
||||
Self(rule)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Printer<'a> {
|
||||
format: &'a SerializationFormat,
|
||||
log_level: &'a LogLevel,
|
||||
@@ -143,7 +160,7 @@ impl<'a> Printer<'a> {
|
||||
.messages
|
||||
.iter()
|
||||
.map(|message| ExpandedMessage {
|
||||
code: message.kind.code(),
|
||||
code: message.kind.rule().into(),
|
||||
message: message.kind.body(),
|
||||
fix: message.fix.as_ref().map(|fix| ExpandedFix {
|
||||
content: &fix.content,
|
||||
@@ -177,8 +194,10 @@ impl<'a> Printer<'a> {
|
||||
message.location.column(),
|
||||
message.kind.body()
|
||||
));
|
||||
let mut case =
|
||||
TestCase::new(format!("org.ruff.{}", message.kind.code()), status);
|
||||
let mut case = TestCase::new(
|
||||
format!("org.ruff.{}", message.kind.rule().code()),
|
||||
status,
|
||||
);
|
||||
let file_path = Path::new(filename);
|
||||
let file_stem = file_path.file_stem().unwrap().to_str().unwrap();
|
||||
let classname = file_path.parent().unwrap().join(file_stem);
|
||||
@@ -248,14 +267,14 @@ impl<'a> Printer<'a> {
|
||||
":",
|
||||
message.location.column(),
|
||||
":",
|
||||
message.kind.code().as_ref(),
|
||||
message.kind.rule().code(),
|
||||
message.kind.body(),
|
||||
);
|
||||
writeln!(
|
||||
stdout,
|
||||
"::error title=Ruff \
|
||||
({}),file={},line={},col={},endLine={},endColumn={}::{}",
|
||||
message.kind.code(),
|
||||
message.kind.rule().code(),
|
||||
message.filename,
|
||||
message.location.row(),
|
||||
message.location.column(),
|
||||
@@ -266,7 +285,7 @@ impl<'a> Printer<'a> {
|
||||
}
|
||||
}
|
||||
SerializationFormat::Gitlab => {
|
||||
// Generate JSON with errors in GitLab CI format
|
||||
// Generate JSON with violations in GitLab CI format
|
||||
// https://docs.gitlab.com/ee/ci/testing/code_quality.html#implementing-a-custom-tool
|
||||
writeln!(stdout,
|
||||
"{}",
|
||||
@@ -276,9 +295,9 @@ impl<'a> Printer<'a> {
|
||||
.iter()
|
||||
.map(|message| {
|
||||
json!({
|
||||
"description": format!("({}) {}", message.kind.code(), message.kind.body()),
|
||||
"description": format!("({}) {}", message.kind.rule().code(), message.kind.body()),
|
||||
"severity": "major",
|
||||
"fingerprint": message.kind.code(),
|
||||
"fingerprint": message.kind.rule().code(),
|
||||
"location": {
|
||||
"path": message.filename,
|
||||
"lines": {
|
||||
@@ -293,6 +312,20 @@ impl<'a> Printer<'a> {
|
||||
)?
|
||||
)?;
|
||||
}
|
||||
SerializationFormat::Pylint => {
|
||||
// Generate violations in Pylint format.
|
||||
// See: https://flake8.pycqa.org/en/latest/internal/formatters.html#pylint-formatter
|
||||
for message in &diagnostics.messages {
|
||||
let label = format!(
|
||||
"{}:{}: [{}] {}",
|
||||
relativize_path(Path::new(&message.filename)),
|
||||
message.location.row(),
|
||||
message.kind.rule().code(),
|
||||
message.kind.body(),
|
||||
);
|
||||
writeln!(stdout, "{label}")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stdout.flush()?;
|
||||
@@ -361,7 +394,7 @@ fn print_message<T: Write>(stdout: &mut T, message: &Message) -> Result<()> {
|
||||
":".cyan(),
|
||||
message.location.column(),
|
||||
":".cyan(),
|
||||
message.kind.code().as_ref().red().bold(),
|
||||
message.kind.rule().code().red().bold(),
|
||||
message.kind.body(),
|
||||
);
|
||||
writeln!(stdout, "{label}")?;
|
||||
@@ -388,7 +421,7 @@ fn print_message<T: Write>(stdout: &mut T, message: &Message) -> Result<()> {
|
||||
source: &source.contents,
|
||||
line_start: message.location.row(),
|
||||
annotations: vec![SourceAnnotation {
|
||||
label: message.kind.code().as_ref(),
|
||||
label: message.kind.rule().code(),
|
||||
annotation_type: AnnotationType::Error,
|
||||
range: source.range,
|
||||
}],
|
||||
@@ -425,7 +458,7 @@ fn print_grouped_message<T: Write>(
|
||||
":".cyan(),
|
||||
message.location.column(),
|
||||
" ".repeat(column_length - num_digits(message.location.column())),
|
||||
message.kind.code().as_ref().red().bold(),
|
||||
message.kind.rule().code().red().bold(),
|
||||
message.kind.body(),
|
||||
);
|
||||
writeln!(stdout, "{label}")?;
|
||||
@@ -452,7 +485,7 @@ fn print_grouped_message<T: Write>(
|
||||
source: &source.contents,
|
||||
line_start: message.location.row(),
|
||||
annotations: vec![SourceAnnotation {
|
||||
label: message.kind.code().as_ref(),
|
||||
label: message.kind.rule().code(),
|
||||
annotation_type: AnnotationType::Error,
|
||||
range: source.range,
|
||||
}],
|
||||
|
||||
@@ -13,7 +13,7 @@ use assert_cmd::Command;
|
||||
use itertools::Itertools;
|
||||
use log::info;
|
||||
use ruff::logging::{set_up_logging, LogLevel};
|
||||
use ruff::registry::RuleOrigin;
|
||||
use ruff::registry::Linter;
|
||||
use strum::IntoEnumIterator;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
@@ -175,12 +175,12 @@ fn test_ruff_black_compatibility() -> Result<()> {
|
||||
.filter_map(Result::ok)
|
||||
.collect();
|
||||
|
||||
let codes = RuleOrigin::iter()
|
||||
let codes = Linter::iter()
|
||||
// Exclude ruff codes, specifically RUF100, because it causes differences that are not a
|
||||
// problem. Ruff would add a `# noqa: W292` after the first run, black introduces a
|
||||
// newline, and ruff removes the `# noqa: W292` again.
|
||||
.filter(|origin| *origin != RuleOrigin::Ruff)
|
||||
.map(|origin| origin.prefixes().as_list(","))
|
||||
.filter(|linter| *linter != Linter::Ruff)
|
||||
.map(|linter| linter.prefixes().as_list(","))
|
||||
.join(",");
|
||||
let ruff_args = [
|
||||
"-",
|
||||
|
||||
@@ -151,3 +151,12 @@ fn test_show_source() -> Result<()> {
|
||||
assert!(str::from_utf8(&output.get_output().stdout)?.contains("l = 1"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explain_status_codes() -> Result<()> {
|
||||
let mut cmd = Command::cargo_bin(BIN_NAME)?;
|
||||
cmd.args(["-", "--explain", "F401"]).assert().success();
|
||||
let mut cmd = Command::cargo_bin(BIN_NAME)?;
|
||||
cmd.args(["-", "--explain", "RUF404"]).assert().failure();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.226"
|
||||
version = "0.0.229"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
@@ -11,9 +11,9 @@ libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a87
|
||||
once_cell = { version = "1.16.0" }
|
||||
ruff = { path = ".." }
|
||||
ruff_cli = { path = "../ruff_cli" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "acbc517b55406c76da83d7b2711941d8d3f65b87" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "acbc517b55406c76da83d7b2711941d8d3f65b87" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "acbc517b55406c76da83d7b2711941d8d3f65b87" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "ff90fe52eea578c8ebdd9d95e078cc041a5959fa" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "ff90fe52eea578c8ebdd9d95e078cc041a5959fa" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "ff90fe52eea578c8ebdd9d95e078cc041a5959fa" }
|
||||
schemars = { version = "0.8.11" }
|
||||
serde_json = {version="1.0.91"}
|
||||
strum = { version = "0.24.1", features = ["strum_macros"] }
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Args;
|
||||
use ruff::registry::{Prefixes, RuleCodePrefix, RuleOrigin};
|
||||
use ruff::registry::{Linter, Prefixes, RuleSelector};
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::utils::replace_readme_section;
|
||||
@@ -20,19 +20,22 @@ pub struct Cli {
|
||||
pub(crate) dry_run: bool,
|
||||
}
|
||||
|
||||
fn generate_table(table_out: &mut String, prefix: &RuleCodePrefix) {
|
||||
fn generate_table(table_out: &mut String, prefix: &RuleSelector) {
|
||||
table_out.push_str("| Code | Name | Message | Fix |");
|
||||
table_out.push('\n');
|
||||
table_out.push_str("| ---- | ---- | ------- | --- |");
|
||||
table_out.push('\n');
|
||||
for rule_code in prefix.codes() {
|
||||
let kind = rule_code.kind();
|
||||
let fix_token = if kind.fixable() { "🛠" } else { "" };
|
||||
for rule in prefix.codes() {
|
||||
let fix_token = match rule.autofixable() {
|
||||
None => "",
|
||||
Some(_) => "🛠",
|
||||
};
|
||||
|
||||
table_out.push_str(&format!(
|
||||
"| {} | {} | {} | {} |",
|
||||
kind.code().as_ref(),
|
||||
kind.as_ref(),
|
||||
kind.summary().replace('|', r"\|"),
|
||||
rule.code(),
|
||||
rule.as_ref(),
|
||||
rule.message_formats()[0].replace('|', r"\|"),
|
||||
fix_token
|
||||
));
|
||||
table_out.push('\n');
|
||||
@@ -44,22 +47,22 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
// Generate the table string.
|
||||
let mut table_out = String::new();
|
||||
let mut toc_out = String::new();
|
||||
for origin in RuleOrigin::iter() {
|
||||
let prefixes = origin.prefixes();
|
||||
for linter in Linter::iter() {
|
||||
let prefixes = linter.prefixes();
|
||||
let codes_csv: String = prefixes.as_list(", ");
|
||||
table_out.push_str(&format!("### {} ({codes_csv})", origin.name()));
|
||||
table_out.push_str(&format!("### {} ({codes_csv})", linter.name()));
|
||||
table_out.push('\n');
|
||||
table_out.push('\n');
|
||||
|
||||
toc_out.push_str(&format!(
|
||||
" 1. [{} ({})](#{}-{})\n",
|
||||
origin.name(),
|
||||
linter.name(),
|
||||
codes_csv,
|
||||
origin.name().to_lowercase().replace(' ', "-"),
|
||||
linter.name().to_lowercase().replace(' ', "-"),
|
||||
codes_csv.to_lowercase().replace(',', "-").replace(' ', "")
|
||||
));
|
||||
|
||||
if let Some(url) = origin.url() {
|
||||
if let Some(url) = linter.url() {
|
||||
let host = url
|
||||
.trim_start_matches("https://")
|
||||
.split('/')
|
||||
@@ -67,7 +70,7 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
.unwrap();
|
||||
table_out.push_str(&format!(
|
||||
"For more, see [{}]({}) on {}.",
|
||||
origin.name(),
|
||||
linter.name(),
|
||||
url,
|
||||
match host {
|
||||
"pypi.org" => "PyPI",
|
||||
@@ -75,7 +78,7 @@ pub fn main(cli: &Cli) -> Result<()> {
|
||||
host => panic!(
|
||||
"unexpected host in URL of {}, expected pypi.org or github.com but found \
|
||||
{host}",
|
||||
origin.name()
|
||||
linter.name()
|
||||
),
|
||||
}
|
||||
));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.226"
|
||||
version = "0.0.229"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -1,35 +1,41 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use proc_macro2::Span;
|
||||
use quote::quote;
|
||||
use syn::parse::Parse;
|
||||
use syn::{Ident, Path, Token};
|
||||
use syn::{Ident, LitStr, Path, Token};
|
||||
|
||||
pub fn define_rule_mapping(mapping: Mapping) -> proc_macro2::TokenStream {
|
||||
let mut rulecode_variants = quote!();
|
||||
pub fn define_rule_mapping(mapping: &Mapping) -> proc_macro2::TokenStream {
|
||||
let mut rule_variants = quote!();
|
||||
let mut diagkind_variants = quote!();
|
||||
let mut rulecode_kind_match_arms = quote!();
|
||||
let mut rulecode_origin_match_arms = quote!();
|
||||
let mut rule_message_formats_match_arms = quote!();
|
||||
let mut rule_autofixable_match_arms = quote!();
|
||||
let mut rule_code_match_arms = quote!();
|
||||
let mut rule_from_code_match_arms = quote!();
|
||||
let mut diagkind_code_match_arms = quote!();
|
||||
let mut diagkind_body_match_arms = quote!();
|
||||
let mut diagkind_fixable_match_arms = quote!();
|
||||
let mut diagkind_commit_match_arms = quote!();
|
||||
let mut from_impls_for_diagkind = quote!();
|
||||
|
||||
for (code, path, name) in mapping.entries {
|
||||
rulecode_variants.extend(quote! {#code,});
|
||||
for (code, path, name) in &mapping.entries {
|
||||
let code_str = LitStr::new(&code.to_string(), Span::call_site());
|
||||
rule_variants.extend(quote! {
|
||||
#[doc = #code_str]
|
||||
#name,
|
||||
});
|
||||
diagkind_variants.extend(quote! {#name(#path),});
|
||||
rulecode_kind_match_arms.extend(
|
||||
quote! {RuleCode::#code => DiagnosticKind::#name(<#path as Violation>::placeholder()),},
|
||||
);
|
||||
let origin = get_origin(&code);
|
||||
rulecode_origin_match_arms.extend(quote! {RuleCode::#code => RuleOrigin::#origin,});
|
||||
diagkind_code_match_arms.extend(quote! {DiagnosticKind::#name(..) => &RuleCode::#code, });
|
||||
diagkind_body_match_arms
|
||||
.extend(quote! {DiagnosticKind::#name(x) => Violation::message(x), });
|
||||
rule_message_formats_match_arms
|
||||
.extend(quote! {Self::#name => <#path as Violation>::message_formats(),});
|
||||
rule_autofixable_match_arms.extend(quote! {Self::#name => <#path as Violation>::AUTOFIX,});
|
||||
rule_code_match_arms.extend(quote! {Self::#name => #code_str,});
|
||||
rule_from_code_match_arms.extend(quote! {#code_str => Ok(&Rule::#name), });
|
||||
diagkind_code_match_arms.extend(quote! {Self::#name(..) => &Rule::#name, });
|
||||
diagkind_body_match_arms.extend(quote! {Self::#name(x) => Violation::message(x), });
|
||||
diagkind_fixable_match_arms
|
||||
.extend(quote! {DiagnosticKind::#name(x) => x.autofix_title_formatter().is_some(),});
|
||||
diagkind_commit_match_arms.extend(
|
||||
quote! {DiagnosticKind::#name(x) => x.autofix_title_formatter().map(|f| f(x)), },
|
||||
);
|
||||
.extend(quote! {Self::#name(x) => x.autofix_title_formatter().is_some(),});
|
||||
diagkind_commit_match_arms
|
||||
.extend(quote! {Self::#name(x) => x.autofix_title_formatter().map(|f| f(x)), });
|
||||
from_impls_for_diagkind.extend(quote! {
|
||||
impl From<#path> for DiagnosticKind {
|
||||
fn from(x: #path) -> Self {
|
||||
@@ -39,44 +45,69 @@ pub fn define_rule_mapping(mapping: Mapping) -> proc_macro2::TokenStream {
|
||||
});
|
||||
}
|
||||
|
||||
let code_to_name: HashMap<_, _> = mapping
|
||||
.entries
|
||||
.iter()
|
||||
.map(|(code, _, name)| (code.to_string(), name))
|
||||
.collect();
|
||||
|
||||
let rulecodeprefix = super::rule_code_prefix::expand(
|
||||
&Ident::new("Rule", Span::call_site()),
|
||||
&Ident::new("RuleSelector", Span::call_site()),
|
||||
mapping.entries.iter().map(|(code, ..)| code),
|
||||
|code| code_to_name[code],
|
||||
);
|
||||
|
||||
quote! {
|
||||
#[derive(
|
||||
AsRefStr,
|
||||
RuleCodePrefix,
|
||||
EnumIter,
|
||||
EnumString,
|
||||
Debug,
|
||||
Display,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Clone,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Hash,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
AsRefStr,
|
||||
)]
|
||||
pub enum RuleCode { #rulecode_variants }
|
||||
#[strum(serialize_all = "kebab-case")]
|
||||
pub enum Rule { #rule_variants }
|
||||
|
||||
#[derive(AsRefStr, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum DiagnosticKind { #diagkind_variants }
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum FromCodeError {
|
||||
#[error("unknown rule code")]
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl RuleCode {
|
||||
/// A placeholder representation of the `DiagnosticKind` for the diagnostic.
|
||||
pub fn kind(&self) -> DiagnosticKind {
|
||||
match self { #rulecode_kind_match_arms }
|
||||
impl Rule {
|
||||
/// Returns the format strings used to report violations of this rule.
|
||||
pub fn message_formats(&self) -> &'static [&'static str] {
|
||||
match self { #rule_message_formats_match_arms }
|
||||
}
|
||||
|
||||
pub fn origin(&self) -> RuleOrigin {
|
||||
match self { #rulecode_origin_match_arms }
|
||||
pub fn autofixable(&self) -> Option<crate::violation::AutofixKind> {
|
||||
match self { #rule_autofixable_match_arms }
|
||||
}
|
||||
|
||||
pub fn code(&self) -> &'static str {
|
||||
match self { #rule_code_match_arms }
|
||||
}
|
||||
|
||||
pub fn from_code(code: &str) -> Result<&'static Self, FromCodeError> {
|
||||
match code {
|
||||
#rule_from_code_match_arms
|
||||
_ => Err(FromCodeError::Unknown),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl DiagnosticKind {
|
||||
/// A four-letter shorthand code for the diagnostic.
|
||||
pub fn code(&self) -> &'static RuleCode {
|
||||
/// The rule of the diagnostic.
|
||||
pub fn rule(&self) -> &'static Rule {
|
||||
match self { #diagkind_code_match_arms }
|
||||
}
|
||||
|
||||
@@ -97,22 +128,11 @@ pub fn define_rule_mapping(mapping: Mapping) -> proc_macro2::TokenStream {
|
||||
}
|
||||
|
||||
#from_impls_for_diagkind
|
||||
|
||||
#rulecodeprefix
|
||||
}
|
||||
}
|
||||
|
||||
fn get_origin(ident: &Ident) -> Ident {
|
||||
let ident = ident.to_string();
|
||||
let mut iter = crate::prefixes::PREFIX_TO_ORIGIN.iter();
|
||||
let origin = loop {
|
||||
let (prefix, origin) = iter
|
||||
.next()
|
||||
.unwrap_or_else(|| panic!("code doesn't start with any recognized prefix: {ident}"));
|
||||
if ident.starts_with(prefix) {
|
||||
break origin;
|
||||
}
|
||||
};
|
||||
Ident::new(origin, Span::call_site())
|
||||
}
|
||||
pub struct Mapping {
|
||||
entries: Vec<(Ident, Path, Ident)>,
|
||||
}
|
||||
|
||||
55
ruff_macros/src/derive_message_formats.rs
Normal file
55
ruff_macros/src/derive_message_formats.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, quote_spanned, ToTokens};
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{Block, Expr, ItemFn, Stmt};
|
||||
|
||||
pub fn derive_message_formats(func: &ItemFn) -> proc_macro2::TokenStream {
|
||||
let mut strings = quote!();
|
||||
|
||||
if let Err(err) = parse_block(&func.block, &mut strings) {
|
||||
return err;
|
||||
}
|
||||
|
||||
quote! {
|
||||
#func
|
||||
fn message_formats() -> &'static [&'static str] {
|
||||
&[#strings]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_block(block: &Block, strings: &mut TokenStream) -> Result<(), TokenStream> {
|
||||
let Some(Stmt::Expr(last)) = block.stmts.last() else {panic!("expected last statement in block to be an expression")};
|
||||
parse_expr(last, strings)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_expr(expr: &Expr, strings: &mut TokenStream) -> Result<(), TokenStream> {
|
||||
match expr {
|
||||
Expr::Macro(mac) if mac.mac.path.is_ident("format") => {
|
||||
let Some(first_token) = mac.mac.tokens.to_token_stream().into_iter().next() else {
|
||||
return Err(quote_spanned!(expr.span() => compile_error!("expected format! to have an argument")))
|
||||
};
|
||||
strings.extend(quote! {#first_token,});
|
||||
Ok(())
|
||||
}
|
||||
Expr::Block(block) => parse_block(&block.block, strings),
|
||||
Expr::If(expr) => {
|
||||
parse_block(&expr.then_branch, strings)?;
|
||||
if let Some((_, then)) = &expr.else_branch {
|
||||
parse_expr(then, strings)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Expr::Match(block) => {
|
||||
for arm in &block.arms {
|
||||
parse_expr(&arm.body, strings)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(quote_spanned!(
|
||||
expr.span() =>
|
||||
compile_error!("expected last expression to be a format! macro or a match block")
|
||||
)),
|
||||
}
|
||||
}
|
||||
@@ -13,11 +13,13 @@
|
||||
)]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
use syn::{parse_macro_input, DeriveInput};
|
||||
use proc_macro::TokenStream;
|
||||
use syn::{parse_macro_input, DeriveInput, ItemFn};
|
||||
|
||||
mod config;
|
||||
mod define_rule_mapping;
|
||||
mod prefixes;
|
||||
mod derive_message_formats;
|
||||
mod parse_code;
|
||||
mod rule_code_prefix;
|
||||
|
||||
#[proc_macro_derive(ConfigurationOptions, attributes(option, doc, option_group))]
|
||||
@@ -29,17 +31,23 @@ pub fn derive_config(input: proc_macro::TokenStream) -> proc_macro::TokenStream
|
||||
.into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(RuleCodePrefix)]
|
||||
#[proc_macro]
|
||||
pub fn define_rule_mapping(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let mapping = parse_macro_input!(item as define_rule_mapping::Mapping);
|
||||
define_rule_mapping::define_rule_mapping(&mapping).into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(ParseCode, attributes(prefix))]
|
||||
pub fn derive_rule_code_prefix(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
rule_code_prefix::derive_impl(input)
|
||||
parse_code::derive_impl(input)
|
||||
.unwrap_or_else(syn::Error::into_compile_error)
|
||||
.into()
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn define_rule_mapping(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let mapping = parse_macro_input!(item as define_rule_mapping::Mapping);
|
||||
define_rule_mapping::define_rule_mapping(mapping).into()
|
||||
#[proc_macro_attribute]
|
||||
pub fn derive_message_formats(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let func = parse_macro_input!(item as ItemFn);
|
||||
derive_message_formats::derive_message_formats(&func).into()
|
||||
}
|
||||
|
||||
54
ruff_macros/src/parse_code.rs
Normal file
54
ruff_macros/src/parse_code.rs
Normal file
@@ -0,0 +1,54 @@
|
||||
use quote::quote;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{Data, DataEnum, DeriveInput, Error, Lit, Meta, MetaNameValue};
|
||||
|
||||
pub fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
|
||||
let DeriveInput { ident, data: Data::Enum(DataEnum {
|
||||
variants, ..
|
||||
}), .. } = input else {
|
||||
return Err(Error::new(input.ident.span(), "only named fields are supported"));
|
||||
};
|
||||
|
||||
let mut parsed = Vec::new();
|
||||
|
||||
for variant in variants {
|
||||
let prefix_attrs: Vec<_> = variant
|
||||
.attrs
|
||||
.iter()
|
||||
.filter(|a| a.path.is_ident("prefix"))
|
||||
.collect();
|
||||
|
||||
if prefix_attrs.is_empty() {
|
||||
return Err(Error::new(
|
||||
variant.span(),
|
||||
r#"Missing [#prefix = "..."] attribute"#,
|
||||
));
|
||||
}
|
||||
|
||||
for attr in prefix_attrs {
|
||||
let Ok(Meta::NameValue(MetaNameValue{lit: Lit::Str(lit), ..})) = attr.parse_meta() else {
|
||||
return Err(Error::new(attr.span(), r#"expected attribute to be in the form of [#prefix = "..."]"#))
|
||||
};
|
||||
parsed.push((lit, variant.ident.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
parsed.sort_by_key(|(prefix, _)| prefix.value().len());
|
||||
|
||||
let mut if_statements = quote!();
|
||||
|
||||
for (prefix, field) in parsed {
|
||||
if_statements.extend(quote! {if let Some(rest) = code.strip_prefix(#prefix) {
|
||||
return Some((#ident::#field, rest));
|
||||
}});
|
||||
}
|
||||
|
||||
Ok(quote! {
|
||||
impl crate::registry::ParseCode for #ident {
|
||||
fn parse_code(code: &str) -> Option<(Self, &str)> {
|
||||
#if_statements
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
// Longer prefixes should come first so that you can find an origin for a code
|
||||
// by simply picking the first entry that starts with the given prefix.
|
||||
|
||||
pub const PREFIX_TO_ORIGIN: &[(&str, &str)] = &[
|
||||
("ANN", "Flake8Annotations"),
|
||||
("ARG", "Flake8UnusedArguments"),
|
||||
("A", "Flake8Builtins"),
|
||||
("BLE", "Flake8BlindExcept"),
|
||||
("B", "Flake8Bugbear"),
|
||||
("C4", "Flake8Comprehensions"),
|
||||
("C9", "McCabe"),
|
||||
("COM", "Flake8Commas"),
|
||||
("DTZ", "Flake8Datetimez"),
|
||||
("D", "Pydocstyle"),
|
||||
("ERA", "Eradicate"),
|
||||
("EM", "Flake8ErrMsg"),
|
||||
("E", "Pycodestyle"),
|
||||
("FBT", "Flake8BooleanTrap"),
|
||||
("F", "Pyflakes"),
|
||||
("ICN", "Flake8ImportConventions"),
|
||||
("ISC", "Flake8ImplicitStrConcat"),
|
||||
("I", "Isort"),
|
||||
("N", "PEP8Naming"),
|
||||
("PD", "PandasVet"),
|
||||
("PGH", "PygrepHooks"),
|
||||
("PL", "Pylint"),
|
||||
("PT", "Flake8PytestStyle"),
|
||||
("Q", "Flake8Quotes"),
|
||||
("RET", "Flake8Return"),
|
||||
("SIM", "Flake8Simplify"),
|
||||
("S", "Flake8Bandit"),
|
||||
("T10", "Flake8Debugger"),
|
||||
("T20", "Flake8Print"),
|
||||
("TID", "Flake8TidyImports"),
|
||||
("UP", "Pyupgrade"),
|
||||
("W", "Pycodestyle"),
|
||||
("YTT", "Flake82020"),
|
||||
("PIE", "Flake8Pie"),
|
||||
("RUF", "Ruff"),
|
||||
];
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::PREFIX_TO_ORIGIN;
|
||||
|
||||
#[test]
|
||||
fn order() {
|
||||
for (idx, (prefix, _)) in PREFIX_TO_ORIGIN.iter().enumerate() {
|
||||
for (prior_prefix, _) in PREFIX_TO_ORIGIN[..idx].iter() {
|
||||
assert!(!prefix.starts_with(prior_prefix));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,14 +3,12 @@ use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||
use once_cell::sync::Lazy;
|
||||
use proc_macro2::Span;
|
||||
use quote::quote;
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::token::Comma;
|
||||
use syn::{DataEnum, DeriveInput, Ident, Variant};
|
||||
use syn::Ident;
|
||||
|
||||
const ALL: &str = "ALL";
|
||||
|
||||
/// A hash map from deprecated `RuleCodePrefix` to latest
|
||||
/// `RuleCodePrefix`.
|
||||
/// A hash map from deprecated `RuleSelector` to latest
|
||||
/// `RuleSelector`.
|
||||
pub static PREFIX_REDIRECTS: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
|
||||
HashMap::from_iter([
|
||||
// TODO(charlie): Remove by 2023-01-01.
|
||||
@@ -86,43 +84,17 @@ pub static PREFIX_REDIRECTS: Lazy<HashMap<&'static str, &'static str>> = Lazy::n
|
||||
])
|
||||
});
|
||||
|
||||
pub fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
|
||||
let DeriveInput { ident, data, .. } = input;
|
||||
let syn::Data::Enum(DataEnum { variants, .. }) = data else {
|
||||
return Err(syn::Error::new(
|
||||
ident.span(),
|
||||
"Can only derive `RuleCodePrefix` from enums.",
|
||||
));
|
||||
};
|
||||
|
||||
let prefix_ident = Ident::new(&format!("{ident}Prefix"), ident.span());
|
||||
let prefix = expand(&ident, &prefix_ident, &variants);
|
||||
let expanded = quote! {
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum SuffixLength {
|
||||
None,
|
||||
Zero,
|
||||
One,
|
||||
Two,
|
||||
Three,
|
||||
Four,
|
||||
}
|
||||
|
||||
#prefix
|
||||
};
|
||||
Ok(expanded)
|
||||
}
|
||||
|
||||
fn expand(
|
||||
ident: &Ident,
|
||||
pub fn expand<'a>(
|
||||
rule_type: &Ident,
|
||||
prefix_ident: &Ident,
|
||||
variants: &Punctuated<Variant, Comma>,
|
||||
variants: impl Iterator<Item = &'a Ident>,
|
||||
variant_name: impl Fn(&str) -> &'a Ident,
|
||||
) -> proc_macro2::TokenStream {
|
||||
// Build up a map from prefix to matching RuleCodes.
|
||||
let mut prefix_to_codes: BTreeMap<Ident, BTreeSet<String>> = BTreeMap::default();
|
||||
for variant in variants {
|
||||
let span = variant.ident.span();
|
||||
let code_str = variant.ident.to_string();
|
||||
let span = variant.span();
|
||||
let code_str = variant.to_string();
|
||||
let code_prefix_len = code_str
|
||||
.chars()
|
||||
.take_while(|char| char.is_alphabetic())
|
||||
@@ -158,7 +130,7 @@ fn expand(
|
||||
}
|
||||
});
|
||||
|
||||
let prefix_impl = generate_impls(ident, prefix_ident, &prefix_to_codes);
|
||||
let prefix_impl = generate_impls(rule_type, prefix_ident, &prefix_to_codes, variant_name);
|
||||
|
||||
let prefix_redirects = PREFIX_REDIRECTS.iter().map(|(alias, rule_code)| {
|
||||
let code = Ident::new(rule_code, Span::call_site());
|
||||
@@ -168,6 +140,16 @@ fn expand(
|
||||
});
|
||||
|
||||
quote! {
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum SuffixLength {
|
||||
None,
|
||||
Zero,
|
||||
One,
|
||||
Two,
|
||||
Three,
|
||||
Four,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
::strum_macros::EnumString,
|
||||
::strum_macros::AsRefStr,
|
||||
@@ -187,7 +169,7 @@ fn expand(
|
||||
|
||||
#prefix_impl
|
||||
|
||||
/// A hash map from deprecated `RuleCodePrefix` to latest `RuleCodePrefix`.
|
||||
/// A hash map from deprecated `RuleSelector` to latest `RuleSelector`.
|
||||
pub static PREFIX_REDIRECTS: ::once_cell::sync::Lazy<::rustc_hash::FxHashMap<&'static str, #prefix_ident>> = ::once_cell::sync::Lazy::new(|| {
|
||||
::rustc_hash::FxHashMap::from_iter([
|
||||
#(#prefix_redirects),*
|
||||
@@ -196,16 +178,17 @@ fn expand(
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_impls(
|
||||
ident: &Ident,
|
||||
fn generate_impls<'a>(
|
||||
rule_type: &Ident,
|
||||
prefix_ident: &Ident,
|
||||
prefix_to_codes: &BTreeMap<Ident, BTreeSet<String>>,
|
||||
variant_name: impl Fn(&str) -> &'a Ident,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let codes_match_arms = prefix_to_codes.iter().map(|(prefix, codes)| {
|
||||
let codes = codes.iter().map(|code| {
|
||||
let code = Ident::new(code, Span::call_site());
|
||||
let rule_variant = variant_name(code);
|
||||
quote! {
|
||||
#ident::#code
|
||||
#rule_type::#rule_variant
|
||||
}
|
||||
});
|
||||
let prefix_str = prefix.to_string();
|
||||
@@ -265,7 +248,7 @@ fn generate_impls(
|
||||
|
||||
quote! {
|
||||
impl #prefix_ident {
|
||||
pub fn codes(&self) -> Vec<#ident> {
|
||||
pub fn codes(&self) -> Vec<#rule_type> {
|
||||
use colored::Colorize;
|
||||
|
||||
#[allow(clippy::match_same_arms)]
|
||||
|
||||
18
scripts/_utils.py
Normal file
18
scripts/_utils.py
Normal file
@@ -0,0 +1,18 @@
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
ROOT_DIR = Path(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
|
||||
def dir_name(linter_name: str) -> str:
|
||||
return linter_name.replace("-", "_")
|
||||
|
||||
|
||||
def pascal_case(linter_name: str) -> str:
|
||||
"""Convert from snake-case to PascalCase."""
|
||||
return "".join(word.title() for word in linter_name.split("-"))
|
||||
|
||||
|
||||
def get_indent(line: str) -> str:
|
||||
return re.match(r"^\s*", line).group() # pyright: ignore[reportOptionalMemberAccess]
|
||||
43
scripts/add_plugin.py
Normal file → Executable file
43
scripts/add_plugin.py
Normal file → Executable file
@@ -10,17 +10,8 @@ Example usage:
|
||||
|
||||
import argparse
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
ROOT_DIR = Path(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
|
||||
def dir_name(plugin: str) -> str:
|
||||
return plugin.replace("-", "_")
|
||||
|
||||
|
||||
def pascal_case(plugin: str) -> str:
|
||||
return "".join(word.title() for word in plugin.split("-"))
|
||||
from _utils import ROOT_DIR, dir_name, get_indent, pascal_case
|
||||
|
||||
|
||||
def main(*, plugin: str, url: str) -> None:
|
||||
@@ -36,6 +27,7 @@ def main(*, plugin: str, url: str) -> None:
|
||||
with open(rust_module / "rules.rs", "w+") as fp:
|
||||
fp.write("use crate::checkers::ast::Checker;\n")
|
||||
with open(rust_module / "mod.rs", "w+") as fp:
|
||||
fp.write(f"//! Rules from [{plugin}]({url}).\n")
|
||||
fp.write("pub(crate) mod rules;\n")
|
||||
fp.write("\n")
|
||||
fp.write(
|
||||
@@ -47,11 +39,11 @@ mod tests {
|
||||
use anyhow::Result;
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::registry::RuleCode;
|
||||
use crate::registry::Rule;
|
||||
use crate::linter::test_path;
|
||||
use crate::settings;
|
||||
|
||||
fn rules(rule_code: RuleCode, path: &Path) -> Result<()> {
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.as_ref(), path.to_string_lossy());
|
||||
let diagnostics =test_path(
|
||||
Path::new("./resources/test/fixtures/%s")
|
||||
@@ -76,35 +68,24 @@ mod tests {
|
||||
|
||||
with open(ROOT_DIR / "src/registry.rs", "w") as fp:
|
||||
for line in content.splitlines():
|
||||
indent = get_indent(line)
|
||||
|
||||
if line.strip() == "// Ruff":
|
||||
indent = line.split("// Ruff")[0]
|
||||
fp.write(f"{indent}// {plugin}")
|
||||
fp.write("\n")
|
||||
|
||||
elif line.strip() == "Ruff,":
|
||||
indent = line.split("Ruff,")[0]
|
||||
elif line.strip() == '#[prefix = "RUF"]':
|
||||
fp.write(f'{indent}#[prefix = "TODO"]\n')
|
||||
fp.write(f"{indent}{pascal_case(plugin)},")
|
||||
fp.write("\n")
|
||||
|
||||
elif line.strip() == 'RuleOrigin::Ruff => "Ruff-specific rules",':
|
||||
indent = line.split('RuleOrigin::Ruff => "Ruff-specific rules",')[0]
|
||||
fp.write(f'{indent}RuleOrigin::{pascal_case(plugin)} => "{plugin}",')
|
||||
fp.write("\n")
|
||||
|
||||
elif line.strip() == "RuleOrigin::Ruff => vec![RuleCodePrefix::RUF],":
|
||||
indent = line.split("RuleOrigin::Ruff => vec![RuleCodePrefix::RUF],")[0]
|
||||
elif line.strip() == "Linter::Ruff => Prefixes::Single(RuleSelector::RUF),":
|
||||
prefix = 'todo!("Fill-in prefix after generating codes")'
|
||||
fp.write(
|
||||
f"{indent}RuleOrigin::{pascal_case(plugin)} => vec![\n"
|
||||
f'{indent} todo!("Fill-in prefix after generating codes")\n'
|
||||
f"{indent}],"
|
||||
f"{indent}Linter::{pascal_case(plugin)} => Prefixes::Single({prefix}),"
|
||||
)
|
||||
fp.write("\n")
|
||||
|
||||
elif line.strip() == "RuleOrigin::Ruff => None,":
|
||||
indent = line.split("RuleOrigin::Ruff => None,")[0]
|
||||
fp.write(f"{indent}RuleOrigin::{pascal_case(plugin)} => " f'Some(("{url}", &Platform::PyPI)),')
|
||||
fp.write("\n")
|
||||
|
||||
fp.write(line)
|
||||
fp.write("\n")
|
||||
|
||||
@@ -114,7 +95,7 @@ mod tests {
|
||||
with open(ROOT_DIR / "src/violations.rs", "w") as fp:
|
||||
for line in content.splitlines():
|
||||
if line.strip() == "// Ruff":
|
||||
indent = line.split("// Ruff")[0]
|
||||
indent = get_indent(line)
|
||||
fp.write(f"{indent}// {plugin}")
|
||||
fp.write("\n")
|
||||
|
||||
|
||||
55
scripts/add_rule.py
Normal file → Executable file
55
scripts/add_rule.py
Normal file → Executable file
@@ -6,23 +6,12 @@ Example usage:
|
||||
python scripts/add_rule.py \
|
||||
--name PreferListBuiltin \
|
||||
--code PIE807 \
|
||||
--origin flake8-pie
|
||||
--linter flake8-pie
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
ROOT_DIR = Path(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
|
||||
def dir_name(origin: str) -> str:
|
||||
return origin.replace("-", "_")
|
||||
|
||||
|
||||
def pascal_case(origin: str) -> str:
|
||||
"""Convert from snake-case to PascalCase."""
|
||||
return "".join(word.title() for word in origin.split("-"))
|
||||
from _utils import ROOT_DIR, dir_name, get_indent
|
||||
|
||||
|
||||
def snake_case(name: str) -> str:
|
||||
@@ -30,30 +19,30 @@ def snake_case(name: str) -> str:
|
||||
return "".join(f"_{word.lower()}" if word.isupper() else word for word in name).lstrip("_")
|
||||
|
||||
|
||||
def main(*, name: str, code: str, origin: str) -> None:
|
||||
def main(*, name: str, code: str, linter: str) -> None:
|
||||
# Create a test fixture.
|
||||
with open(
|
||||
ROOT_DIR / "resources/test/fixtures" / dir_name(origin) / f"{code}.py",
|
||||
ROOT_DIR / "resources/test/fixtures" / dir_name(linter) / f"{code}.py",
|
||||
"a",
|
||||
):
|
||||
pass
|
||||
|
||||
# Add the relevant `#testcase` macro.
|
||||
mod_rs = ROOT_DIR / "src/rules" / dir_name(origin) / "mod.rs"
|
||||
mod_rs = ROOT_DIR / "src/rules" / dir_name(linter) / "mod.rs"
|
||||
content = mod_rs.read_text()
|
||||
|
||||
with open(mod_rs, "w") as fp:
|
||||
for line in content.splitlines():
|
||||
if line.strip() == "fn rules(rule_code: RuleCode, path: &Path) -> Result<()> {":
|
||||
indent = line.split("fn rules(rule_code: RuleCode, path: &Path) -> Result<()> {")[0]
|
||||
fp.write(f'{indent}#[test_case(RuleCode::{code}, Path::new("{code}.py"); "{code}")]')
|
||||
if line.strip() == "fn rules(rule_code: Rule, path: &Path) -> Result<()> {":
|
||||
indent = get_indent(line)
|
||||
fp.write(f'{indent}#[test_case(Rule::{code}, Path::new("{code}.py"); "{code}")]')
|
||||
fp.write("\n")
|
||||
|
||||
fp.write(line)
|
||||
fp.write("\n")
|
||||
|
||||
# Add the relevant rule function.
|
||||
with open(ROOT_DIR / "src/rules" / dir_name(origin) / "rules.rs", "a") as fp:
|
||||
with open(ROOT_DIR / "src/rules" / dir_name(linter) / (snake_case(name) + ".rs"), "w") as fp:
|
||||
fp.write(
|
||||
f"""
|
||||
/// {code}
|
||||
@@ -70,22 +59,20 @@ pub fn {snake_case(name)}(checker: &mut Checker) {{}}
|
||||
fp.write(line)
|
||||
fp.write("\n")
|
||||
|
||||
if line.startswith(f"// {origin}"):
|
||||
if line.startswith(f"// {linter}"):
|
||||
fp.write(
|
||||
"""define_violation!(
|
||||
pub struct %s;
|
||||
);
|
||||
impl Violation for %s {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
todo!("Implement message")
|
||||
}
|
||||
|
||||
fn placeholder() -> Self {
|
||||
%s
|
||||
todo!("implement message");
|
||||
format!("TODO: write message")
|
||||
}
|
||||
}
|
||||
"""
|
||||
% (name, name, name)
|
||||
% (name, name)
|
||||
)
|
||||
fp.write("\n")
|
||||
|
||||
@@ -102,24 +89,26 @@ impl Violation for %s {
|
||||
if has_written:
|
||||
continue
|
||||
|
||||
if line.startswith("define_rule_mapping!"):
|
||||
if line.startswith("ruff_macros::define_rule_mapping!"):
|
||||
seen_macro = True
|
||||
continue
|
||||
|
||||
if not seen_macro:
|
||||
continue
|
||||
|
||||
if line.strip() == f"// {origin}":
|
||||
indent = line.split("//")[0]
|
||||
if line.strip() == f"// {linter}":
|
||||
indent = get_indent(line)
|
||||
fp.write(f"{indent}{code} => violations::{name},")
|
||||
fp.write("\n")
|
||||
has_written = True
|
||||
|
||||
assert has_written
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generate boilerplate for a new rule.",
|
||||
epilog="python scripts/add_rule.py --name PreferListBuiltin --code PIE807 --origin flake8-pie",
|
||||
epilog="python scripts/add_rule.py --name PreferListBuiltin --code PIE807 --linter flake8-pie",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--name",
|
||||
@@ -134,11 +123,11 @@ if __name__ == "__main__":
|
||||
help="The code of the check to generate (e.g., 'A001').",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--origin",
|
||||
"--linter",
|
||||
type=str,
|
||||
required=True,
|
||||
help="The source with which the check originated (e.g., 'flake8-builtins').",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
main(name=args.name, code=args.code, origin=args.origin)
|
||||
main(name=args.name, code=args.code, linter=args.linter)
|
||||
|
||||
9
scripts/pyproject.toml
Normal file
9
scripts/pyproject.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[tool.ruff]
|
||||
select = ["ALL"]
|
||||
ignore = [
|
||||
"S101", # assert-used
|
||||
"PLR2004", # magic-value-comparison
|
||||
]
|
||||
|
||||
[tool.ruff.pydocstyle]
|
||||
convention = "pep257"
|
||||
1
setup.py
1
setup.py
@@ -13,6 +13,7 @@ Please use `python -m pip install .` instead.
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
"abc".isidentifier()
|
||||
|
||||
# The below code will never execute, however GitHub is particularly
|
||||
# picky about where it finds Python packaging metadata.
|
||||
|
||||
1551
src/checkers/ast.rs
1551
src/checkers/ast.rs
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
use std::path::Path;
|
||||
|
||||
use crate::registry::{Diagnostic, RuleCode};
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::rules::flake8_no_pep420::rules::implicit_namespace_package;
|
||||
use crate::settings::Settings;
|
||||
|
||||
@@ -8,7 +8,7 @@ pub fn check_file_path(path: &Path, settings: &Settings) -> Vec<Diagnostic> {
|
||||
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||
|
||||
// flake8-no-pep420
|
||||
if settings.rules.enabled(&RuleCode::INP001) {
|
||||
if settings.rules.enabled(&Rule::ImplicitNamespacePackage) {
|
||||
if let Some(diagnostic) = implicit_namespace_package(path) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use rustpython_parser::ast::Suite;
|
||||
|
||||
use crate::ast::visitor::Visitor;
|
||||
use crate::directives::IsortDirectives;
|
||||
use crate::registry::{Diagnostic, RuleCode};
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::rules::isort;
|
||||
use crate::rules::isort::track::{Block, ImportTracker};
|
||||
use crate::settings::{flags, Settings};
|
||||
@@ -36,7 +36,7 @@ pub fn check_imports(
|
||||
|
||||
// Enforce import rules.
|
||||
let mut diagnostics = vec![];
|
||||
if settings.rules.enabled(&RuleCode::I001) {
|
||||
if settings.rules.enabled(&Rule::UnsortedImports) {
|
||||
for block in &blocks {
|
||||
if !block.imports.is_empty() {
|
||||
if let Some(diagnostic) = isort::rules::organize_imports(
|
||||
@@ -47,7 +47,7 @@ pub fn check_imports(
|
||||
}
|
||||
}
|
||||
}
|
||||
if settings.rules.enabled(&RuleCode::I002) {
|
||||
if settings.rules.enabled(&Rule::MissingRequiredImport) {
|
||||
diagnostics.extend(isort::rules::add_required_imports(
|
||||
&blocks, python_ast, locator, settings, autofix,
|
||||
));
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
//! Lint rules based on checking raw physical lines.
|
||||
|
||||
use crate::registry::{Diagnostic, RuleCode};
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::rules::flake8_executable::helpers::extract_shebang;
|
||||
use crate::rules::flake8_executable::rules::{shebang_newline, shebang_python, shebang_whitespace};
|
||||
use crate::rules::pycodestyle::rules::{
|
||||
doc_line_too_long, line_too_long, no_newline_at_end_of_file,
|
||||
doc_line_too_long, line_too_long, mixed_spaces_and_tabs, no_newline_at_end_of_file,
|
||||
};
|
||||
use crate::rules::pygrep_hooks::rules::{blanket_noqa, blanket_type_ignore};
|
||||
use crate::rules::pyupgrade::rules::unnecessary_coding_comment;
|
||||
@@ -17,12 +19,25 @@ pub fn check_lines(
|
||||
) -> Vec<Diagnostic> {
|
||||
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||
|
||||
let enforce_blanket_noqa = settings.rules.enabled(&RuleCode::PGH004);
|
||||
let enforce_blanket_type_ignore = settings.rules.enabled(&RuleCode::PGH003);
|
||||
let enforce_doc_line_too_long = settings.rules.enabled(&RuleCode::W505);
|
||||
let enforce_line_too_long = settings.rules.enabled(&RuleCode::E501);
|
||||
let enforce_no_newline_at_end_of_file = settings.rules.enabled(&RuleCode::W292);
|
||||
let enforce_unnecessary_coding_comment = settings.rules.enabled(&RuleCode::UP009);
|
||||
let enforce_blanket_noqa = settings.rules.enabled(&Rule::BlanketNOQA);
|
||||
let enforce_shebang_whitespace = settings.rules.enabled(&Rule::ShebangWhitespace);
|
||||
let enforce_shebang_newline = settings.rules.enabled(&Rule::ShebangNewline);
|
||||
let enforce_shebang_python = settings.rules.enabled(&Rule::ShebangPython);
|
||||
let enforce_blanket_type_ignore = settings.rules.enabled(&Rule::BlanketTypeIgnore);
|
||||
let enforce_doc_line_too_long = settings.rules.enabled(&Rule::DocLineTooLong);
|
||||
let enforce_line_too_long = settings.rules.enabled(&Rule::LineTooLong);
|
||||
let enforce_no_newline_at_end_of_file = settings.rules.enabled(&Rule::NoNewLineAtEndOfFile);
|
||||
let enforce_unnecessary_coding_comment = settings
|
||||
.rules
|
||||
.enabled(&Rule::PEP3120UnnecessaryCodingComment);
|
||||
let enforce_mixed_spaces_and_tabs = settings.rules.enabled(&Rule::MixedSpacesAndTabs);
|
||||
|
||||
let fix_unnecessary_coding_comment = matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings
|
||||
.rules
|
||||
.should_fix(&Rule::PEP3120UnnecessaryCodingComment);
|
||||
let fix_shebang_whitespace = matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings.rules.should_fix(&Rule::ShebangWhitespace);
|
||||
|
||||
let mut commented_lines_iter = commented_lines.iter().peekable();
|
||||
let mut doc_lines_iter = doc_lines.iter().peekable();
|
||||
@@ -33,12 +48,9 @@ pub fn check_lines(
|
||||
{
|
||||
if enforce_unnecessary_coding_comment {
|
||||
if index < 2 {
|
||||
if let Some(diagnostic) = unnecessary_coding_comment(
|
||||
index,
|
||||
line,
|
||||
matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings.rules.should_fix(&RuleCode::UP009),
|
||||
) {
|
||||
if let Some(diagnostic) =
|
||||
unnecessary_coding_comment(index, line, fix_unnecessary_coding_comment)
|
||||
{
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
@@ -55,6 +67,27 @@ pub fn check_lines(
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
if enforce_shebang_whitespace || enforce_shebang_newline || enforce_shebang_python {
|
||||
let shebang = extract_shebang(line);
|
||||
if enforce_shebang_whitespace {
|
||||
if let Some(diagnostic) =
|
||||
shebang_whitespace(index, &shebang, fix_shebang_whitespace)
|
||||
{
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if enforce_shebang_newline {
|
||||
if let Some(diagnostic) = shebang_newline(index, &shebang) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if enforce_shebang_python {
|
||||
if let Some(diagnostic) = shebang_python(index, &shebang) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while doc_lines_iter
|
||||
@@ -68,6 +101,12 @@ pub fn check_lines(
|
||||
}
|
||||
}
|
||||
|
||||
if enforce_mixed_spaces_and_tabs {
|
||||
if let Some(diagnostic) = mixed_spaces_and_tabs(index, line) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
if enforce_line_too_long {
|
||||
if let Some(diagnostic) = line_too_long(index, line, settings) {
|
||||
diagnostics.push(diagnostic);
|
||||
@@ -79,7 +118,7 @@ pub fn check_lines(
|
||||
if let Some(diagnostic) = no_newline_at_end_of_file(
|
||||
contents,
|
||||
matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings.rules.should_fix(&RuleCode::W292),
|
||||
&& settings.rules.should_fix(&Rule::NoNewLineAtEndOfFile),
|
||||
) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
@@ -92,7 +131,7 @@ pub fn check_lines(
|
||||
mod tests {
|
||||
|
||||
use super::check_lines;
|
||||
use crate::registry::RuleCode;
|
||||
use crate::registry::Rule;
|
||||
use crate::settings::{flags, Settings};
|
||||
|
||||
#[test]
|
||||
@@ -105,7 +144,7 @@ mod tests {
|
||||
&[],
|
||||
&Settings {
|
||||
line_length,
|
||||
..Settings::for_rule(RuleCode::E501)
|
||||
..Settings::for_rule(Rule::LineTooLong)
|
||||
},
|
||||
flags::Autofix::Enabled,
|
||||
)
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
//! `NoQA` enforcement and validation.
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use nohash_hasher::IntMap;
|
||||
use rustpython_parser::ast::Location;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::fix::Fix;
|
||||
use crate::noqa::{is_file_exempt, Directive};
|
||||
use crate::registry::{Diagnostic, DiagnosticKind, RuleCode, CODE_REDIRECTS};
|
||||
use crate::registry::{Diagnostic, DiagnosticKind, Rule, CODE_REDIRECTS};
|
||||
use crate::settings::{flags, Settings};
|
||||
use crate::violations::UnusedCodes;
|
||||
use crate::{noqa, violations};
|
||||
@@ -24,7 +22,7 @@ pub fn check_noqa(
|
||||
let mut noqa_directives: IntMap<usize, (Directive, Vec<&str>)> = IntMap::default();
|
||||
let mut ignored = vec![];
|
||||
|
||||
let enforce_noqa = settings.rules.enabled(&RuleCode::RUF100);
|
||||
let enforce_noqa = settings.rules.enabled(&Rule::UnusedNOQA);
|
||||
|
||||
let lines: Vec<&str> = contents.lines().collect();
|
||||
for lineno in commented_lines {
|
||||
@@ -56,13 +54,13 @@ pub fn check_noqa(
|
||||
});
|
||||
match noqa {
|
||||
(Directive::All(..), matches) => {
|
||||
matches.push(diagnostic.kind.code().as_ref());
|
||||
matches.push(diagnostic.kind.rule().code());
|
||||
ignored.push(index);
|
||||
continue;
|
||||
}
|
||||
(Directive::Codes(.., codes), matches) => {
|
||||
if noqa::includes(diagnostic.kind.code(), codes) {
|
||||
matches.push(diagnostic.kind.code().as_ref());
|
||||
if noqa::includes(diagnostic.kind.rule(), codes) {
|
||||
matches.push(diagnostic.kind.rule().code());
|
||||
ignored.push(index);
|
||||
continue;
|
||||
}
|
||||
@@ -83,12 +81,12 @@ pub fn check_noqa(
|
||||
.or_insert_with(|| (noqa::extract_noqa_directive(lines[noqa_lineno - 1]), vec![]));
|
||||
match noqa {
|
||||
(Directive::All(..), matches) => {
|
||||
matches.push(diagnostic.kind.code().as_ref());
|
||||
matches.push(diagnostic.kind.rule().code());
|
||||
ignored.push(index);
|
||||
}
|
||||
(Directive::Codes(.., codes), matches) => {
|
||||
if noqa::includes(diagnostic.kind.code(), codes) {
|
||||
matches.push(diagnostic.kind.code().as_ref());
|
||||
if noqa::includes(diagnostic.kind.rule(), codes) {
|
||||
matches.push(diagnostic.kind.rule().code());
|
||||
ignored.push(index);
|
||||
}
|
||||
}
|
||||
@@ -108,7 +106,7 @@ pub fn check_noqa(
|
||||
Range::new(Location::new(row + 1, start), Location::new(row + 1, end)),
|
||||
);
|
||||
if matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings.rules.should_fix(diagnostic.kind.code())
|
||||
&& settings.rules.should_fix(diagnostic.kind.rule())
|
||||
{
|
||||
diagnostic.amend(Fix::deletion(
|
||||
Location::new(row + 1, start - spaces),
|
||||
@@ -125,8 +123,8 @@ pub fn check_noqa(
|
||||
let mut valid_codes = vec![];
|
||||
let mut self_ignore = false;
|
||||
for code in codes {
|
||||
let code = CODE_REDIRECTS.get(code).map_or(code, AsRef::as_ref);
|
||||
if code == RuleCode::RUF100.as_ref() {
|
||||
let code = CODE_REDIRECTS.get(code).map_or(code, |r| r.code());
|
||||
if code == Rule::UnusedNOQA.code() {
|
||||
self_ignore = true;
|
||||
break;
|
||||
}
|
||||
@@ -134,8 +132,8 @@ pub fn check_noqa(
|
||||
if matches.contains(&code) || settings.external.contains(code) {
|
||||
valid_codes.push(code);
|
||||
} else {
|
||||
if let Ok(rule_code) = RuleCode::from_str(code) {
|
||||
if settings.rules.enabled(&rule_code) {
|
||||
if let Ok(rule) = Rule::from_code(code) {
|
||||
if settings.rules.enabled(rule) {
|
||||
unmatched_codes.push(code);
|
||||
} else {
|
||||
disabled_codes.push(code);
|
||||
@@ -172,7 +170,7 @@ pub fn check_noqa(
|
||||
Range::new(Location::new(row + 1, start), Location::new(row + 1, end)),
|
||||
);
|
||||
if matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings.rules.should_fix(diagnostic.kind.code())
|
||||
&& settings.rules.should_fix(diagnostic.kind.rule())
|
||||
{
|
||||
if valid_codes.is_empty() {
|
||||
diagnostic.amend(Fix::deletion(
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
use rustpython_parser::lexer::{LexResult, Tok};
|
||||
|
||||
use crate::lex::docstring_detection::StateMachine;
|
||||
use crate::registry::{Diagnostic, RuleCode};
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::rules::ruff::rules::Context;
|
||||
use crate::rules::{
|
||||
eradicate, flake8_commas, flake8_implicit_str_concat, flake8_quotes, pycodestyle, ruff,
|
||||
eradicate, flake8_commas, flake8_implicit_str_concat, flake8_quotes, pycodestyle, pyupgrade,
|
||||
ruff,
|
||||
};
|
||||
use crate::settings::{flags, Settings};
|
||||
use crate::source_code::Locator;
|
||||
@@ -19,20 +20,33 @@ pub fn check_tokens(
|
||||
) -> Vec<Diagnostic> {
|
||||
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||
|
||||
let enforce_ambiguous_unicode_character = settings.rules.enabled(&RuleCode::RUF001)
|
||||
|| settings.rules.enabled(&RuleCode::RUF002)
|
||||
|| settings.rules.enabled(&RuleCode::RUF003);
|
||||
let enforce_quotes = settings.rules.enabled(&RuleCode::Q000)
|
||||
|| settings.rules.enabled(&RuleCode::Q001)
|
||||
|| settings.rules.enabled(&RuleCode::Q002)
|
||||
|| settings.rules.enabled(&RuleCode::Q003);
|
||||
let enforce_commented_out_code = settings.rules.enabled(&RuleCode::ERA001);
|
||||
let enforce_invalid_escape_sequence = settings.rules.enabled(&RuleCode::W605);
|
||||
let enforce_implicit_string_concatenation =
|
||||
settings.rules.enabled(&RuleCode::ISC001) || settings.rules.enabled(&RuleCode::ISC002);
|
||||
let enforce_trailing_comma = settings.rules.enabled(&RuleCode::COM812)
|
||||
|| settings.rules.enabled(&RuleCode::COM818)
|
||||
|| settings.rules.enabled(&RuleCode::COM819);
|
||||
let enforce_ambiguous_unicode_character = settings
|
||||
.rules
|
||||
.enabled(&Rule::AmbiguousUnicodeCharacterString)
|
||||
|| settings
|
||||
.rules
|
||||
.enabled(&Rule::AmbiguousUnicodeCharacterDocstring)
|
||||
|| settings
|
||||
.rules
|
||||
.enabled(&Rule::AmbiguousUnicodeCharacterComment);
|
||||
let enforce_quotes = settings.rules.enabled(&Rule::BadQuotesInlineString)
|
||||
|| settings.rules.enabled(&Rule::BadQuotesMultilineString)
|
||||
|| settings.rules.enabled(&Rule::BadQuotesDocstring)
|
||||
|| settings.rules.enabled(&Rule::AvoidQuoteEscape);
|
||||
let enforce_commented_out_code = settings.rules.enabled(&Rule::CommentedOutCode);
|
||||
let enforce_invalid_escape_sequence = settings.rules.enabled(&Rule::InvalidEscapeSequence);
|
||||
let enforce_implicit_string_concatenation = settings
|
||||
.rules
|
||||
.enabled(&Rule::SingleLineImplicitStringConcatenation)
|
||||
|| settings
|
||||
.rules
|
||||
.enabled(&Rule::MultiLineImplicitStringConcatenation);
|
||||
let enforce_trailing_comma = settings.rules.enabled(&Rule::TrailingCommaMissing)
|
||||
|| settings
|
||||
.rules
|
||||
.enabled(&Rule::TrailingCommaOnBareTupleProhibited)
|
||||
|| settings.rules.enabled(&Rule::TrailingCommaProhibited);
|
||||
let enforce_extraneous_parenthesis = settings.rules.enabled(&Rule::ExtraneousParentheses);
|
||||
|
||||
let mut state_machine = StateMachine::default();
|
||||
for &(start, ref tok, end) in tokens.iter().flatten() {
|
||||
@@ -75,7 +89,7 @@ pub fn check_tokens(
|
||||
settings,
|
||||
autofix,
|
||||
) {
|
||||
if settings.rules.enabled(diagnostic.kind.code()) {
|
||||
if settings.rules.enabled(diagnostic.kind.rule()) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
@@ -101,7 +115,7 @@ pub fn check_tokens(
|
||||
start,
|
||||
end,
|
||||
matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings.rules.should_fix(&RuleCode::W605),
|
||||
&& settings.rules.should_fix(&Rule::InvalidEscapeSequence),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -112,7 +126,7 @@ pub fn check_tokens(
|
||||
diagnostics.extend(
|
||||
flake8_implicit_str_concat::rules::implicit(tokens)
|
||||
.into_iter()
|
||||
.filter(|diagnostic| settings.rules.enabled(diagnostic.kind.code())),
|
||||
.filter(|diagnostic| settings.rules.enabled(diagnostic.kind.rule())),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -121,7 +135,15 @@ pub fn check_tokens(
|
||||
diagnostics.extend(
|
||||
flake8_commas::rules::trailing_commas(tokens, settings, autofix)
|
||||
.into_iter()
|
||||
.filter(|diagnostic| settings.rules.enabled(diagnostic.kind.code())),
|
||||
.filter(|diagnostic| settings.rules.enabled(diagnostic.kind.rule())),
|
||||
);
|
||||
}
|
||||
|
||||
// UP034
|
||||
if enforce_extraneous_parenthesis {
|
||||
diagnostics.extend(
|
||||
pyupgrade::rules::extraneous_parentheses(tokens, locator, settings, autofix)
|
||||
.into_iter(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ use colored::Colorize;
|
||||
use super::black::Black;
|
||||
use super::plugin::Plugin;
|
||||
use super::{parser, plugin};
|
||||
use crate::registry::RuleCodePrefix;
|
||||
use crate::registry::RuleSelector;
|
||||
use crate::rules::flake8_pytest_style::types::{
|
||||
ParametrizeNameType, ParametrizeValuesRowType, ParametrizeValuesType,
|
||||
};
|
||||
@@ -14,8 +14,8 @@ use crate::rules::flake8_quotes::settings::Quote;
|
||||
use crate::rules::flake8_tidy_imports::relative_imports::Strictness;
|
||||
use crate::rules::pydocstyle::settings::Convention;
|
||||
use crate::rules::{
|
||||
flake8_annotations, flake8_bugbear, flake8_errmsg, flake8_pytest_style, flake8_quotes,
|
||||
flake8_tidy_imports, mccabe, pep8_naming, pydocstyle,
|
||||
flake8_annotations, flake8_bugbear, flake8_builtins, flake8_errmsg, flake8_pytest_style,
|
||||
flake8_quotes, flake8_tidy_imports, mccabe, pep8_naming, pydocstyle,
|
||||
};
|
||||
use crate::settings::options::Options;
|
||||
use crate::settings::pyproject::Pyproject;
|
||||
@@ -32,7 +32,7 @@ pub fn convert(
|
||||
.expect("Unable to find flake8 section in INI file");
|
||||
|
||||
// Extract all referenced rule code prefixes, to power plugin inference.
|
||||
let mut referenced_codes: BTreeSet<RuleCodePrefix> = BTreeSet::default();
|
||||
let mut referenced_codes: BTreeSet<RuleSelector> = BTreeSet::default();
|
||||
for (key, value) in flake8 {
|
||||
if let Some(value) = value {
|
||||
match key.as_str() {
|
||||
@@ -90,6 +90,7 @@ pub fn convert(
|
||||
let mut options = Options::default();
|
||||
let mut flake8_annotations = flake8_annotations::settings::Options::default();
|
||||
let mut flake8_bugbear = flake8_bugbear::settings::Options::default();
|
||||
let mut flake8_builtins = flake8_builtins::settings::Options::default();
|
||||
let mut flake8_errmsg = flake8_errmsg::settings::Options::default();
|
||||
let mut flake8_pytest_style = flake8_pytest_style::settings::Options::default();
|
||||
let mut flake8_quotes = flake8_quotes::settings::Options::default();
|
||||
@@ -104,7 +105,7 @@ pub fn convert(
|
||||
"builtins" => {
|
||||
options.builtins = Some(parser::parse_strings(value.as_ref()));
|
||||
}
|
||||
"max-line-length" | "max_line_length" => match value.clone().parse::<usize>() {
|
||||
"max-line-length" | "max_line_length" => match value.parse::<usize>() {
|
||||
Ok(line_length) => options.line_length = Some(line_length),
|
||||
Err(e) => {
|
||||
warn_user!("Unable to parse '{key}' property: {e}");
|
||||
@@ -147,6 +148,11 @@ pub fn convert(
|
||||
flake8_bugbear.extend_immutable_calls =
|
||||
Some(parser::parse_strings(value.as_ref()));
|
||||
}
|
||||
// flake8-builtins
|
||||
"builtins-ignorelist" | "builtins_ignorelist" => {
|
||||
flake8_builtins.builtins_ignorelist =
|
||||
Some(parser::parse_strings(value.as_ref()));
|
||||
}
|
||||
// flake8-annotations
|
||||
"suppress-none-returning" | "suppress_none_returning" => {
|
||||
match parser::parse_bool(value.as_ref()) {
|
||||
@@ -241,7 +247,7 @@ pub fn convert(
|
||||
}
|
||||
},
|
||||
// mccabe
|
||||
"max-complexity" | "max_complexity" => match value.clone().parse::<usize>() {
|
||||
"max-complexity" | "max_complexity" => match value.parse::<usize>() {
|
||||
Ok(max_complexity) => mccabe.max_complexity = Some(max_complexity),
|
||||
Err(e) => {
|
||||
warn_user!("Unable to parse '{key}' property: {e}");
|
||||
@@ -249,7 +255,7 @@ pub fn convert(
|
||||
},
|
||||
// flake8-errmsg
|
||||
"errmsg-max-string-length" | "errmsg_max_string_length" => {
|
||||
match value.clone().parse::<usize>() {
|
||||
match value.parse::<usize>() {
|
||||
Ok(max_string_length) => {
|
||||
flake8_errmsg.max_string_length = Some(max_string_length);
|
||||
}
|
||||
@@ -345,6 +351,9 @@ pub fn convert(
|
||||
if flake8_bugbear != flake8_bugbear::settings::Options::default() {
|
||||
options.flake8_bugbear = Some(flake8_bugbear);
|
||||
}
|
||||
if flake8_builtins != flake8_builtins::settings::Options::default() {
|
||||
options.flake8_builtins = Some(flake8_builtins);
|
||||
}
|
||||
if flake8_errmsg != flake8_errmsg::settings::Options::default() {
|
||||
options.flake8_errmsg = Some(flake8_errmsg);
|
||||
}
|
||||
@@ -392,7 +401,7 @@ mod tests {
|
||||
|
||||
use super::super::plugin::Plugin;
|
||||
use super::convert;
|
||||
use crate::registry::RuleCodePrefix;
|
||||
use crate::registry::RuleSelector;
|
||||
use crate::rules::pydocstyle::settings::Convention;
|
||||
use crate::rules::{flake8_quotes, pydocstyle};
|
||||
use crate::settings::options::Options;
|
||||
@@ -428,11 +437,7 @@ mod tests {
|
||||
per_file_ignores: None,
|
||||
required_version: None,
|
||||
respect_gitignore: None,
|
||||
select: Some(vec![
|
||||
RuleCodePrefix::E,
|
||||
RuleCodePrefix::F,
|
||||
RuleCodePrefix::W,
|
||||
]),
|
||||
select: Some(vec![RuleSelector::E, RuleSelector::F, RuleSelector::W]),
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
@@ -443,6 +448,7 @@ mod tests {
|
||||
flake8_annotations: None,
|
||||
flake8_bandit: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_builtins: None,
|
||||
flake8_errmsg: None,
|
||||
flake8_pytest_style: None,
|
||||
flake8_quotes: None,
|
||||
@@ -495,11 +501,7 @@ mod tests {
|
||||
per_file_ignores: None,
|
||||
required_version: None,
|
||||
respect_gitignore: None,
|
||||
select: Some(vec![
|
||||
RuleCodePrefix::E,
|
||||
RuleCodePrefix::F,
|
||||
RuleCodePrefix::W,
|
||||
]),
|
||||
select: Some(vec![RuleSelector::E, RuleSelector::F, RuleSelector::W]),
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
@@ -510,6 +512,7 @@ mod tests {
|
||||
flake8_annotations: None,
|
||||
flake8_bandit: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_builtins: None,
|
||||
flake8_errmsg: None,
|
||||
flake8_pytest_style: None,
|
||||
flake8_quotes: None,
|
||||
@@ -562,11 +565,7 @@ mod tests {
|
||||
per_file_ignores: None,
|
||||
required_version: None,
|
||||
respect_gitignore: None,
|
||||
select: Some(vec![
|
||||
RuleCodePrefix::E,
|
||||
RuleCodePrefix::F,
|
||||
RuleCodePrefix::W,
|
||||
]),
|
||||
select: Some(vec![RuleSelector::E, RuleSelector::F, RuleSelector::W]),
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
@@ -577,6 +576,7 @@ mod tests {
|
||||
flake8_annotations: None,
|
||||
flake8_bandit: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_builtins: None,
|
||||
flake8_errmsg: None,
|
||||
flake8_pytest_style: None,
|
||||
flake8_quotes: None,
|
||||
@@ -629,11 +629,7 @@ mod tests {
|
||||
per_file_ignores: None,
|
||||
required_version: None,
|
||||
respect_gitignore: None,
|
||||
select: Some(vec![
|
||||
RuleCodePrefix::E,
|
||||
RuleCodePrefix::F,
|
||||
RuleCodePrefix::W,
|
||||
]),
|
||||
select: Some(vec![RuleSelector::E, RuleSelector::F, RuleSelector::W]),
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
@@ -644,6 +640,7 @@ mod tests {
|
||||
flake8_annotations: None,
|
||||
flake8_bandit: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_builtins: None,
|
||||
flake8_errmsg: None,
|
||||
flake8_pytest_style: None,
|
||||
flake8_quotes: None,
|
||||
@@ -696,11 +693,7 @@ mod tests {
|
||||
per_file_ignores: None,
|
||||
required_version: None,
|
||||
respect_gitignore: None,
|
||||
select: Some(vec![
|
||||
RuleCodePrefix::E,
|
||||
RuleCodePrefix::F,
|
||||
RuleCodePrefix::W,
|
||||
]),
|
||||
select: Some(vec![RuleSelector::E, RuleSelector::F, RuleSelector::W]),
|
||||
show_source: None,
|
||||
src: None,
|
||||
target_version: None,
|
||||
@@ -711,6 +704,7 @@ mod tests {
|
||||
flake8_annotations: None,
|
||||
flake8_bandit: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_builtins: None,
|
||||
flake8_errmsg: None,
|
||||
flake8_pytest_style: None,
|
||||
flake8_quotes: Some(flake8_quotes::settings::Options {
|
||||
@@ -772,10 +766,10 @@ mod tests {
|
||||
required_version: None,
|
||||
respect_gitignore: None,
|
||||
select: Some(vec![
|
||||
RuleCodePrefix::D,
|
||||
RuleCodePrefix::E,
|
||||
RuleCodePrefix::F,
|
||||
RuleCodePrefix::W,
|
||||
RuleSelector::D,
|
||||
RuleSelector::E,
|
||||
RuleSelector::F,
|
||||
RuleSelector::W,
|
||||
]),
|
||||
show_source: None,
|
||||
src: None,
|
||||
@@ -787,6 +781,7 @@ mod tests {
|
||||
flake8_annotations: None,
|
||||
flake8_bandit: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_builtins: None,
|
||||
flake8_errmsg: None,
|
||||
flake8_pytest_style: None,
|
||||
flake8_quotes: None,
|
||||
@@ -842,10 +837,10 @@ mod tests {
|
||||
required_version: None,
|
||||
respect_gitignore: None,
|
||||
select: Some(vec![
|
||||
RuleCodePrefix::E,
|
||||
RuleCodePrefix::F,
|
||||
RuleCodePrefix::Q,
|
||||
RuleCodePrefix::W,
|
||||
RuleSelector::E,
|
||||
RuleSelector::F,
|
||||
RuleSelector::Q,
|
||||
RuleSelector::W,
|
||||
]),
|
||||
show_source: None,
|
||||
src: None,
|
||||
@@ -857,6 +852,7 @@ mod tests {
|
||||
flake8_annotations: None,
|
||||
flake8_bandit: None,
|
||||
flake8_bugbear: None,
|
||||
flake8_builtins: None,
|
||||
flake8_errmsg: None,
|
||||
flake8_pytest_style: None,
|
||||
flake8_quotes: Some(flake8_quotes::settings::Options {
|
||||
|
||||
@@ -6,16 +6,16 @@ use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::registry::{RuleCodePrefix, PREFIX_REDIRECTS};
|
||||
use crate::registry::{RuleSelector, PREFIX_REDIRECTS};
|
||||
use crate::settings::types::PatternPrefixPair;
|
||||
use crate::warn_user;
|
||||
|
||||
static COMMA_SEPARATED_LIST_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"[,\s]").unwrap());
|
||||
|
||||
/// Parse a comma-separated list of `RuleCodePrefix` values (e.g.,
|
||||
/// Parse a comma-separated list of `RuleSelector` values (e.g.,
|
||||
/// "F401,E501").
|
||||
pub fn parse_prefix_codes(value: &str) -> Vec<RuleCodePrefix> {
|
||||
let mut codes: Vec<RuleCodePrefix> = vec![];
|
||||
pub fn parse_prefix_codes(value: &str) -> Vec<RuleSelector> {
|
||||
let mut codes: Vec<RuleSelector> = vec![];
|
||||
for code in COMMA_SEPARATED_LIST_RE.split(value) {
|
||||
let code = code.trim();
|
||||
if code.is_empty() {
|
||||
@@ -23,7 +23,7 @@ pub fn parse_prefix_codes(value: &str) -> Vec<RuleCodePrefix> {
|
||||
}
|
||||
if let Some(code) = PREFIX_REDIRECTS.get(code) {
|
||||
codes.push(code.clone());
|
||||
} else if let Ok(code) = RuleCodePrefix::from_str(code) {
|
||||
} else if let Ok(code) = RuleSelector::from_str(code) {
|
||||
codes.push(code);
|
||||
} else {
|
||||
warn_user!("Unsupported prefix code: {code}");
|
||||
@@ -96,7 +96,7 @@ impl State {
|
||||
prefix: code.clone(),
|
||||
});
|
||||
}
|
||||
} else if let Ok(code) = RuleCodePrefix::from_str(code) {
|
||||
} else if let Ok(code) = RuleSelector::from_str(code) {
|
||||
for filename in &self.filenames {
|
||||
codes.push(PatternPrefixPair {
|
||||
pattern: filename.clone(),
|
||||
@@ -190,8 +190,8 @@ pub fn parse_files_to_codes_mapping(value: &str) -> Result<Vec<PatternPrefixPair
|
||||
/// Collect a list of `PatternPrefixPair` structs as a `BTreeMap`.
|
||||
pub fn collect_per_file_ignores(
|
||||
pairs: Vec<PatternPrefixPair>,
|
||||
) -> FxHashMap<String, Vec<RuleCodePrefix>> {
|
||||
let mut per_file_ignores: FxHashMap<String, Vec<RuleCodePrefix>> = FxHashMap::default();
|
||||
) -> FxHashMap<String, Vec<RuleSelector>> {
|
||||
let mut per_file_ignores: FxHashMap<String, Vec<RuleSelector>> = FxHashMap::default();
|
||||
for pair in pairs {
|
||||
per_file_ignores
|
||||
.entry(pair.pattern)
|
||||
@@ -206,33 +206,33 @@ mod tests {
|
||||
use anyhow::Result;
|
||||
|
||||
use super::{parse_files_to_codes_mapping, parse_prefix_codes, parse_strings};
|
||||
use crate::registry::RuleCodePrefix;
|
||||
use crate::registry::RuleSelector;
|
||||
use crate::settings::types::PatternPrefixPair;
|
||||
|
||||
#[test]
|
||||
fn it_parses_prefix_codes() {
|
||||
let actual = parse_prefix_codes("");
|
||||
let expected: Vec<RuleCodePrefix> = vec![];
|
||||
let expected: Vec<RuleSelector> = vec![];
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let actual = parse_prefix_codes(" ");
|
||||
let expected: Vec<RuleCodePrefix> = vec![];
|
||||
let expected: Vec<RuleSelector> = vec![];
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let actual = parse_prefix_codes("F401");
|
||||
let expected = vec![RuleCodePrefix::F401];
|
||||
let expected = vec![RuleSelector::F401];
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let actual = parse_prefix_codes("F401,");
|
||||
let expected = vec![RuleCodePrefix::F401];
|
||||
let expected = vec![RuleSelector::F401];
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let actual = parse_prefix_codes("F401,E501");
|
||||
let expected = vec![RuleCodePrefix::F401, RuleCodePrefix::E501];
|
||||
let expected = vec![RuleSelector::F401, RuleSelector::E501];
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let actual = parse_prefix_codes("F401, E501");
|
||||
let expected = vec![RuleCodePrefix::F401, RuleCodePrefix::E501];
|
||||
let expected = vec![RuleSelector::F401, RuleSelector::E501];
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
@@ -285,11 +285,11 @@ mod tests {
|
||||
let expected: Vec<PatternPrefixPair> = vec![
|
||||
PatternPrefixPair {
|
||||
pattern: "locust/test/*".to_string(),
|
||||
prefix: RuleCodePrefix::F841,
|
||||
prefix: RuleSelector::F841,
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "examples/*".to_string(),
|
||||
prefix: RuleCodePrefix::F841,
|
||||
prefix: RuleSelector::F841,
|
||||
},
|
||||
];
|
||||
assert_eq!(actual, expected);
|
||||
@@ -305,23 +305,23 @@ mod tests {
|
||||
let expected: Vec<PatternPrefixPair> = vec![
|
||||
PatternPrefixPair {
|
||||
pattern: "t/*".to_string(),
|
||||
prefix: RuleCodePrefix::D,
|
||||
prefix: RuleSelector::D,
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "setup.py".to_string(),
|
||||
prefix: RuleCodePrefix::D,
|
||||
prefix: RuleSelector::D,
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "examples/*".to_string(),
|
||||
prefix: RuleCodePrefix::D,
|
||||
prefix: RuleSelector::D,
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "docs/*".to_string(),
|
||||
prefix: RuleCodePrefix::D,
|
||||
prefix: RuleSelector::D,
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "extra/*".to_string(),
|
||||
prefix: RuleCodePrefix::D,
|
||||
prefix: RuleSelector::D,
|
||||
},
|
||||
];
|
||||
assert_eq!(actual, expected);
|
||||
@@ -343,47 +343,47 @@ mod tests {
|
||||
let expected: Vec<PatternPrefixPair> = vec![
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/__init__.py".to_string(),
|
||||
prefix: RuleCodePrefix::E402,
|
||||
prefix: RuleSelector::E402,
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/core/downloader/handlers/http.py".to_string(),
|
||||
prefix: RuleCodePrefix::F401,
|
||||
prefix: RuleSelector::F401,
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/http/__init__.py".to_string(),
|
||||
prefix: RuleCodePrefix::F401,
|
||||
prefix: RuleSelector::F401,
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/linkextractors/__init__.py".to_string(),
|
||||
prefix: RuleCodePrefix::E402,
|
||||
prefix: RuleSelector::E402,
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/linkextractors/__init__.py".to_string(),
|
||||
prefix: RuleCodePrefix::F401,
|
||||
prefix: RuleSelector::F401,
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/selector/__init__.py".to_string(),
|
||||
prefix: RuleCodePrefix::F401,
|
||||
prefix: RuleSelector::F401,
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/spiders/__init__.py".to_string(),
|
||||
prefix: RuleCodePrefix::E402,
|
||||
prefix: RuleSelector::E402,
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/spiders/__init__.py".to_string(),
|
||||
prefix: RuleCodePrefix::F401,
|
||||
prefix: RuleSelector::F401,
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/utils/url.py".to_string(),
|
||||
prefix: RuleCodePrefix::F403,
|
||||
prefix: RuleSelector::F403,
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/utils/url.py".to_string(),
|
||||
prefix: RuleCodePrefix::F405,
|
||||
prefix: RuleSelector::F405,
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "tests/test_loader.py".to_string(),
|
||||
prefix: RuleCodePrefix::E741,
|
||||
prefix: RuleSelector::E741,
|
||||
},
|
||||
];
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::str::FromStr;
|
||||
|
||||
use anyhow::anyhow;
|
||||
|
||||
use crate::registry::RuleCodePrefix;
|
||||
use crate::registry::RuleSelector;
|
||||
|
||||
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||
pub enum Plugin {
|
||||
@@ -98,32 +98,32 @@ impl fmt::Debug for Plugin {
|
||||
}
|
||||
|
||||
impl Plugin {
|
||||
pub fn prefix(&self) -> RuleCodePrefix {
|
||||
pub fn prefix(&self) -> RuleSelector {
|
||||
match self {
|
||||
Plugin::Flake8Annotations => RuleCodePrefix::ANN,
|
||||
Plugin::Flake8Bandit => RuleCodePrefix::S,
|
||||
Plugin::Flake8Annotations => RuleSelector::ANN,
|
||||
Plugin::Flake8Bandit => RuleSelector::S,
|
||||
// TODO(charlie): Handle rename of `B` to `BLE`.
|
||||
Plugin::Flake8BlindExcept => RuleCodePrefix::BLE,
|
||||
Plugin::Flake8Bugbear => RuleCodePrefix::B,
|
||||
Plugin::Flake8Builtins => RuleCodePrefix::A,
|
||||
Plugin::Flake8Comprehensions => RuleCodePrefix::C4,
|
||||
Plugin::Flake8Datetimez => RuleCodePrefix::DTZ,
|
||||
Plugin::Flake8Debugger => RuleCodePrefix::T1,
|
||||
Plugin::Flake8Docstrings => RuleCodePrefix::D,
|
||||
Plugin::Flake8BlindExcept => RuleSelector::BLE,
|
||||
Plugin::Flake8Bugbear => RuleSelector::B,
|
||||
Plugin::Flake8Builtins => RuleSelector::A,
|
||||
Plugin::Flake8Comprehensions => RuleSelector::C4,
|
||||
Plugin::Flake8Datetimez => RuleSelector::DTZ,
|
||||
Plugin::Flake8Debugger => RuleSelector::T1,
|
||||
Plugin::Flake8Docstrings => RuleSelector::D,
|
||||
// TODO(charlie): Handle rename of `E` to `ERA`.
|
||||
Plugin::Flake8Eradicate => RuleCodePrefix::ERA,
|
||||
Plugin::Flake8ErrMsg => RuleCodePrefix::EM,
|
||||
Plugin::Flake8ImplicitStrConcat => RuleCodePrefix::ISC,
|
||||
Plugin::Flake8Print => RuleCodePrefix::T2,
|
||||
Plugin::Flake8PytestStyle => RuleCodePrefix::PT,
|
||||
Plugin::Flake8Quotes => RuleCodePrefix::Q,
|
||||
Plugin::Flake8Return => RuleCodePrefix::RET,
|
||||
Plugin::Flake8Simplify => RuleCodePrefix::SIM,
|
||||
Plugin::Flake8TidyImports => RuleCodePrefix::TID25,
|
||||
Plugin::McCabe => RuleCodePrefix::C9,
|
||||
Plugin::PandasVet => RuleCodePrefix::PD,
|
||||
Plugin::PEP8Naming => RuleCodePrefix::N,
|
||||
Plugin::Pyupgrade => RuleCodePrefix::UP,
|
||||
Plugin::Flake8Eradicate => RuleSelector::ERA,
|
||||
Plugin::Flake8ErrMsg => RuleSelector::EM,
|
||||
Plugin::Flake8ImplicitStrConcat => RuleSelector::ISC,
|
||||
Plugin::Flake8Print => RuleSelector::T2,
|
||||
Plugin::Flake8PytestStyle => RuleSelector::PT,
|
||||
Plugin::Flake8Quotes => RuleSelector::Q,
|
||||
Plugin::Flake8Return => RuleSelector::RET,
|
||||
Plugin::Flake8Simplify => RuleSelector::SIM,
|
||||
Plugin::Flake8TidyImports => RuleSelector::TID25,
|
||||
Plugin::McCabe => RuleSelector::C9,
|
||||
Plugin::PandasVet => RuleSelector::PD,
|
||||
Plugin::PEP8Naming => RuleSelector::N,
|
||||
Plugin::Pyupgrade => RuleSelector::UP,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -249,7 +249,7 @@ pub fn infer_plugins_from_options(flake8: &HashMap<String, Option<String>>) -> V
|
||||
///
|
||||
/// For example, if the user ignores `ANN101`, we should infer that
|
||||
/// `flake8-annotations` is active.
|
||||
pub fn infer_plugins_from_codes(codes: &BTreeSet<RuleCodePrefix>) -> Vec<Plugin> {
|
||||
pub fn infer_plugins_from_codes(codes: &BTreeSet<RuleSelector>) -> Vec<Plugin> {
|
||||
[
|
||||
Plugin::Flake8Annotations,
|
||||
Plugin::Flake8Bandit,
|
||||
@@ -287,10 +287,10 @@ pub fn infer_plugins_from_codes(codes: &BTreeSet<RuleCodePrefix>) -> Vec<Plugin>
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Resolve the set of enabled `RuleCodePrefix` values for the given
|
||||
/// Resolve the set of enabled `RuleSelector` values for the given
|
||||
/// plugins.
|
||||
pub fn resolve_select(plugins: &[Plugin]) -> BTreeSet<RuleCodePrefix> {
|
||||
let mut select = BTreeSet::from([RuleCodePrefix::F, RuleCodePrefix::E, RuleCodePrefix::W]);
|
||||
pub fn resolve_select(plugins: &[Plugin]) -> BTreeSet<RuleSelector> {
|
||||
let mut select = BTreeSet::from([RuleSelector::F, RuleSelector::E, RuleSelector::W]);
|
||||
select.extend(plugins.iter().map(Plugin::prefix));
|
||||
select
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use anyhow::{anyhow, Result};
|
||||
use path_absolutize::{path_dedot, Absolutize};
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::registry::RuleCode;
|
||||
use crate::registry::Rule;
|
||||
use crate::settings::hashable::{HashableGlobMatcher, HashableHashSet};
|
||||
|
||||
/// Extract the absolute path and basename (as strings) from a Path.
|
||||
@@ -29,9 +29,9 @@ pub(crate) fn ignores_from_path<'a>(
|
||||
pattern_code_pairs: &'a [(
|
||||
HashableGlobMatcher,
|
||||
HashableGlobMatcher,
|
||||
HashableHashSet<RuleCode>,
|
||||
HashableHashSet<Rule>,
|
||||
)],
|
||||
) -> Result<FxHashSet<&'a RuleCode>> {
|
||||
) -> Result<FxHashSet<&'a Rule>> {
|
||||
let (file_path, file_basename) = extract_path_names(path)?;
|
||||
Ok(pattern_code_pairs
|
||||
.iter()
|
||||
|
||||
@@ -47,6 +47,7 @@ mod violations;
|
||||
mod visibility;
|
||||
|
||||
use cfg_if::cfg_if;
|
||||
pub use violation::{AutofixKind, Availability as AutofixAvailability};
|
||||
pub use violations::IOError;
|
||||
|
||||
cfg_if! {
|
||||
|
||||
@@ -7,11 +7,12 @@ use wasm_bindgen::prelude::*;
|
||||
|
||||
use crate::directives;
|
||||
use crate::linter::check_path;
|
||||
use crate::registry::RuleCode;
|
||||
use crate::registry::Rule;
|
||||
use crate::rules::{
|
||||
flake8_annotations, flake8_bandit, flake8_bugbear, flake8_errmsg, flake8_import_conventions,
|
||||
flake8_pytest_style, flake8_quotes, flake8_tidy_imports, flake8_unused_arguments, isort,
|
||||
mccabe, pep8_naming, pycodestyle, pydocstyle, pylint, pyupgrade,
|
||||
flake8_annotations, flake8_bandit, flake8_bugbear, flake8_builtins, flake8_errmsg,
|
||||
flake8_import_conventions, flake8_pytest_style, flake8_quotes, flake8_tidy_imports,
|
||||
flake8_unused_arguments, isort, mccabe, pep8_naming, pycodestyle, pydocstyle, pylint,
|
||||
pyupgrade,
|
||||
};
|
||||
use crate::rustpython_helpers::tokenize;
|
||||
use crate::settings::configuration::Configuration;
|
||||
@@ -51,13 +52,30 @@ export interface Diagnostic {
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ExpandedMessage {
|
||||
code: RuleCode,
|
||||
code: SerializeRuleAsCode,
|
||||
message: String,
|
||||
location: Location,
|
||||
end_location: Location,
|
||||
fix: Option<ExpandedFix>,
|
||||
}
|
||||
|
||||
struct SerializeRuleAsCode(Rule);
|
||||
|
||||
impl Serialize for SerializeRuleAsCode {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_str(self.0.code())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Rule> for SerializeRuleAsCode {
|
||||
fn from(rule: Rule) -> Self {
|
||||
Self(rule)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ExpandedFix {
|
||||
content: String,
|
||||
@@ -119,6 +137,7 @@ pub fn defaultSettings() -> Result<JsValue, JsValue> {
|
||||
flake8_annotations: Some(flake8_annotations::settings::Settings::default().into()),
|
||||
flake8_bandit: Some(flake8_bandit::settings::Settings::default().into()),
|
||||
flake8_bugbear: Some(flake8_bugbear::settings::Settings::default().into()),
|
||||
flake8_builtins: Some(flake8_builtins::settings::Settings::default().into()),
|
||||
flake8_errmsg: Some(flake8_errmsg::settings::Settings::default().into()),
|
||||
flake8_pytest_style: Some(flake8_pytest_style::settings::Settings::default().into()),
|
||||
flake8_quotes: Some(flake8_quotes::settings::Settings::default().into()),
|
||||
@@ -182,7 +201,7 @@ pub fn check(contents: &str, options: JsValue) -> Result<JsValue, JsValue> {
|
||||
let messages: Vec<ExpandedMessage> = diagnostics
|
||||
.into_iter()
|
||||
.map(|diagnostic| ExpandedMessage {
|
||||
code: diagnostic.kind.code().clone(),
|
||||
code: diagnostic.kind.rule().clone().into(),
|
||||
message: diagnostic.kind.body(),
|
||||
location: diagnostic.location,
|
||||
end_location: diagnostic.end_location,
|
||||
@@ -224,7 +243,7 @@ mod test {
|
||||
"if (1, 2): pass",
|
||||
r#"{}"#,
|
||||
[ExpandedMessage {
|
||||
code: RuleCode::F634,
|
||||
code: Rule::IfTuple.into(),
|
||||
message: "If test is a tuple, which is always `True`".to_string(),
|
||||
location: Location::new(1, 0),
|
||||
end_location: Location::new(1, 15),
|
||||
|
||||
@@ -16,7 +16,7 @@ use crate::directives::Directives;
|
||||
use crate::doc_lines::{doc_lines_from_ast, doc_lines_from_tokens};
|
||||
use crate::message::{Message, Source};
|
||||
use crate::noqa::add_noqa;
|
||||
use crate::registry::{Diagnostic, LintSource, RuleCode};
|
||||
use crate::registry::{Diagnostic, LintSource, Rule};
|
||||
use crate::settings::{flags, Settings};
|
||||
use crate::source_code::{Indexer, Locator, Stylist};
|
||||
use crate::{directives, fs, rustpython_helpers, violations};
|
||||
@@ -48,7 +48,7 @@ pub fn check_path(
|
||||
|
||||
// Collect doc lines. This requires a rare mix of tokens (for comments) and AST
|
||||
// (for docstrings), which demands special-casing at this level.
|
||||
let use_doc_lines = settings.rules.enabled(&RuleCode::W505);
|
||||
let use_doc_lines = settings.rules.enabled(&Rule::DocLineTooLong);
|
||||
let mut doc_lines = vec![];
|
||||
if use_doc_lines {
|
||||
doc_lines.extend(doc_lines_from_tokens(&tokens));
|
||||
@@ -116,7 +116,7 @@ pub fn check_path(
|
||||
}
|
||||
}
|
||||
Err(parse_error) => {
|
||||
if settings.rules.enabled(&RuleCode::E999) {
|
||||
if settings.rules.enabled(&Rule::SyntaxError) {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
violations::SyntaxError(parse_error.error.to_string()),
|
||||
Range::new(parse_error.location, parse_error.location),
|
||||
@@ -170,7 +170,7 @@ pub fn check_path(
|
||||
if !ignores.is_empty() {
|
||||
return Ok(diagnostics
|
||||
.into_iter()
|
||||
.filter(|diagnostic| !ignores.contains(&diagnostic.kind.code()))
|
||||
.filter(|diagnostic| !ignores.contains(&diagnostic.kind.rule()))
|
||||
.collect());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,13 +44,14 @@ macro_rules! notify_user {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialOrd, Ord, PartialEq, Eq)]
|
||||
#[derive(Debug, Default, PartialOrd, Ord, PartialEq, Eq)]
|
||||
pub enum LogLevel {
|
||||
// No output (+ `log::LevelFilter::Off`).
|
||||
Silent,
|
||||
// Only show lint violations, with no decorative output (+ `log::LevelFilter::Off`).
|
||||
Quiet,
|
||||
// All user-facing output (+ `log::LevelFilter::Info`).
|
||||
#[default]
|
||||
Default,
|
||||
// All user-facing output (+ `log::LevelFilter::Debug`).
|
||||
Verbose,
|
||||
@@ -67,12 +68,6 @@ impl LogLevel {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LogLevel {
|
||||
fn default() -> Self {
|
||||
LogLevel::Default
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_up_logging(level: &LogLevel) -> Result<()> {
|
||||
fern::Dispatch::new()
|
||||
.format(|out, message, record| {
|
||||
|
||||
24
src/noqa.rs
24
src/noqa.rs
@@ -8,7 +8,7 @@ use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
|
||||
use crate::registry::{Diagnostic, RuleCode, CODE_REDIRECTS};
|
||||
use crate::registry::{Diagnostic, Rule, CODE_REDIRECTS};
|
||||
use crate::settings::hashable::HashableHashSet;
|
||||
use crate::source_code::LineEnding;
|
||||
|
||||
@@ -69,11 +69,11 @@ pub fn extract_noqa_directive(line: &str) -> Directive {
|
||||
|
||||
/// Returns `true` if the string list of `codes` includes `code` (or an alias
|
||||
/// thereof).
|
||||
pub fn includes(needle: &RuleCode, haystack: &[&str]) -> bool {
|
||||
let needle: &str = needle.as_ref();
|
||||
pub fn includes(needle: &Rule, haystack: &[&str]) -> bool {
|
||||
let needle: &str = needle.code();
|
||||
haystack.iter().any(|candidate| {
|
||||
if let Some(candidate) = CODE_REDIRECTS.get(candidate) {
|
||||
needle == candidate.as_ref()
|
||||
needle == candidate.code()
|
||||
} else {
|
||||
&needle == candidate
|
||||
}
|
||||
@@ -101,21 +101,21 @@ fn add_noqa_inner(
|
||||
external: &HashableHashSet<String>,
|
||||
line_ending: &LineEnding,
|
||||
) -> (usize, String) {
|
||||
let mut matches_by_line: FxHashMap<usize, FxHashSet<&RuleCode>> = FxHashMap::default();
|
||||
let mut matches_by_line: FxHashMap<usize, FxHashSet<&Rule>> = FxHashMap::default();
|
||||
for (lineno, line) in contents.lines().enumerate() {
|
||||
// If we hit an exemption for the entire file, bail.
|
||||
if is_file_exempt(line) {
|
||||
return (0, contents.to_string());
|
||||
}
|
||||
|
||||
let mut codes: FxHashSet<&RuleCode> = FxHashSet::default();
|
||||
let mut codes: FxHashSet<&Rule> = FxHashSet::default();
|
||||
for diagnostic in diagnostics {
|
||||
// TODO(charlie): Consider respecting parent `noqa` directives. For now, we'll
|
||||
// add a `noqa` for every diagnostic, on its own line. This could lead to
|
||||
// duplication, whereby some parent `noqa` directives become
|
||||
// redundant.
|
||||
if diagnostic.location.row() == lineno + 1 {
|
||||
codes.insert(diagnostic.kind.code());
|
||||
codes.insert(diagnostic.kind.rule());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ fn add_noqa_inner(
|
||||
output.push_str(line);
|
||||
output.push_str(line_ending);
|
||||
}
|
||||
Some(codes) => {
|
||||
Some(rules) => {
|
||||
match extract_noqa_directive(line) {
|
||||
Directive::None => {
|
||||
// Add existing content.
|
||||
@@ -148,7 +148,7 @@ fn add_noqa_inner(
|
||||
output.push_str(" # noqa: ");
|
||||
|
||||
// Add codes.
|
||||
let codes: Vec<&str> = codes.iter().map(AsRef::as_ref).collect();
|
||||
let codes: Vec<&str> = rules.iter().map(|r| r.code()).collect();
|
||||
let suffix = codes.join(", ");
|
||||
output.push_str(&suffix);
|
||||
output.push_str(line_ending);
|
||||
@@ -163,7 +163,7 @@ fn add_noqa_inner(
|
||||
|
||||
// Add codes.
|
||||
let codes: Vec<&str> =
|
||||
codes.iter().map(AsRef::as_ref).sorted_unstable().collect();
|
||||
rules.iter().map(|r| r.code()).sorted_unstable().collect();
|
||||
let suffix = codes.join(", ");
|
||||
output.push_str(&suffix);
|
||||
output.push_str(line_ending);
|
||||
@@ -181,9 +181,9 @@ fn add_noqa_inner(
|
||||
formatted.push_str(" # noqa: ");
|
||||
|
||||
// Add codes.
|
||||
let codes: Vec<&str> = codes
|
||||
let codes: Vec<&str> = rules
|
||||
.iter()
|
||||
.map(AsRef::as_ref)
|
||||
.map(|r| r.code())
|
||||
.chain(existing.into_iter().filter(|code| external.contains(*code)))
|
||||
.sorted_unstable()
|
||||
.collect();
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
/// Returns `true` if a string is a valid Python identifier (e.g., variable
|
||||
/// name).
|
||||
pub fn is_identifier(s: &str) -> bool {
|
||||
// Is the first character a letter or underscore?
|
||||
if !s
|
||||
.chars()
|
||||
.next()
|
||||
.map_or(false, |c| c.is_alphabetic() || c == '_')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pub static IDENTIFIER_REGEX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"^[A-Za-z_][A-Za-z0-9_]*$").unwrap());
|
||||
// Are the rest of the characters letters, digits, or underscores?
|
||||
s.chars().skip(1).all(|c| c.is_alphanumeric() || c == '_')
|
||||
}
|
||||
|
||||
379
src/registry.rs
379
src/registry.rs
@@ -1,12 +1,12 @@
|
||||
//! Registry of [`RuleCode`] to [`DiagnosticKind`] mappings.
|
||||
//! Registry of [`Rule`] to [`DiagnosticKind`] mappings.
|
||||
|
||||
use itertools::Itertools;
|
||||
use once_cell::sync::Lazy;
|
||||
use ruff_macros::RuleCodePrefix;
|
||||
use ruff_macros::ParseCode;
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustpython_parser::ast::Location;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum_macros::{AsRefStr, Display, EnumIter, EnumString};
|
||||
use strum_macros::{AsRefStr, EnumIter};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::fix::Fix;
|
||||
@@ -15,6 +15,7 @@ use crate::{rules, violations};
|
||||
|
||||
ruff_macros::define_rule_mapping!(
|
||||
// pycodestyle errors
|
||||
E101 => violations::MixedSpacesAndTabs,
|
||||
E401 => violations::MultipleImportsOnOneLine,
|
||||
E402 => violations::ModuleImportNotAtTopOfFile,
|
||||
E501 => violations::LineTooLong,
|
||||
@@ -251,8 +252,10 @@ ruff_macros::define_rule_mapping!(
|
||||
UP028 => violations::RewriteYieldFrom,
|
||||
UP029 => violations::UnnecessaryBuiltinImport,
|
||||
UP030 => violations::FormatLiterals,
|
||||
UP031 => violations::PrintfStringFormatting,
|
||||
UP032 => violations::FString,
|
||||
UP033 => violations::FunctoolsCache,
|
||||
UP033 => violations::FunctoolsCache,
|
||||
UP034 => violations::ExtraneousParentheses,
|
||||
// pydocstyle
|
||||
D100 => violations::PublicModule,
|
||||
D101 => violations::PublicClass,
|
||||
@@ -281,6 +284,7 @@ ruff_macros::define_rule_mapping!(
|
||||
D300 => violations::UsesTripleQuotes,
|
||||
D301 => violations::UsesRPrefixForBackslashedContent,
|
||||
D400 => violations::EndsInPeriod,
|
||||
D401 => crate::rules::pydocstyle::rules::non_imperative_mood::NonImperativeMood,
|
||||
D402 => violations::NoSignature,
|
||||
D403 => violations::FirstLineCapitalized,
|
||||
D404 => violations::NoThisPrefix,
|
||||
@@ -417,56 +421,111 @@ ruff_macros::define_rule_mapping!(
|
||||
COM819 => violations::TrailingCommaProhibited,
|
||||
// flake8-no-pep420
|
||||
INP001 => violations::ImplicitNamespacePackage,
|
||||
// flake8-executable
|
||||
EXE003 => rules::flake8_executable::rules::ShebangPython,
|
||||
EXE004 => rules::flake8_executable::rules::ShebangWhitespace,
|
||||
EXE005 => rules::flake8_executable::rules::ShebangNewline,
|
||||
// flake8-type-checking
|
||||
TYP005 => rules::flake8_type_checking::rules::EmptyTypeCheckingBlock,
|
||||
// tryceratops
|
||||
TRY300 => rules::tryceratops::rules::TryConsiderElse,
|
||||
// Ruff
|
||||
RUF001 => violations::AmbiguousUnicodeCharacterString,
|
||||
RUF002 => violations::AmbiguousUnicodeCharacterDocstring,
|
||||
RUF003 => violations::AmbiguousUnicodeCharacterComment,
|
||||
RUF004 => violations::KeywordArgumentBeforeStarArgument,
|
||||
RUF005 => violations::UnpackInsteadOfConcatenatingToCollectionLiteral,
|
||||
RUF100 => violations::UnusedNOQA,
|
||||
);
|
||||
|
||||
#[derive(EnumIter, Debug, PartialEq, Eq)]
|
||||
pub enum RuleOrigin {
|
||||
#[derive(EnumIter, Debug, PartialEq, Eq, ParseCode)]
|
||||
pub enum Linter {
|
||||
#[prefix = "F"]
|
||||
Pyflakes,
|
||||
#[prefix = "E"]
|
||||
#[prefix = "W"]
|
||||
Pycodestyle,
|
||||
#[prefix = "C9"]
|
||||
McCabe,
|
||||
#[prefix = "I"]
|
||||
Isort,
|
||||
#[prefix = "D"]
|
||||
Pydocstyle,
|
||||
#[prefix = "UP"]
|
||||
Pyupgrade,
|
||||
#[prefix = "N"]
|
||||
PEP8Naming,
|
||||
#[prefix = "YTT"]
|
||||
Flake82020,
|
||||
#[prefix = "ANN"]
|
||||
Flake8Annotations,
|
||||
#[prefix = "S"]
|
||||
Flake8Bandit,
|
||||
#[prefix = "BLE"]
|
||||
Flake8BlindExcept,
|
||||
#[prefix = "FBT"]
|
||||
Flake8BooleanTrap,
|
||||
#[prefix = "B"]
|
||||
Flake8Bugbear,
|
||||
#[prefix = "A"]
|
||||
Flake8Builtins,
|
||||
#[prefix = "C4"]
|
||||
Flake8Comprehensions,
|
||||
#[prefix = "T10"]
|
||||
Flake8Debugger,
|
||||
#[prefix = "EM"]
|
||||
Flake8ErrMsg,
|
||||
#[prefix = "ISC"]
|
||||
Flake8ImplicitStrConcat,
|
||||
#[prefix = "ICN"]
|
||||
Flake8ImportConventions,
|
||||
#[prefix = "T20"]
|
||||
Flake8Print,
|
||||
#[prefix = "PT"]
|
||||
Flake8PytestStyle,
|
||||
#[prefix = "Q"]
|
||||
Flake8Quotes,
|
||||
#[prefix = "RET"]
|
||||
Flake8Return,
|
||||
#[prefix = "SIM"]
|
||||
Flake8Simplify,
|
||||
#[prefix = "TID"]
|
||||
Flake8TidyImports,
|
||||
#[prefix = "ARG"]
|
||||
Flake8UnusedArguments,
|
||||
#[prefix = "DTZ"]
|
||||
Flake8Datetimez,
|
||||
#[prefix = "ERA"]
|
||||
Eradicate,
|
||||
#[prefix = "PD"]
|
||||
PandasVet,
|
||||
#[prefix = "PGH"]
|
||||
PygrepHooks,
|
||||
#[prefix = "PL"]
|
||||
Pylint,
|
||||
#[prefix = "PIE"]
|
||||
Flake8Pie,
|
||||
#[prefix = "COM"]
|
||||
Flake8Commas,
|
||||
#[prefix = "INP"]
|
||||
Flake8NoPep420,
|
||||
#[prefix = "EXE"]
|
||||
Flake8Executable,
|
||||
#[prefix = "TYP"]
|
||||
Flake8TypeChecking,
|
||||
#[prefix = "TRY"]
|
||||
Tryceratops,
|
||||
#[prefix = "RUF"]
|
||||
Ruff,
|
||||
}
|
||||
|
||||
pub trait ParseCode: Sized {
|
||||
fn parse_code(code: &str) -> Option<(Self, &str)>;
|
||||
}
|
||||
|
||||
pub enum Prefixes {
|
||||
Single(RuleCodePrefix),
|
||||
Multiple(Vec<(RuleCodePrefix, &'static str)>),
|
||||
Single(RuleSelector),
|
||||
Multiple(Vec<(RuleSelector, &'static str)>),
|
||||
}
|
||||
|
||||
impl Prefixes {
|
||||
@@ -481,54 +540,57 @@ impl Prefixes {
|
||||
}
|
||||
}
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/origin.rs"));
|
||||
include!(concat!(env!("OUT_DIR"), "/linter.rs"));
|
||||
|
||||
impl RuleOrigin {
|
||||
impl Linter {
|
||||
pub fn prefixes(&self) -> Prefixes {
|
||||
match self {
|
||||
RuleOrigin::Eradicate => Prefixes::Single(RuleCodePrefix::ERA),
|
||||
RuleOrigin::Flake82020 => Prefixes::Single(RuleCodePrefix::YTT),
|
||||
RuleOrigin::Flake8Annotations => Prefixes::Single(RuleCodePrefix::ANN),
|
||||
RuleOrigin::Flake8Bandit => Prefixes::Single(RuleCodePrefix::S),
|
||||
RuleOrigin::Flake8BlindExcept => Prefixes::Single(RuleCodePrefix::BLE),
|
||||
RuleOrigin::Flake8BooleanTrap => Prefixes::Single(RuleCodePrefix::FBT),
|
||||
RuleOrigin::Flake8Bugbear => Prefixes::Single(RuleCodePrefix::B),
|
||||
RuleOrigin::Flake8Builtins => Prefixes::Single(RuleCodePrefix::A),
|
||||
RuleOrigin::Flake8Comprehensions => Prefixes::Single(RuleCodePrefix::C4),
|
||||
RuleOrigin::Flake8Datetimez => Prefixes::Single(RuleCodePrefix::DTZ),
|
||||
RuleOrigin::Flake8Debugger => Prefixes::Single(RuleCodePrefix::T10),
|
||||
RuleOrigin::Flake8ErrMsg => Prefixes::Single(RuleCodePrefix::EM),
|
||||
RuleOrigin::Flake8ImplicitStrConcat => Prefixes::Single(RuleCodePrefix::ISC),
|
||||
RuleOrigin::Flake8ImportConventions => Prefixes::Single(RuleCodePrefix::ICN),
|
||||
RuleOrigin::Flake8Print => Prefixes::Single(RuleCodePrefix::T20),
|
||||
RuleOrigin::Flake8PytestStyle => Prefixes::Single(RuleCodePrefix::PT),
|
||||
RuleOrigin::Flake8Quotes => Prefixes::Single(RuleCodePrefix::Q),
|
||||
RuleOrigin::Flake8Return => Prefixes::Single(RuleCodePrefix::RET),
|
||||
RuleOrigin::Flake8Simplify => Prefixes::Single(RuleCodePrefix::SIM),
|
||||
RuleOrigin::Flake8TidyImports => Prefixes::Single(RuleCodePrefix::TID),
|
||||
RuleOrigin::Flake8UnusedArguments => Prefixes::Single(RuleCodePrefix::ARG),
|
||||
RuleOrigin::Isort => Prefixes::Single(RuleCodePrefix::I),
|
||||
RuleOrigin::McCabe => Prefixes::Single(RuleCodePrefix::C90),
|
||||
RuleOrigin::PEP8Naming => Prefixes::Single(RuleCodePrefix::N),
|
||||
RuleOrigin::PandasVet => Prefixes::Single(RuleCodePrefix::PD),
|
||||
RuleOrigin::Pycodestyle => Prefixes::Multiple(vec![
|
||||
(RuleCodePrefix::E, "Error"),
|
||||
(RuleCodePrefix::W, "Warning"),
|
||||
Linter::Eradicate => Prefixes::Single(RuleSelector::ERA),
|
||||
Linter::Flake82020 => Prefixes::Single(RuleSelector::YTT),
|
||||
Linter::Flake8Annotations => Prefixes::Single(RuleSelector::ANN),
|
||||
Linter::Flake8Bandit => Prefixes::Single(RuleSelector::S),
|
||||
Linter::Flake8BlindExcept => Prefixes::Single(RuleSelector::BLE),
|
||||
Linter::Flake8BooleanTrap => Prefixes::Single(RuleSelector::FBT),
|
||||
Linter::Flake8Bugbear => Prefixes::Single(RuleSelector::B),
|
||||
Linter::Flake8Builtins => Prefixes::Single(RuleSelector::A),
|
||||
Linter::Flake8Comprehensions => Prefixes::Single(RuleSelector::C4),
|
||||
Linter::Flake8Datetimez => Prefixes::Single(RuleSelector::DTZ),
|
||||
Linter::Flake8Debugger => Prefixes::Single(RuleSelector::T10),
|
||||
Linter::Flake8ErrMsg => Prefixes::Single(RuleSelector::EM),
|
||||
Linter::Flake8ImplicitStrConcat => Prefixes::Single(RuleSelector::ISC),
|
||||
Linter::Flake8ImportConventions => Prefixes::Single(RuleSelector::ICN),
|
||||
Linter::Flake8Print => Prefixes::Single(RuleSelector::T20),
|
||||
Linter::Flake8PytestStyle => Prefixes::Single(RuleSelector::PT),
|
||||
Linter::Flake8Quotes => Prefixes::Single(RuleSelector::Q),
|
||||
Linter::Flake8Return => Prefixes::Single(RuleSelector::RET),
|
||||
Linter::Flake8Simplify => Prefixes::Single(RuleSelector::SIM),
|
||||
Linter::Flake8TidyImports => Prefixes::Single(RuleSelector::TID),
|
||||
Linter::Flake8UnusedArguments => Prefixes::Single(RuleSelector::ARG),
|
||||
Linter::Isort => Prefixes::Single(RuleSelector::I),
|
||||
Linter::McCabe => Prefixes::Single(RuleSelector::C90),
|
||||
Linter::PEP8Naming => Prefixes::Single(RuleSelector::N),
|
||||
Linter::PandasVet => Prefixes::Single(RuleSelector::PD),
|
||||
Linter::Pycodestyle => Prefixes::Multiple(vec![
|
||||
(RuleSelector::E, "Error"),
|
||||
(RuleSelector::W, "Warning"),
|
||||
]),
|
||||
RuleOrigin::Pydocstyle => Prefixes::Single(RuleCodePrefix::D),
|
||||
RuleOrigin::Pyflakes => Prefixes::Single(RuleCodePrefix::F),
|
||||
RuleOrigin::PygrepHooks => Prefixes::Single(RuleCodePrefix::PGH),
|
||||
RuleOrigin::Pylint => Prefixes::Multiple(vec![
|
||||
(RuleCodePrefix::PLC, "Convention"),
|
||||
(RuleCodePrefix::PLE, "Error"),
|
||||
(RuleCodePrefix::PLR, "Refactor"),
|
||||
(RuleCodePrefix::PLW, "Warning"),
|
||||
Linter::Pydocstyle => Prefixes::Single(RuleSelector::D),
|
||||
Linter::Pyflakes => Prefixes::Single(RuleSelector::F),
|
||||
Linter::PygrepHooks => Prefixes::Single(RuleSelector::PGH),
|
||||
Linter::Pylint => Prefixes::Multiple(vec![
|
||||
(RuleSelector::PLC, "Convention"),
|
||||
(RuleSelector::PLE, "Error"),
|
||||
(RuleSelector::PLR, "Refactor"),
|
||||
(RuleSelector::PLW, "Warning"),
|
||||
]),
|
||||
RuleOrigin::Pyupgrade => Prefixes::Single(RuleCodePrefix::UP),
|
||||
RuleOrigin::Flake8Pie => Prefixes::Single(RuleCodePrefix::PIE),
|
||||
RuleOrigin::Flake8Commas => Prefixes::Single(RuleCodePrefix::COM),
|
||||
RuleOrigin::Flake8NoPep420 => Prefixes::Single(RuleCodePrefix::INP),
|
||||
RuleOrigin::Ruff => Prefixes::Single(RuleCodePrefix::RUF),
|
||||
Linter::Pyupgrade => Prefixes::Single(RuleSelector::UP),
|
||||
Linter::Flake8Pie => Prefixes::Single(RuleSelector::PIE),
|
||||
Linter::Flake8Commas => Prefixes::Single(RuleSelector::COM),
|
||||
Linter::Flake8NoPep420 => Prefixes::Single(RuleSelector::INP),
|
||||
Linter::Flake8Executable => Prefixes::Single(RuleSelector::EXE),
|
||||
Linter::Flake8TypeChecking => Prefixes::Single(RuleSelector::TYP),
|
||||
Linter::Tryceratops => Prefixes::Single(RuleSelector::TRY),
|
||||
Linter::Ruff => Prefixes::Single(RuleSelector::RUF),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -543,81 +605,45 @@ pub enum LintSource {
|
||||
Filesystem,
|
||||
}
|
||||
|
||||
impl RuleCode {
|
||||
impl Rule {
|
||||
/// The source for the diagnostic (either the AST, the filesystem, or the
|
||||
/// physical lines).
|
||||
pub fn lint_source(&self) -> &'static LintSource {
|
||||
match self {
|
||||
RuleCode::RUF100 => &LintSource::NoQa,
|
||||
RuleCode::E501
|
||||
| RuleCode::W292
|
||||
| RuleCode::W505
|
||||
| RuleCode::UP009
|
||||
| RuleCode::PGH003
|
||||
| RuleCode::PGH004 => &LintSource::Lines,
|
||||
RuleCode::ERA001
|
||||
| RuleCode::ISC001
|
||||
| RuleCode::ISC002
|
||||
| RuleCode::Q000
|
||||
| RuleCode::Q001
|
||||
| RuleCode::Q002
|
||||
| RuleCode::Q003
|
||||
| RuleCode::W605
|
||||
| RuleCode::COM812
|
||||
| RuleCode::COM818
|
||||
| RuleCode::COM819
|
||||
| RuleCode::RUF001
|
||||
| RuleCode::RUF002
|
||||
| RuleCode::RUF003 => &LintSource::Tokens,
|
||||
RuleCode::E902 => &LintSource::Io,
|
||||
RuleCode::I001 | RuleCode::I002 => &LintSource::Imports,
|
||||
RuleCode::INP001 => &LintSource::Filesystem,
|
||||
Rule::UnusedNOQA => &LintSource::NoQa,
|
||||
Rule::BlanketNOQA
|
||||
| Rule::BlanketTypeIgnore
|
||||
| Rule::DocLineTooLong
|
||||
| Rule::LineTooLong
|
||||
| Rule::MixedSpacesAndTabs
|
||||
| Rule::NoNewLineAtEndOfFile
|
||||
| Rule::PEP3120UnnecessaryCodingComment
|
||||
| Rule::ShebangNewline
|
||||
| Rule::ShebangPython
|
||||
| Rule::ShebangWhitespace => &LintSource::Lines,
|
||||
Rule::AmbiguousUnicodeCharacterComment
|
||||
| Rule::AmbiguousUnicodeCharacterDocstring
|
||||
| Rule::AmbiguousUnicodeCharacterString
|
||||
| Rule::AvoidQuoteEscape
|
||||
| Rule::BadQuotesDocstring
|
||||
| Rule::BadQuotesInlineString
|
||||
| Rule::BadQuotesMultilineString
|
||||
| Rule::CommentedOutCode
|
||||
| Rule::ExtraneousParentheses
|
||||
| Rule::InvalidEscapeSequence
|
||||
| Rule::MultiLineImplicitStringConcatenation
|
||||
| Rule::SingleLineImplicitStringConcatenation
|
||||
| Rule::TrailingCommaMissing
|
||||
| Rule::TrailingCommaOnBareTupleProhibited
|
||||
| Rule::TrailingCommaProhibited => &LintSource::Tokens,
|
||||
Rule::IOError => &LintSource::Io,
|
||||
Rule::UnsortedImports | Rule::MissingRequiredImport => &LintSource::Imports,
|
||||
Rule::ImplicitNamespacePackage => &LintSource::Filesystem,
|
||||
_ => &LintSource::Ast,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DiagnosticKind {
|
||||
/// The summary text for the diagnostic. Typically a truncated form of the
|
||||
/// body text.
|
||||
pub fn summary(&self) -> String {
|
||||
match self {
|
||||
DiagnosticKind::UnaryPrefixIncrement(..) => {
|
||||
"Python does not support the unary prefix increment".to_string()
|
||||
}
|
||||
DiagnosticKind::UnusedLoopControlVariable(violations::UnusedLoopControlVariable(
|
||||
name,
|
||||
)) => {
|
||||
format!("Loop control variable `{name}` not used within the loop body")
|
||||
}
|
||||
DiagnosticKind::NoAssertRaisesException(..) => {
|
||||
"`assertRaises(Exception)` should be considered evil".to_string()
|
||||
}
|
||||
DiagnosticKind::StarArgUnpackingAfterKeywordArg(..) => {
|
||||
"Star-arg unpacking after a keyword argument is strongly discouraged".to_string()
|
||||
}
|
||||
|
||||
// flake8-datetimez
|
||||
DiagnosticKind::CallDatetimeToday(..) => {
|
||||
"The use of `datetime.datetime.today()` is not allowed".to_string()
|
||||
}
|
||||
DiagnosticKind::CallDatetimeUtcnow(..) => {
|
||||
"The use of `datetime.datetime.utcnow()` is not allowed".to_string()
|
||||
}
|
||||
DiagnosticKind::CallDatetimeUtcfromtimestamp(..) => {
|
||||
"The use of `datetime.datetime.utcfromtimestamp()` is not allowed".to_string()
|
||||
}
|
||||
DiagnosticKind::CallDateToday(..) => {
|
||||
"The use of `datetime.date.today()` is not allowed.".to_string()
|
||||
}
|
||||
DiagnosticKind::CallDateFromtimestamp(..) => {
|
||||
"The use of `datetime.date.fromtimestamp()` is not allowed".to_string()
|
||||
}
|
||||
_ => self.body(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Diagnostic {
|
||||
pub kind: DiagnosticKind,
|
||||
@@ -650,95 +676,88 @@ impl Diagnostic {
|
||||
}
|
||||
|
||||
/// Pairs of checks that shouldn't be enabled together.
|
||||
pub const INCOMPATIBLE_CODES: &[(RuleCode, RuleCode, &str)] = &[(
|
||||
RuleCode::D203,
|
||||
RuleCode::D211,
|
||||
pub const INCOMPATIBLE_CODES: &[(Rule, Rule, &str)] = &[(
|
||||
Rule::OneBlankLineBeforeClass,
|
||||
Rule::NoBlankLineBeforeClass,
|
||||
"`D203` (OneBlankLineBeforeClass) and `D211` (NoBlankLinesBeforeClass) are incompatible. \
|
||||
Consider adding `D203` to `ignore`.",
|
||||
)];
|
||||
|
||||
/// A hash map from deprecated to latest `RuleCode`.
|
||||
pub static CODE_REDIRECTS: Lazy<FxHashMap<&'static str, RuleCode>> = Lazy::new(|| {
|
||||
/// A hash map from deprecated to latest `Rule`.
|
||||
pub static CODE_REDIRECTS: Lazy<FxHashMap<&'static str, Rule>> = Lazy::new(|| {
|
||||
FxHashMap::from_iter([
|
||||
// TODO(charlie): Remove by 2023-01-01.
|
||||
("U001", RuleCode::UP001),
|
||||
("U003", RuleCode::UP003),
|
||||
("U004", RuleCode::UP004),
|
||||
("U005", RuleCode::UP005),
|
||||
("U006", RuleCode::UP006),
|
||||
("U007", RuleCode::UP007),
|
||||
("U008", RuleCode::UP008),
|
||||
("U009", RuleCode::UP009),
|
||||
("U010", RuleCode::UP010),
|
||||
("U011", RuleCode::UP011),
|
||||
("U012", RuleCode::UP012),
|
||||
("U013", RuleCode::UP013),
|
||||
("U014", RuleCode::UP014),
|
||||
("U015", RuleCode::UP015),
|
||||
("U016", RuleCode::UP016),
|
||||
("U017", RuleCode::UP017),
|
||||
("U019", RuleCode::UP019),
|
||||
("U001", Rule::UselessMetaclassType),
|
||||
("U003", Rule::TypeOfPrimitive),
|
||||
("U004", Rule::UselessObjectInheritance),
|
||||
("U005", Rule::DeprecatedUnittestAlias),
|
||||
("U006", Rule::UsePEP585Annotation),
|
||||
("U007", Rule::UsePEP604Annotation),
|
||||
("U008", Rule::SuperCallWithParameters),
|
||||
("U009", Rule::PEP3120UnnecessaryCodingComment),
|
||||
("U010", Rule::UnnecessaryFutureImport),
|
||||
("U011", Rule::LRUCacheWithoutParameters),
|
||||
("U012", Rule::UnnecessaryEncodeUTF8),
|
||||
("U013", Rule::ConvertTypedDictFunctionalToClass),
|
||||
("U014", Rule::ConvertNamedTupleFunctionalToClass),
|
||||
("U015", Rule::RedundantOpenModes),
|
||||
("U016", Rule::RemoveSixCompat),
|
||||
("U017", Rule::DatetimeTimezoneUTC),
|
||||
("U019", Rule::TypingTextStrAlias),
|
||||
// TODO(charlie): Remove by 2023-02-01.
|
||||
("I252", RuleCode::TID252),
|
||||
("M001", RuleCode::RUF100),
|
||||
("I252", Rule::RelativeImports),
|
||||
("M001", Rule::UnusedNOQA),
|
||||
// TODO(charlie): Remove by 2023-02-01.
|
||||
("PDV002", RuleCode::PD002),
|
||||
("PDV003", RuleCode::PD003),
|
||||
("PDV004", RuleCode::PD004),
|
||||
("PDV007", RuleCode::PD007),
|
||||
("PDV008", RuleCode::PD008),
|
||||
("PDV009", RuleCode::PD009),
|
||||
("PDV010", RuleCode::PD010),
|
||||
("PDV011", RuleCode::PD011),
|
||||
("PDV012", RuleCode::PD012),
|
||||
("PDV013", RuleCode::PD013),
|
||||
("PDV015", RuleCode::PD015),
|
||||
("PDV901", RuleCode::PD901),
|
||||
("PDV002", Rule::UseOfInplaceArgument),
|
||||
("PDV003", Rule::UseOfDotIsNull),
|
||||
("PDV004", Rule::UseOfDotNotNull),
|
||||
("PDV007", Rule::UseOfDotIx),
|
||||
("PDV008", Rule::UseOfDotAt),
|
||||
("PDV009", Rule::UseOfDotIat),
|
||||
("PDV010", Rule::UseOfDotPivotOrUnstack),
|
||||
("PDV011", Rule::UseOfDotValues),
|
||||
("PDV012", Rule::UseOfDotReadTable),
|
||||
("PDV013", Rule::UseOfDotStack),
|
||||
("PDV015", Rule::UseOfPdMerge),
|
||||
("PDV901", Rule::DfIsABadVariableName),
|
||||
// TODO(charlie): Remove by 2023-02-01.
|
||||
("R501", RuleCode::RET501),
|
||||
("R502", RuleCode::RET502),
|
||||
("R503", RuleCode::RET503),
|
||||
("R504", RuleCode::RET504),
|
||||
("R505", RuleCode::RET505),
|
||||
("R506", RuleCode::RET506),
|
||||
("R507", RuleCode::RET507),
|
||||
("R508", RuleCode::RET508),
|
||||
("R501", Rule::UnnecessaryReturnNone),
|
||||
("R502", Rule::ImplicitReturnValue),
|
||||
("R503", Rule::ImplicitReturn),
|
||||
("R504", Rule::UnnecessaryAssign),
|
||||
("R505", Rule::SuperfluousElseReturn),
|
||||
("R506", Rule::SuperfluousElseRaise),
|
||||
("R507", Rule::SuperfluousElseContinue),
|
||||
("R508", Rule::SuperfluousElseBreak),
|
||||
// TODO(charlie): Remove by 2023-02-01.
|
||||
("IC001", RuleCode::ICN001),
|
||||
("IC002", RuleCode::ICN001),
|
||||
("IC003", RuleCode::ICN001),
|
||||
("IC004", RuleCode::ICN001),
|
||||
("IC001", Rule::ImportAliasIsNotConventional),
|
||||
("IC002", Rule::ImportAliasIsNotConventional),
|
||||
("IC003", Rule::ImportAliasIsNotConventional),
|
||||
("IC004", Rule::ImportAliasIsNotConventional),
|
||||
])
|
||||
});
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::registry::RuleCode;
|
||||
use super::{Linter, ParseCode, Rule};
|
||||
|
||||
#[test]
|
||||
fn check_code_serialization() {
|
||||
for check_code in RuleCode::iter() {
|
||||
for rule in Rule::iter() {
|
||||
assert!(
|
||||
RuleCode::from_str(check_code.as_ref()).is_ok(),
|
||||
"{check_code:?} could not be round-trip serialized."
|
||||
Rule::from_code(rule.code()).is_ok(),
|
||||
"{rule:?} could not be round-trip serialized."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fixable_codes() {
|
||||
for check_code in RuleCode::iter() {
|
||||
let kind = check_code.kind();
|
||||
if kind.fixable() {
|
||||
assert!(
|
||||
kind.commit().is_some(),
|
||||
"{check_code:?} is fixable but has no commit message."
|
||||
);
|
||||
}
|
||||
fn test_linter_prefixes() {
|
||||
for rule in Rule::iter() {
|
||||
Linter::parse_code(rule.code())
|
||||
.unwrap_or_else(|| panic!("couldn't parse {:?}", rule.code()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,19 +4,18 @@ pub(crate) mod rules;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::convert::AsRef;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::linter::test_path;
|
||||
use crate::registry::RuleCode;
|
||||
use crate::registry::Rule;
|
||||
use crate::settings;
|
||||
|
||||
#[test_case(RuleCode::ERA001, Path::new("ERA001.py"); "ERA001")]
|
||||
fn rules(rule_code: RuleCode, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.as_ref(), path.to_string_lossy());
|
||||
#[test_case(Rule::CommentedOutCode, Path::new("ERA001.py"); "ERA001")]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
Path::new("./resources/test/fixtures/eradicate")
|
||||
.join(path)
|
||||
|
||||
@@ -3,7 +3,7 @@ use rustpython_ast::Location;
|
||||
use super::detection::comment_contains_code;
|
||||
use crate::ast::types::Range;
|
||||
use crate::fix::Fix;
|
||||
use crate::registry::{Diagnostic, RuleCode};
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::settings::{flags, Settings};
|
||||
use crate::source_code::Locator;
|
||||
use crate::violations;
|
||||
@@ -35,7 +35,7 @@ pub fn commented_out_code(
|
||||
if is_standalone_comment(&line) && comment_contains_code(&line, &settings.task_tags[..]) {
|
||||
let mut diagnostic = Diagnostic::new(violations::CommentedOutCode, Range::new(start, end));
|
||||
if matches!(autofix, flags::Autofix::Enabled)
|
||||
&& settings.rules.should_fix(&RuleCode::ERA001)
|
||||
&& settings.rules.should_fix(&Rule::CommentedOutCode)
|
||||
{
|
||||
diagnostic.amend(Fix::deletion(location, end_location));
|
||||
}
|
||||
|
||||
@@ -3,28 +3,27 @@ pub(crate) mod rules;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::convert::AsRef;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::linter::test_path;
|
||||
use crate::registry::RuleCode;
|
||||
use crate::registry::Rule;
|
||||
use crate::settings;
|
||||
|
||||
#[test_case(RuleCode::YTT101, Path::new("YTT101.py"); "YTT101")]
|
||||
#[test_case(RuleCode::YTT102, Path::new("YTT102.py"); "YTT102")]
|
||||
#[test_case(RuleCode::YTT103, Path::new("YTT103.py"); "YTT103")]
|
||||
#[test_case(RuleCode::YTT201, Path::new("YTT201.py"); "YTT201")]
|
||||
#[test_case(RuleCode::YTT202, Path::new("YTT202.py"); "YTT202")]
|
||||
#[test_case(RuleCode::YTT203, Path::new("YTT203.py"); "YTT203")]
|
||||
#[test_case(RuleCode::YTT204, Path::new("YTT204.py"); "YTT204")]
|
||||
#[test_case(RuleCode::YTT301, Path::new("YTT301.py"); "YTT301")]
|
||||
#[test_case(RuleCode::YTT302, Path::new("YTT302.py"); "YTT302")]
|
||||
#[test_case(RuleCode::YTT303, Path::new("YTT303.py"); "YTT303")]
|
||||
fn rules(rule_code: RuleCode, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.as_ref(), path.to_string_lossy());
|
||||
#[test_case(Rule::SysVersionSlice3Referenced, Path::new("YTT101.py"); "YTT101")]
|
||||
#[test_case(Rule::SysVersion2Referenced, Path::new("YTT102.py"); "YTT102")]
|
||||
#[test_case(Rule::SysVersionCmpStr3, Path::new("YTT103.py"); "YTT103")]
|
||||
#[test_case(Rule::SysVersionInfo0Eq3Referenced, Path::new("YTT201.py"); "YTT201")]
|
||||
#[test_case(Rule::SixPY3Referenced, Path::new("YTT202.py"); "YTT202")]
|
||||
#[test_case(Rule::SysVersionInfo1CmpInt, Path::new("YTT203.py"); "YTT203")]
|
||||
#[test_case(Rule::SysVersionInfoMinorCmpInt, Path::new("YTT204.py"); "YTT204")]
|
||||
#[test_case(Rule::SysVersion0Referenced, Path::new("YTT301.py"); "YTT301")]
|
||||
#[test_case(Rule::SysVersionCmpStr10, Path::new("YTT302.py"); "YTT302")]
|
||||
#[test_case(Rule::SysVersionSlice1Referenced, Path::new("YTT303.py"); "YTT303")]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_2020")
|
||||
.join(path)
|
||||
|
||||
@@ -3,7 +3,7 @@ use rustpython_ast::{Cmpop, Constant, Expr, ExprKind, Located};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::{Diagnostic, RuleCode};
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::violations;
|
||||
|
||||
fn is_sys(checker: &Checker, expr: &Expr, target: &str) -> bool {
|
||||
@@ -27,13 +27,21 @@ pub fn subscript(checker: &mut Checker, value: &Expr, slice: &Expr) {
|
||||
..
|
||||
} = &upper.node
|
||||
{
|
||||
if *i == BigInt::from(1) && checker.settings.rules.enabled(&RuleCode::YTT303) {
|
||||
if *i == BigInt::from(1)
|
||||
&& checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::SysVersionSlice1Referenced)
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::SysVersionSlice1Referenced,
|
||||
Range::from_located(value),
|
||||
));
|
||||
} else if *i == BigInt::from(3)
|
||||
&& checker.settings.rules.enabled(&RuleCode::YTT101)
|
||||
&& checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::SysVersionSlice3Referenced)
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::SysVersionSlice3Referenced,
|
||||
@@ -47,12 +55,15 @@ pub fn subscript(checker: &mut Checker, value: &Expr, slice: &Expr) {
|
||||
value: Constant::Int(i),
|
||||
..
|
||||
} => {
|
||||
if *i == BigInt::from(2) && checker.settings.rules.enabled(&RuleCode::YTT102) {
|
||||
if *i == BigInt::from(2)
|
||||
&& checker.settings.rules.enabled(&Rule::SysVersion2Referenced)
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::SysVersion2Referenced,
|
||||
Range::from_located(value),
|
||||
));
|
||||
} else if *i == BigInt::from(0) && checker.settings.rules.enabled(&RuleCode::YTT301)
|
||||
} else if *i == BigInt::from(0)
|
||||
&& checker.settings.rules.enabled(&Rule::SysVersion0Referenced)
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::SysVersion0Referenced,
|
||||
@@ -89,7 +100,10 @@ pub fn compare(checker: &mut Checker, left: &Expr, ops: &[Cmpop], comparators: &
|
||||
) = (ops, comparators)
|
||||
{
|
||||
if *n == BigInt::from(3)
|
||||
&& checker.settings.rules.enabled(&RuleCode::YTT201)
|
||||
&& checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::SysVersionInfo0Eq3Referenced)
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::SysVersionInfo0Eq3Referenced,
|
||||
@@ -110,7 +124,7 @@ pub fn compare(checker: &mut Checker, left: &Expr, ops: &[Cmpop], comparators: &
|
||||
}],
|
||||
) = (ops, comparators)
|
||||
{
|
||||
if checker.settings.rules.enabled(&RuleCode::YTT203) {
|
||||
if checker.settings.rules.enabled(&Rule::SysVersionInfo1CmpInt) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::SysVersionInfo1CmpInt,
|
||||
Range::from_located(left),
|
||||
@@ -136,7 +150,11 @@ pub fn compare(checker: &mut Checker, left: &Expr, ops: &[Cmpop], comparators: &
|
||||
}],
|
||||
) = (ops, comparators)
|
||||
{
|
||||
if checker.settings.rules.enabled(&RuleCode::YTT204) {
|
||||
if checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::SysVersionInfoMinorCmpInt)
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::SysVersionInfoMinorCmpInt,
|
||||
Range::from_located(left),
|
||||
@@ -162,13 +180,13 @@ pub fn compare(checker: &mut Checker, left: &Expr, ops: &[Cmpop], comparators: &
|
||||
) = (ops, comparators)
|
||||
{
|
||||
if s.len() == 1 {
|
||||
if checker.settings.rules.enabled(&RuleCode::YTT302) {
|
||||
if checker.settings.rules.enabled(&Rule::SysVersionCmpStr10) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::SysVersionCmpStr10,
|
||||
Range::from_located(left),
|
||||
));
|
||||
}
|
||||
} else if checker.settings.rules.enabled(&RuleCode::YTT103) {
|
||||
} else if checker.settings.rules.enabled(&Rule::SysVersionCmpStr3) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::SysVersionCmpStr3,
|
||||
Range::from_located(left),
|
||||
|
||||
@@ -11,7 +11,7 @@ mod tests {
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::linter::test_path;
|
||||
use crate::registry::RuleCode;
|
||||
use crate::registry::Rule;
|
||||
use crate::settings::Settings;
|
||||
|
||||
#[test]
|
||||
@@ -20,17 +20,17 @@ mod tests {
|
||||
Path::new("./resources/test/fixtures/flake8_annotations/annotation_presence.py"),
|
||||
&Settings {
|
||||
..Settings::for_rules(vec![
|
||||
RuleCode::ANN001,
|
||||
RuleCode::ANN002,
|
||||
RuleCode::ANN003,
|
||||
RuleCode::ANN101,
|
||||
RuleCode::ANN102,
|
||||
RuleCode::ANN201,
|
||||
RuleCode::ANN202,
|
||||
RuleCode::ANN204,
|
||||
RuleCode::ANN205,
|
||||
RuleCode::ANN206,
|
||||
RuleCode::ANN401,
|
||||
Rule::MissingTypeFunctionArgument,
|
||||
Rule::MissingTypeArgs,
|
||||
Rule::MissingTypeKwargs,
|
||||
Rule::MissingTypeSelf,
|
||||
Rule::MissingTypeCls,
|
||||
Rule::MissingReturnTypePublicFunction,
|
||||
Rule::MissingReturnTypePrivateFunction,
|
||||
Rule::MissingReturnTypeSpecialMethod,
|
||||
Rule::MissingReturnTypeStaticMethod,
|
||||
Rule::MissingReturnTypeClassMethod,
|
||||
Rule::DynamicallyTypedExpression,
|
||||
])
|
||||
},
|
||||
)?;
|
||||
@@ -50,11 +50,11 @@ mod tests {
|
||||
allow_star_arg_any: false,
|
||||
},
|
||||
..Settings::for_rules(vec![
|
||||
RuleCode::ANN001,
|
||||
RuleCode::ANN002,
|
||||
RuleCode::ANN003,
|
||||
RuleCode::ANN101,
|
||||
RuleCode::ANN102,
|
||||
Rule::MissingTypeFunctionArgument,
|
||||
Rule::MissingTypeArgs,
|
||||
Rule::MissingTypeKwargs,
|
||||
Rule::MissingTypeSelf,
|
||||
Rule::MissingTypeCls,
|
||||
])
|
||||
},
|
||||
)?;
|
||||
@@ -74,11 +74,11 @@ mod tests {
|
||||
allow_star_arg_any: false,
|
||||
},
|
||||
..Settings::for_rules(vec![
|
||||
RuleCode::ANN201,
|
||||
RuleCode::ANN202,
|
||||
RuleCode::ANN204,
|
||||
RuleCode::ANN205,
|
||||
RuleCode::ANN206,
|
||||
Rule::MissingReturnTypePublicFunction,
|
||||
Rule::MissingReturnTypePrivateFunction,
|
||||
Rule::MissingReturnTypeSpecialMethod,
|
||||
Rule::MissingReturnTypeStaticMethod,
|
||||
Rule::MissingReturnTypeClassMethod,
|
||||
])
|
||||
},
|
||||
)?;
|
||||
@@ -98,11 +98,11 @@ mod tests {
|
||||
allow_star_arg_any: false,
|
||||
},
|
||||
..Settings::for_rules(vec![
|
||||
RuleCode::ANN201,
|
||||
RuleCode::ANN202,
|
||||
RuleCode::ANN204,
|
||||
RuleCode::ANN205,
|
||||
RuleCode::ANN206,
|
||||
Rule::MissingReturnTypePublicFunction,
|
||||
Rule::MissingReturnTypePrivateFunction,
|
||||
Rule::MissingReturnTypeSpecialMethod,
|
||||
Rule::MissingReturnTypeStaticMethod,
|
||||
Rule::MissingReturnTypeClassMethod,
|
||||
])
|
||||
},
|
||||
)?;
|
||||
@@ -121,7 +121,7 @@ mod tests {
|
||||
suppress_none_returning: false,
|
||||
allow_star_arg_any: true,
|
||||
},
|
||||
..Settings::for_rules(vec![RuleCode::ANN401])
|
||||
..Settings::for_rules(vec![Rule::DynamicallyTypedExpression])
|
||||
},
|
||||
)?;
|
||||
insta::assert_yaml_snapshot!(diagnostics);
|
||||
@@ -134,11 +134,11 @@ mod tests {
|
||||
Path::new("./resources/test/fixtures/flake8_annotations/allow_overload.py"),
|
||||
&Settings {
|
||||
..Settings::for_rules(vec![
|
||||
RuleCode::ANN201,
|
||||
RuleCode::ANN202,
|
||||
RuleCode::ANN204,
|
||||
RuleCode::ANN205,
|
||||
RuleCode::ANN206,
|
||||
Rule::MissingReturnTypePublicFunction,
|
||||
Rule::MissingReturnTypePrivateFunction,
|
||||
Rule::MissingReturnTypeSpecialMethod,
|
||||
Rule::MissingReturnTypeStaticMethod,
|
||||
Rule::MissingReturnTypeClassMethod,
|
||||
])
|
||||
},
|
||||
)?;
|
||||
@@ -152,11 +152,11 @@ mod tests {
|
||||
Path::new("./resources/test/fixtures/flake8_annotations/allow_nested_overload.py"),
|
||||
&Settings {
|
||||
..Settings::for_rules(vec![
|
||||
RuleCode::ANN201,
|
||||
RuleCode::ANN202,
|
||||
RuleCode::ANN204,
|
||||
RuleCode::ANN205,
|
||||
RuleCode::ANN206,
|
||||
Rule::MissingReturnTypePublicFunction,
|
||||
Rule::MissingReturnTypePrivateFunction,
|
||||
Rule::MissingReturnTypeSpecialMethod,
|
||||
Rule::MissingReturnTypeStaticMethod,
|
||||
Rule::MissingReturnTypeClassMethod,
|
||||
])
|
||||
},
|
||||
)?;
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::ast::visitor::Visitor;
|
||||
use crate::ast::{cast, helpers, visitor};
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::docstrings::definition::{Definition, DefinitionKind};
|
||||
use crate::registry::{Diagnostic, RuleCode};
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::visibility::Visibility;
|
||||
use crate::{violations, visibility};
|
||||
|
||||
@@ -85,14 +85,22 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
.chain(args.kwonlyargs.iter())
|
||||
{
|
||||
if let Some(expr) = &arg.node.annotation {
|
||||
if checker.settings.rules.enabled(&RuleCode::ANN401) {
|
||||
if checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::DynamicallyTypedExpression)
|
||||
{
|
||||
check_dynamically_typed(checker, expr, || arg.node.arg.to_string());
|
||||
};
|
||||
} else {
|
||||
if !(checker.settings.flake8_annotations.suppress_dummy_args
|
||||
&& checker.settings.dummy_variable_rgx.is_match(&arg.node.arg))
|
||||
{
|
||||
if checker.settings.rules.enabled(&RuleCode::ANN001) {
|
||||
if checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::MissingTypeFunctionArgument)
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::MissingTypeFunctionArgument(arg.node.arg.to_string()),
|
||||
Range::from_located(arg),
|
||||
@@ -106,8 +114,12 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
if let Some(arg) = &args.vararg {
|
||||
if let Some(expr) = &arg.node.annotation {
|
||||
if !checker.settings.flake8_annotations.allow_star_arg_any {
|
||||
if checker.settings.rules.enabled(&RuleCode::ANN401) {
|
||||
let name = arg.node.arg.to_string();
|
||||
if checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::DynamicallyTypedExpression)
|
||||
{
|
||||
let name = &arg.node.arg;
|
||||
check_dynamically_typed(checker, expr, || format!("*{name}"));
|
||||
}
|
||||
}
|
||||
@@ -115,7 +127,7 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
if !(checker.settings.flake8_annotations.suppress_dummy_args
|
||||
&& checker.settings.dummy_variable_rgx.is_match(&arg.node.arg))
|
||||
{
|
||||
if checker.settings.rules.enabled(&RuleCode::ANN002) {
|
||||
if checker.settings.rules.enabled(&Rule::MissingTypeArgs) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::MissingTypeArgs(arg.node.arg.to_string()),
|
||||
Range::from_located(arg),
|
||||
@@ -129,8 +141,12 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
if let Some(arg) = &args.kwarg {
|
||||
if let Some(expr) = &arg.node.annotation {
|
||||
if !checker.settings.flake8_annotations.allow_star_arg_any {
|
||||
if checker.settings.rules.enabled(&RuleCode::ANN401) {
|
||||
let name = arg.node.arg.to_string();
|
||||
if checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::DynamicallyTypedExpression)
|
||||
{
|
||||
let name = &arg.node.arg;
|
||||
check_dynamically_typed(checker, expr, || format!("**{name}"));
|
||||
}
|
||||
}
|
||||
@@ -138,7 +154,7 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
if !(checker.settings.flake8_annotations.suppress_dummy_args
|
||||
&& checker.settings.dummy_variable_rgx.is_match(&arg.node.arg))
|
||||
{
|
||||
if checker.settings.rules.enabled(&RuleCode::ANN003) {
|
||||
if checker.settings.rules.enabled(&Rule::MissingTypeKwargs) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::MissingTypeKwargs(arg.node.arg.to_string()),
|
||||
Range::from_located(arg),
|
||||
@@ -150,7 +166,11 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
|
||||
// ANN201, ANN202, ANN401
|
||||
if let Some(expr) = &returns {
|
||||
if checker.settings.rules.enabled(&RuleCode::ANN401) {
|
||||
if checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::DynamicallyTypedExpression)
|
||||
{
|
||||
check_dynamically_typed(checker, expr, || name.to_string());
|
||||
};
|
||||
} else {
|
||||
@@ -164,7 +184,11 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
|
||||
match visibility {
|
||||
Visibility::Public => {
|
||||
if checker.settings.rules.enabled(&RuleCode::ANN201) {
|
||||
if checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::MissingReturnTypePublicFunction)
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::MissingReturnTypePublicFunction(name.to_string()),
|
||||
helpers::identifier_range(stmt, checker.locator),
|
||||
@@ -172,7 +196,11 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
}
|
||||
}
|
||||
Visibility::Private => {
|
||||
if checker.settings.rules.enabled(&RuleCode::ANN202) {
|
||||
if checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::MissingReturnTypePrivateFunction)
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::MissingReturnTypePrivateFunction(name.to_string()),
|
||||
helpers::identifier_range(stmt, checker.locator),
|
||||
@@ -203,14 +231,22 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
// ANN401 for dynamically typed arguments
|
||||
if let Some(annotation) = &arg.node.annotation {
|
||||
has_any_typed_arg = true;
|
||||
if checker.settings.rules.enabled(&RuleCode::ANN401) {
|
||||
if checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::DynamicallyTypedExpression)
|
||||
{
|
||||
check_dynamically_typed(checker, annotation, || arg.node.arg.to_string());
|
||||
}
|
||||
} else {
|
||||
if !(checker.settings.flake8_annotations.suppress_dummy_args
|
||||
&& checker.settings.dummy_variable_rgx.is_match(&arg.node.arg))
|
||||
{
|
||||
if checker.settings.rules.enabled(&RuleCode::ANN001) {
|
||||
if checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::MissingTypeFunctionArgument)
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::MissingTypeFunctionArgument(arg.node.arg.to_string()),
|
||||
Range::from_located(arg),
|
||||
@@ -225,8 +261,12 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
has_any_typed_arg = true;
|
||||
if let Some(expr) = &arg.node.annotation {
|
||||
if !checker.settings.flake8_annotations.allow_star_arg_any {
|
||||
if checker.settings.rules.enabled(&RuleCode::ANN401) {
|
||||
let name = arg.node.arg.to_string();
|
||||
if checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::DynamicallyTypedExpression)
|
||||
{
|
||||
let name = &arg.node.arg;
|
||||
check_dynamically_typed(checker, expr, || format!("*{name}"));
|
||||
}
|
||||
}
|
||||
@@ -234,7 +274,7 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
if !(checker.settings.flake8_annotations.suppress_dummy_args
|
||||
&& checker.settings.dummy_variable_rgx.is_match(&arg.node.arg))
|
||||
{
|
||||
if checker.settings.rules.enabled(&RuleCode::ANN002) {
|
||||
if checker.settings.rules.enabled(&Rule::MissingTypeArgs) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::MissingTypeArgs(arg.node.arg.to_string()),
|
||||
Range::from_located(arg),
|
||||
@@ -249,8 +289,12 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
has_any_typed_arg = true;
|
||||
if let Some(expr) = &arg.node.annotation {
|
||||
if !checker.settings.flake8_annotations.allow_star_arg_any {
|
||||
if checker.settings.rules.enabled(&RuleCode::ANN401) {
|
||||
let name = arg.node.arg.to_string();
|
||||
if checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::DynamicallyTypedExpression)
|
||||
{
|
||||
let name = &arg.node.arg;
|
||||
check_dynamically_typed(checker, expr, || format!("**{name}"));
|
||||
}
|
||||
}
|
||||
@@ -258,7 +302,7 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
if !(checker.settings.flake8_annotations.suppress_dummy_args
|
||||
&& checker.settings.dummy_variable_rgx.is_match(&arg.node.arg))
|
||||
{
|
||||
if checker.settings.rules.enabled(&RuleCode::ANN003) {
|
||||
if checker.settings.rules.enabled(&Rule::MissingTypeKwargs) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::MissingTypeKwargs(arg.node.arg.to_string()),
|
||||
Range::from_located(arg),
|
||||
@@ -273,14 +317,14 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
if let Some(arg) = args.args.first() {
|
||||
if arg.node.annotation.is_none() {
|
||||
if visibility::is_classmethod(checker, cast::decorator_list(stmt)) {
|
||||
if checker.settings.rules.enabled(&RuleCode::ANN102) {
|
||||
if checker.settings.rules.enabled(&Rule::MissingTypeCls) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::MissingTypeCls(arg.node.arg.to_string()),
|
||||
Range::from_located(arg),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
if checker.settings.rules.enabled(&RuleCode::ANN101) {
|
||||
if checker.settings.rules.enabled(&Rule::MissingTypeSelf) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::MissingTypeSelf(arg.node.arg.to_string()),
|
||||
Range::from_located(arg),
|
||||
@@ -293,7 +337,11 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
|
||||
// ANN201, ANN202
|
||||
if let Some(expr) = &returns {
|
||||
if checker.settings.rules.enabled(&RuleCode::ANN401) {
|
||||
if checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::DynamicallyTypedExpression)
|
||||
{
|
||||
check_dynamically_typed(checker, expr, || name.to_string());
|
||||
}
|
||||
} else {
|
||||
@@ -306,14 +354,22 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
}
|
||||
|
||||
if visibility::is_classmethod(checker, cast::decorator_list(stmt)) {
|
||||
if checker.settings.rules.enabled(&RuleCode::ANN206) {
|
||||
if checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::MissingReturnTypeClassMethod)
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::MissingReturnTypeClassMethod(name.to_string()),
|
||||
helpers::identifier_range(stmt, checker.locator),
|
||||
));
|
||||
}
|
||||
} else if visibility::is_staticmethod(checker, cast::decorator_list(stmt)) {
|
||||
if checker.settings.rules.enabled(&RuleCode::ANN205) {
|
||||
if checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::MissingReturnTypeStaticMethod)
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::MissingReturnTypeStaticMethod(name.to_string()),
|
||||
helpers::identifier_range(stmt, checker.locator),
|
||||
@@ -322,7 +378,11 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
} else if visibility::is_init(cast::name(stmt)) {
|
||||
// Allow omission of return annotation in `__init__` functions, as long as at
|
||||
// least one argument is typed.
|
||||
if checker.settings.rules.enabled(&RuleCode::ANN204) {
|
||||
if checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::MissingReturnTypeSpecialMethod)
|
||||
{
|
||||
if !(checker.settings.flake8_annotations.mypy_init_return
|
||||
&& has_any_typed_arg)
|
||||
{
|
||||
@@ -330,7 +390,7 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
violations::MissingReturnTypeSpecialMethod(name.to_string()),
|
||||
helpers::identifier_range(stmt, checker.locator),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.code()) {
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
match fixes::add_return_none_annotation(checker.locator, stmt) {
|
||||
Ok(fix) => {
|
||||
diagnostic.amend(fix);
|
||||
@@ -342,7 +402,11 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
}
|
||||
}
|
||||
} else if visibility::is_magic(cast::name(stmt)) {
|
||||
if checker.settings.rules.enabled(&RuleCode::ANN204) {
|
||||
if checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::MissingReturnTypeSpecialMethod)
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::MissingReturnTypeSpecialMethod(name.to_string()),
|
||||
helpers::identifier_range(stmt, checker.locator),
|
||||
@@ -351,7 +415,11 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
} else {
|
||||
match visibility {
|
||||
Visibility::Public => {
|
||||
if checker.settings.rules.enabled(&RuleCode::ANN201) {
|
||||
if checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::MissingReturnTypePublicFunction)
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::MissingReturnTypePublicFunction(name.to_string()),
|
||||
helpers::identifier_range(stmt, checker.locator),
|
||||
@@ -359,7 +427,11 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
|
||||
}
|
||||
}
|
||||
Visibility::Private => {
|
||||
if checker.settings.rules.enabled(&RuleCode::ANN202) {
|
||||
if checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::MissingReturnTypePrivateFunction)
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::MissingReturnTypePrivateFunction(name.to_string()),
|
||||
helpers::identifier_range(stmt, checker.locator),
|
||||
|
||||
@@ -11,26 +11,26 @@ mod tests {
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::linter::test_path;
|
||||
use crate::registry::RuleCode;
|
||||
use crate::registry::Rule;
|
||||
use crate::settings::Settings;
|
||||
|
||||
#[test_case(RuleCode::S101, Path::new("S101.py"); "S101")]
|
||||
#[test_case(RuleCode::S102, Path::new("S102.py"); "S102")]
|
||||
#[test_case(RuleCode::S103, Path::new("S103.py"); "S103")]
|
||||
#[test_case(RuleCode::S104, Path::new("S104.py"); "S104")]
|
||||
#[test_case(RuleCode::S105, Path::new("S105.py"); "S105")]
|
||||
#[test_case(RuleCode::S106, Path::new("S106.py"); "S106")]
|
||||
#[test_case(RuleCode::S107, Path::new("S107.py"); "S107")]
|
||||
#[test_case(RuleCode::S108, Path::new("S108.py"); "S108")]
|
||||
#[test_case(RuleCode::S113, Path::new("S113.py"); "S113")]
|
||||
#[test_case(RuleCode::S324, Path::new("S324.py"); "S324")]
|
||||
#[test_case(RuleCode::S501, Path::new("S501.py"); "S501")]
|
||||
#[test_case(RuleCode::S506, Path::new("S506.py"); "S506")]
|
||||
#[test_case(RuleCode::S508, Path::new("S508.py"); "S508")]
|
||||
#[test_case(RuleCode::S509, Path::new("S509.py"); "S509")]
|
||||
#[test_case(RuleCode::S701, Path::new("S701.py"); "S701")]
|
||||
fn rules(rule_code: RuleCode, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.as_ref(), path.to_string_lossy());
|
||||
#[test_case(Rule::AssertUsed, Path::new("S101.py"); "S101")]
|
||||
#[test_case(Rule::ExecUsed, Path::new("S102.py"); "S102")]
|
||||
#[test_case(Rule::BadFilePermissions, Path::new("S103.py"); "S103")]
|
||||
#[test_case(Rule::HardcodedBindAllInterfaces, Path::new("S104.py"); "S104")]
|
||||
#[test_case(Rule::HardcodedPasswordString, Path::new("S105.py"); "S105")]
|
||||
#[test_case(Rule::HardcodedPasswordFuncArg, Path::new("S106.py"); "S106")]
|
||||
#[test_case(Rule::HardcodedPasswordDefault, Path::new("S107.py"); "S107")]
|
||||
#[test_case(Rule::HardcodedTempFile, Path::new("S108.py"); "S108")]
|
||||
#[test_case(Rule::RequestWithoutTimeout, Path::new("S113.py"); "S113")]
|
||||
#[test_case(Rule::HashlibInsecureHashFunction, Path::new("S324.py"); "S324")]
|
||||
#[test_case(Rule::RequestWithNoCertValidation, Path::new("S501.py"); "S501")]
|
||||
#[test_case(Rule::UnsafeYAMLLoad, Path::new("S506.py"); "S506")]
|
||||
#[test_case(Rule::SnmpInsecureVersion, Path::new("S508.py"); "S508")]
|
||||
#[test_case(Rule::SnmpWeakCryptography, Path::new("S509.py"); "S509")]
|
||||
#[test_case(Rule::Jinja2AutoescapeFalse, Path::new("S701.py"); "S701")]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_bandit")
|
||||
.join(path)
|
||||
@@ -54,7 +54,7 @@ mod tests {
|
||||
"/foo".to_string(),
|
||||
],
|
||||
},
|
||||
..Settings::for_rule(RuleCode::S108)
|
||||
..Settings::for_rule(Rule::HardcodedTempFile)
|
||||
},
|
||||
)?;
|
||||
insta::assert_yaml_snapshot!("S108_extend", diagnostics);
|
||||
|
||||
@@ -6,5 +6,8 @@ use crate::violations;
|
||||
|
||||
/// S101
|
||||
pub fn assert_used(stmt: &Located<StmtKind>) -> Diagnostic {
|
||||
Diagnostic::new(violations::AssertUsed, Range::from_located(stmt))
|
||||
Diagnostic::new(
|
||||
violations::AssertUsed,
|
||||
Range::new(stmt.location, stmt.location.with_col_offset("assert".len())),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ expression: diagnostics
|
||||
column: 0
|
||||
end_location:
|
||||
row: 2
|
||||
column: 11
|
||||
column: 6
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
@@ -19,7 +19,7 @@ expression: diagnostics
|
||||
column: 4
|
||||
end_location:
|
||||
row: 8
|
||||
column: 17
|
||||
column: 10
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
@@ -29,7 +29,7 @@ expression: diagnostics
|
||||
column: 4
|
||||
end_location:
|
||||
row: 11
|
||||
column: 17
|
||||
column: 10
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
|
||||
@@ -3,19 +3,18 @@ pub(crate) mod rules;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::convert::AsRef;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::linter::test_path;
|
||||
use crate::registry::RuleCode;
|
||||
use crate::registry::Rule;
|
||||
use crate::settings;
|
||||
|
||||
#[test_case(RuleCode::BLE001, Path::new("BLE.py"); "BLE001")]
|
||||
fn rules(rule_code: RuleCode, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.as_ref(), path.to_string_lossy());
|
||||
#[test_case(Rule::BlindExcept, Path::new("BLE.py"); "BLE001")]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_blind_except")
|
||||
.join(path)
|
||||
|
||||
@@ -3,21 +3,20 @@ pub(crate) mod rules;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::convert::AsRef;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::linter::test_path;
|
||||
use crate::registry::RuleCode;
|
||||
use crate::registry::Rule;
|
||||
use crate::settings;
|
||||
|
||||
#[test_case(RuleCode::FBT001, Path::new("FBT.py"); "FBT001")]
|
||||
#[test_case(RuleCode::FBT002, Path::new("FBT.py"); "FBT002")]
|
||||
#[test_case(RuleCode::FBT003, Path::new("FBT.py"); "FBT003")]
|
||||
fn rules(rule_code: RuleCode, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.as_ref(), path.to_string_lossy());
|
||||
#[test_case(Rule::BooleanPositionalArgInFunctionDefinition, Path::new("FBT.py"); "FBT001")]
|
||||
#[test_case(Rule::BooleanDefaultValueInFunctionDefinition, Path::new("FBT.py"); "FBT002")]
|
||||
#[test_case(Rule::BooleanPositionalValueInFunctionCall, Path::new("FBT.py"); "FBT003")]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_boolean_trap")
|
||||
.join(path)
|
||||
|
||||
@@ -10,39 +10,39 @@ mod tests {
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::linter::test_path;
|
||||
use crate::registry::RuleCode;
|
||||
use crate::registry::Rule;
|
||||
use crate::settings::Settings;
|
||||
|
||||
#[test_case(RuleCode::B002, Path::new("B002.py"); "B002")]
|
||||
#[test_case(RuleCode::B003, Path::new("B003.py"); "B003")]
|
||||
#[test_case(RuleCode::B004, Path::new("B004.py"); "B004")]
|
||||
#[test_case(RuleCode::B005, Path::new("B005.py"); "B005")]
|
||||
#[test_case(RuleCode::B006, Path::new("B006_B008.py"); "B006")]
|
||||
#[test_case(RuleCode::B007, Path::new("B007.py"); "B007")]
|
||||
#[test_case(RuleCode::B008, Path::new("B006_B008.py"); "B008")]
|
||||
#[test_case(RuleCode::B009, Path::new("B009_B010.py"); "B009")]
|
||||
#[test_case(RuleCode::B010, Path::new("B009_B010.py"); "B010")]
|
||||
#[test_case(RuleCode::B011, Path::new("B011.py"); "B011")]
|
||||
#[test_case(RuleCode::B012, Path::new("B012.py"); "B012")]
|
||||
#[test_case(RuleCode::B013, Path::new("B013.py"); "B013")]
|
||||
#[test_case(RuleCode::B014, Path::new("B014.py"); "B014")]
|
||||
#[test_case(RuleCode::B015, Path::new("B015.py"); "B015")]
|
||||
#[test_case(RuleCode::B016, Path::new("B016.py"); "B016")]
|
||||
#[test_case(RuleCode::B017, Path::new("B017.py"); "B017")]
|
||||
#[test_case(RuleCode::B018, Path::new("B018.py"); "B018")]
|
||||
#[test_case(RuleCode::B019, Path::new("B019.py"); "B019")]
|
||||
#[test_case(RuleCode::B020, Path::new("B020.py"); "B020")]
|
||||
#[test_case(RuleCode::B021, Path::new("B021.py"); "B021")]
|
||||
#[test_case(RuleCode::B022, Path::new("B022.py"); "B022")]
|
||||
#[test_case(RuleCode::B023, Path::new("B023.py"); "B023")]
|
||||
#[test_case(RuleCode::B024, Path::new("B024.py"); "B024")]
|
||||
#[test_case(RuleCode::B025, Path::new("B025.py"); "B025")]
|
||||
#[test_case(RuleCode::B026, Path::new("B026.py"); "B026")]
|
||||
#[test_case(RuleCode::B027, Path::new("B027.py"); "B027")]
|
||||
#[test_case(RuleCode::B904, Path::new("B904.py"); "B904")]
|
||||
#[test_case(RuleCode::B905, Path::new("B905.py"); "B905")]
|
||||
fn rules(rule_code: RuleCode, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.as_ref(), path.to_string_lossy());
|
||||
#[test_case(Rule::UnaryPrefixIncrement, Path::new("B002.py"); "B002")]
|
||||
#[test_case(Rule::AssignmentToOsEnviron, Path::new("B003.py"); "B003")]
|
||||
#[test_case(Rule::UnreliableCallableCheck, Path::new("B004.py"); "B004")]
|
||||
#[test_case(Rule::StripWithMultiCharacters, Path::new("B005.py"); "B005")]
|
||||
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_B008.py"); "B006")]
|
||||
#[test_case(Rule::UnusedLoopControlVariable, Path::new("B007.py"); "B007")]
|
||||
#[test_case(Rule::FunctionCallArgumentDefault, Path::new("B006_B008.py"); "B008")]
|
||||
#[test_case(Rule::GetAttrWithConstant, Path::new("B009_B010.py"); "B009")]
|
||||
#[test_case(Rule::SetAttrWithConstant, Path::new("B009_B010.py"); "B010")]
|
||||
#[test_case(Rule::DoNotAssertFalse, Path::new("B011.py"); "B011")]
|
||||
#[test_case(Rule::JumpStatementInFinally, Path::new("B012.py"); "B012")]
|
||||
#[test_case(Rule::RedundantTupleInExceptionHandler, Path::new("B013.py"); "B013")]
|
||||
#[test_case(Rule::DuplicateHandlerException, Path::new("B014.py"); "B014")]
|
||||
#[test_case(Rule::UselessComparison, Path::new("B015.py"); "B015")]
|
||||
#[test_case(Rule::CannotRaiseLiteral, Path::new("B016.py"); "B016")]
|
||||
#[test_case(Rule::NoAssertRaisesException, Path::new("B017.py"); "B017")]
|
||||
#[test_case(Rule::UselessExpression, Path::new("B018.py"); "B018")]
|
||||
#[test_case(Rule::CachedInstanceMethod, Path::new("B019.py"); "B019")]
|
||||
#[test_case(Rule::LoopVariableOverridesIterator, Path::new("B020.py"); "B020")]
|
||||
#[test_case(Rule::FStringDocstring, Path::new("B021.py"); "B021")]
|
||||
#[test_case(Rule::UselessContextlibSuppress, Path::new("B022.py"); "B022")]
|
||||
#[test_case(Rule::FunctionUsesLoopVariable, Path::new("B023.py"); "B023")]
|
||||
#[test_case(Rule::AbstractBaseClassWithoutAbstractMethod, Path::new("B024.py"); "B024")]
|
||||
#[test_case(Rule::DuplicateTryBlockException, Path::new("B025.py"); "B025")]
|
||||
#[test_case(Rule::StarArgUnpackingAfterKeywordArg, Path::new("B026.py"); "B026")]
|
||||
#[test_case(Rule::EmptyMethodWithoutAbstractDecorator, Path::new("B027.py"); "B027")]
|
||||
#[test_case(Rule::RaiseWithoutFromInsideExcept, Path::new("B904.py"); "B904")]
|
||||
#[test_case(Rule::ZipWithoutExplicitStrict, Path::new("B905.py"); "B905")]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
Path::new("./resources/test/fixtures/flake8_bugbear")
|
||||
.join(path)
|
||||
@@ -65,7 +65,7 @@ mod tests {
|
||||
"fastapi.Query".to_string(),
|
||||
],
|
||||
},
|
||||
..Settings::for_rules(vec![RuleCode::B008])
|
||||
..Settings::for_rules(vec![Rule::FunctionCallArgumentDefault])
|
||||
},
|
||||
)?;
|
||||
insta::assert_yaml_snapshot!(snapshot, diagnostics);
|
||||
|
||||
@@ -2,7 +2,7 @@ use rustpython_ast::{Constant, Expr, ExprKind, Keyword, Stmt, StmtKind};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::{Diagnostic, RuleCode};
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::violations;
|
||||
use crate::visibility::{is_abstract, is_overload};
|
||||
|
||||
@@ -78,7 +78,11 @@ pub fn abstract_base_class(
|
||||
let has_abstract_decorator = is_abstract(checker, decorator_list);
|
||||
has_abstract_method |= has_abstract_decorator;
|
||||
|
||||
if !checker.settings.rules.enabled(&RuleCode::B027) {
|
||||
if !checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::EmptyMethodWithoutAbstractDecorator)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -89,7 +93,11 @@ pub fn abstract_base_class(
|
||||
));
|
||||
}
|
||||
}
|
||||
if checker.settings.rules.enabled(&RuleCode::B024) {
|
||||
if checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::AbstractBaseClassWithoutAbstractMethod)
|
||||
{
|
||||
if !has_abstract_method {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
violations::AbstractBaseClassWithoutAbstractMethod(name.to_string()),
|
||||
|
||||
@@ -47,7 +47,7 @@ pub fn assert_false(checker: &mut Checker, stmt: &Stmt, test: &Expr, msg: Option
|
||||
};
|
||||
|
||||
let mut diagnostic = Diagnostic::new(violations::DoNotAssertFalse, Range::from_located(test));
|
||||
if checker.patch(diagnostic.kind.code()) {
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let mut generator: Generator = checker.stylist.into();
|
||||
generator.unparse_stmt(&assertion_error(msg));
|
||||
diagnostic.amend(Fix::replacement(
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
//! Checks for `self.assertRaises(Exception)`.
|
||||
//!
|
||||
//! ## Why is this bad?
|
||||
//!
|
||||
//! `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`.
|
||||
|
||||
use rustpython_ast::{ExprKind, Stmt, Withitem};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::ast::helpers;
|
||||
use crate::ast::types::{CallPath, Range};
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::Fix;
|
||||
use crate::registry::{Diagnostic, RuleCode};
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::source_code::Generator;
|
||||
use crate::violations;
|
||||
|
||||
@@ -41,7 +41,11 @@ fn duplicate_handler_exceptions<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
if checker.settings.rules.enabled(&RuleCode::B014) {
|
||||
if checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::DuplicateHandlerException)
|
||||
{
|
||||
// TODO(charlie): Handle "BaseException" and redundant exception aliases.
|
||||
if !duplicates.is_empty() {
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
@@ -54,7 +58,7 @@ fn duplicate_handler_exceptions<'a>(
|
||||
),
|
||||
Range::from_located(expr),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.code()) {
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let mut generator: Generator = checker.stylist.into();
|
||||
if unique_elts.len() == 1 {
|
||||
generator.unparse_expr(unique_elts[0], 0);
|
||||
@@ -105,7 +109,11 @@ pub fn duplicate_exceptions(checker: &mut Checker, handlers: &[Excepthandler]) {
|
||||
}
|
||||
}
|
||||
|
||||
if checker.settings.rules.enabled(&RuleCode::B025) {
|
||||
if checker
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::DuplicateTryBlockException)
|
||||
{
|
||||
for (name, exprs) in duplicates {
|
||||
for expr in exprs {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
|
||||
@@ -3,7 +3,7 @@ use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Location};
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::Fix;
|
||||
use crate::python::identifiers::IDENTIFIER_REGEX;
|
||||
use crate::python::identifiers::is_identifier;
|
||||
use crate::python::keyword::KWLIST;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::source_code::Generator;
|
||||
@@ -38,7 +38,7 @@ pub fn getattr_with_constant(checker: &mut Checker, expr: &Expr, func: &Expr, ar
|
||||
} = &arg.node else {
|
||||
return;
|
||||
};
|
||||
if !IDENTIFIER_REGEX.is_match(value) {
|
||||
if !is_identifier(value) {
|
||||
return;
|
||||
}
|
||||
if KWLIST.contains(&value.as_str()) {
|
||||
@@ -47,7 +47,7 @@ pub fn getattr_with_constant(checker: &mut Checker, expr: &Expr, func: &Expr, ar
|
||||
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(violations::GetAttrWithConstant, Range::from_located(expr));
|
||||
if checker.patch(diagnostic.kind.code()) {
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let mut generator: Generator = checker.stylist.into();
|
||||
generator.unparse_expr(&attribute(obj, value), 0);
|
||||
diagnostic.amend(Fix::replacement(
|
||||
|
||||
@@ -23,7 +23,7 @@ pub fn redundant_tuple_in_exception_handler(checker: &mut Checker, handlers: &[E
|
||||
violations::RedundantTupleInExceptionHandler(elt.to_string()),
|
||||
Range::from_located(type_),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.code()) {
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let mut generator: Generator = checker.stylist.into();
|
||||
generator.unparse_expr(elt, 0);
|
||||
diagnostic.amend(Fix::replacement(
|
||||
|
||||
@@ -3,7 +3,7 @@ use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Location, Stmt, Stmt
|
||||
use crate::ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::Fix;
|
||||
use crate::python::identifiers::IDENTIFIER_REGEX;
|
||||
use crate::python::identifiers::is_identifier;
|
||||
use crate::python::keyword::KWLIST;
|
||||
use crate::registry::Diagnostic;
|
||||
use crate::source_code::{Generator, Stylist};
|
||||
@@ -49,7 +49,7 @@ pub fn setattr_with_constant(checker: &mut Checker, expr: &Expr, func: &Expr, ar
|
||||
} = &name.node else {
|
||||
return;
|
||||
};
|
||||
if !IDENTIFIER_REGEX.is_match(name) {
|
||||
if !is_identifier(name) {
|
||||
return;
|
||||
}
|
||||
if KWLIST.contains(&name.as_str()) {
|
||||
@@ -62,7 +62,7 @@ pub fn setattr_with_constant(checker: &mut Checker, expr: &Expr, func: &Expr, ar
|
||||
if expr == child.as_ref() {
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(violations::SetAttrWithConstant, Range::from_located(expr));
|
||||
if checker.patch(diagnostic.kind.code()) {
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.amend(Fix::replacement(
|
||||
assignment(obj, name, value, checker.stylist),
|
||||
expr.location,
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
//! Checks for `f(x=0, *(1, 2))`.
|
||||
//!
|
||||
//! ## Why is this bad?
|
||||
//!
|
||||
//! 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.
|
||||
|
||||
use rustpython_ast::{Expr, ExprKind, Keyword};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
//! Checks for `++n`.
|
||||
//!
|
||||
//! ## Why is this bad?
|
||||
//!
|
||||
//! Python does not support the unary prefix increment. Writing `++n` is
|
||||
//! equivalent to `+(+(n))`, which equals `n`.
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
//! ```python
|
||||
//! ++n;
|
||||
//! ```
|
||||
//!
|
||||
//! Use instead:
|
||||
//!
|
||||
//! ```python
|
||||
//! n += 1
|
||||
//! ```
|
||||
|
||||
use rustpython_ast::{Expr, ExprKind, Unaryop};
|
||||
|
||||
use crate::ast::types::Range;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user