mirror of https://github.com/astral-sh/ruff
Merge branch 'main' into specialize-non-generic
This commit is contained in:
commit
ea59e420aa
|
|
@ -24,6 +24,8 @@ env:
|
||||||
PACKAGE_NAME: ruff
|
PACKAGE_NAME: ruff
|
||||||
PYTHON_VERSION: "3.14"
|
PYTHON_VERSION: "3.14"
|
||||||
NEXTEST_PROFILE: ci
|
NEXTEST_PROFILE: ci
|
||||||
|
# Enable mdtests that require external dependencies
|
||||||
|
MDTEST_EXTERNAL: "1"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
determine_changes:
|
determine_changes:
|
||||||
|
|
@ -296,7 +298,7 @@ jobs:
|
||||||
# sync, not just public items. Eventually we should do this for all
|
# sync, not just public items. Eventually we should do this for all
|
||||||
# crates; for now add crates here as they are warning-clean to prevent
|
# crates; for now add crates here as they are warning-clean to prevent
|
||||||
# regression.
|
# regression.
|
||||||
- run: cargo doc --no-deps -p ty_python_semantic -p ty -p ty_test -p ruff_db --document-private-items
|
- run: cargo doc --no-deps -p ty_python_semantic -p ty -p ty_test -p ruff_db -p ruff_python_formatter --document-private-items
|
||||||
env:
|
env:
|
||||||
# Setting RUSTDOCFLAGS because `cargo doc --check` isn't yet implemented (https://github.com/rust-lang/cargo/issues/10025).
|
# Setting RUSTDOCFLAGS because `cargo doc --check` isn't yet implemented (https://github.com/rust-lang/cargo/issues/10025).
|
||||||
RUSTDOCFLAGS: "-D warnings"
|
RUSTDOCFLAGS: "-D warnings"
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ jobs:
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
|
shared-key: "mypy-primer"
|
||||||
workspaces: "ruff"
|
workspaces: "ruff"
|
||||||
|
|
||||||
- name: Install Rust toolchain
|
- name: Install Rust toolchain
|
||||||
|
|
@ -86,6 +87,7 @@ jobs:
|
||||||
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
with:
|
with:
|
||||||
workspaces: "ruff"
|
workspaces: "ruff"
|
||||||
|
shared-key: "mypy-primer"
|
||||||
|
|
||||||
- name: Install Rust toolchain
|
- name: Install Rust toolchain
|
||||||
run: rustup show
|
run: rustup show
|
||||||
|
|
@ -105,3 +107,54 @@ jobs:
|
||||||
with:
|
with:
|
||||||
name: mypy_primer_memory_diff
|
name: mypy_primer_memory_diff
|
||||||
path: mypy_primer_memory.diff
|
path: mypy_primer_memory.diff
|
||||||
|
|
||||||
|
# Runs mypy twice against the same ty version to catch any non-deterministic behavior (ideally).
|
||||||
|
# The job is disabled for now because there are some non-deterministic diagnostics.
|
||||||
|
mypy_primer_same_revision:
|
||||||
|
name: Run mypy_primer on same revision
|
||||||
|
runs-on: ${{ github.repository == 'astral-sh/ruff' && 'depot-ubuntu-22.04-32' || 'ubuntu-latest' }}
|
||||||
|
timeout-minutes: 20
|
||||||
|
# TODO: Enable once we fixed the non-deterministic diagnostics
|
||||||
|
if: false
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
with:
|
||||||
|
path: ruff
|
||||||
|
fetch-depth: 0
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Install the latest version of uv
|
||||||
|
uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
|
||||||
|
|
||||||
|
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
|
||||||
|
with:
|
||||||
|
workspaces: "ruff"
|
||||||
|
shared-key: "mypy-primer"
|
||||||
|
|
||||||
|
- name: Install Rust toolchain
|
||||||
|
run: rustup show
|
||||||
|
|
||||||
|
- name: Run determinism check
|
||||||
|
env:
|
||||||
|
BASE_REVISION: ${{ github.event.pull_request.head.sha }}
|
||||||
|
PRIMER_SELECTOR: crates/ty_python_semantic/resources/primer/good.txt
|
||||||
|
CLICOLOR_FORCE: "1"
|
||||||
|
DIFF_FILE: mypy_primer_determinism.diff
|
||||||
|
run: |
|
||||||
|
cd ruff
|
||||||
|
scripts/mypy_primer.sh
|
||||||
|
|
||||||
|
- name: Check for non-determinism
|
||||||
|
run: |
|
||||||
|
# Remove ANSI color codes for checking
|
||||||
|
sed -e 's/\x1b\[[0-9;]*m//g' mypy_primer_determinism.diff > mypy_primer_determinism_clean.diff
|
||||||
|
|
||||||
|
# Check if there are any differences (non-determinism)
|
||||||
|
if [ -s mypy_primer_determinism_clean.diff ]; then
|
||||||
|
echo "ERROR: Non-deterministic output detected!"
|
||||||
|
echo "The following differences were found when running ty twice on the same commit:"
|
||||||
|
cat mypy_primer_determinism_clean.diff
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "✓ Output is deterministic"
|
||||||
|
fi
|
||||||
|
|
|
||||||
42
CHANGELOG.md
42
CHANGELOG.md
|
|
@ -1,5 +1,47 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.14.9
|
||||||
|
|
||||||
|
Released on 2025-12-11.
|
||||||
|
|
||||||
|
### Preview features
|
||||||
|
|
||||||
|
- \[`ruff`\] New `RUF100` diagnostics for unused range suppressions ([#21783](https://github.com/astral-sh/ruff/pull/21783))
|
||||||
|
- \[`pylint`\] Detect subclasses of builtin exceptions (`PLW0133`) ([#21382](https://github.com/astral-sh/ruff/pull/21382))
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
- Fix comment placement in lambda parameters ([#21868](https://github.com/astral-sh/ruff/pull/21868))
|
||||||
|
- Skip over trivia tokens after re-lexing ([#21895](https://github.com/astral-sh/ruff/pull/21895))
|
||||||
|
- \[`flake8-bandit`\] Fix false positive when using non-standard `CSafeLoader` path (S506). ([#21830](https://github.com/astral-sh/ruff/pull/21830))
|
||||||
|
- \[`flake8-bugbear`\] Accept immutable slice default arguments (`B008`) ([#21823](https://github.com/astral-sh/ruff/pull/21823))
|
||||||
|
|
||||||
|
### Rule changes
|
||||||
|
|
||||||
|
- \[`pydocstyle`\] Suppress `D417` for parameters with `Unpack` annotations ([#21816](https://github.com/astral-sh/ruff/pull/21816))
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
|
||||||
|
- Use `memchr` for computing line indexes ([#21838](https://github.com/astral-sh/ruff/pull/21838))
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
- Document `*.pyw` is included by default in preview ([#21885](https://github.com/astral-sh/ruff/pull/21885))
|
||||||
|
- Document range suppressions, reorganize suppression docs ([#21884](https://github.com/astral-sh/ruff/pull/21884))
|
||||||
|
- Update mkdocs-material to 9.7.0 (Insiders now free) ([#21797](https://github.com/astral-sh/ruff/pull/21797))
|
||||||
|
|
||||||
|
### Contributors
|
||||||
|
|
||||||
|
- [@Avasam](https://github.com/Avasam)
|
||||||
|
- [@MichaReiser](https://github.com/MichaReiser)
|
||||||
|
- [@charliermarsh](https://github.com/charliermarsh)
|
||||||
|
- [@amyreese](https://github.com/amyreese)
|
||||||
|
- [@phongddo](https://github.com/phongddo)
|
||||||
|
- [@prakhar1144](https://github.com/prakhar1144)
|
||||||
|
- [@mahiro72](https://github.com/mahiro72)
|
||||||
|
- [@ntBre](https://github.com/ntBre)
|
||||||
|
- [@LoicRiegel](https://github.com/LoicRiegel)
|
||||||
|
|
||||||
## 0.14.8
|
## 0.14.8
|
||||||
|
|
||||||
Released on 2025-12-04.
|
Released on 2025-12-04.
|
||||||
|
|
|
||||||
|
|
@ -1016,7 +1016,7 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"option-ext",
|
"option-ext",
|
||||||
"redox_users",
|
"redox_users",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.61.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1108,7 +1108,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.61.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1238,9 +1238,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "get-size-derive2"
|
name = "get-size-derive2"
|
||||||
version = "0.7.2"
|
version = "0.7.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ff47daa61505c85af126e9dd64af6a342a33dc0cccfe1be74ceadc7d352e6efd"
|
checksum = "ab21d7bd2c625f2064f04ce54bcb88bc57c45724cde45cba326d784e22d3f71a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"attribute-derive",
|
"attribute-derive",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
@ -1249,14 +1249,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "get-size2"
|
name = "get-size2"
|
||||||
version = "0.7.2"
|
version = "0.7.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ac7bb8710e1f09672102be7ddf39f764d8440ae74a9f4e30aaa4820dcdffa4af"
|
checksum = "879272b0de109e2b67b39fcfe3d25fdbba96ac07e44a254f5a0b4d7ff55340cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"compact_str",
|
"compact_str",
|
||||||
"get-size-derive2",
|
"get-size-derive2",
|
||||||
"hashbrown 0.16.1",
|
"hashbrown 0.16.1",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
|
"ordermap",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -1763,7 +1764,7 @@ dependencies = [
|
||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
"portable-atomic-util",
|
"portable-atomic-util",
|
||||||
"serde_core",
|
"serde_core",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.61.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2233,9 +2234,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ordermap"
|
name = "ordermap"
|
||||||
version = "0.5.12"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b100f7dd605611822d30e182214d3c02fdefce2d801d23993f6b6ba6ca1392af"
|
checksum = "ed637741ced8fb240855d22a2b4f208dab7a06bcce73380162e5253000c16758"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
@ -2859,7 +2860,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruff"
|
name = "ruff"
|
||||||
version = "0.14.8"
|
version = "0.14.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"argfile",
|
"argfile",
|
||||||
|
|
@ -3117,7 +3118,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruff_linter"
|
name = "ruff_linter"
|
||||||
version = "0.14.8"
|
version = "0.14.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
|
@ -3348,6 +3349,7 @@ dependencies = [
|
||||||
"compact_str",
|
"compact_str",
|
||||||
"get-size2",
|
"get-size2",
|
||||||
"insta",
|
"insta",
|
||||||
|
"itertools 0.14.0",
|
||||||
"memchr",
|
"memchr",
|
||||||
"ruff_annotate_snippets",
|
"ruff_annotate_snippets",
|
||||||
"ruff_python_ast",
|
"ruff_python_ast",
|
||||||
|
|
@ -3473,7 +3475,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruff_wasm"
|
name = "ruff_wasm"
|
||||||
version = "0.14.8"
|
version = "0.14.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"console_error_panic_hook",
|
"console_error_panic_hook",
|
||||||
"console_log",
|
"console_log",
|
||||||
|
|
@ -3571,7 +3573,7 @@ dependencies = [
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.61.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -3589,7 +3591,7 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "salsa"
|
name = "salsa"
|
||||||
version = "0.24.0"
|
version = "0.24.0"
|
||||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0#59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0"
|
source = "git+https://github.com/salsa-rs/salsa.git?rev=55e5e7d32fa3fc189276f35bb04c9438f9aedbd1#55e5e7d32fa3fc189276f35bb04c9438f9aedbd1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"boxcar",
|
"boxcar",
|
||||||
"compact_str",
|
"compact_str",
|
||||||
|
|
@ -3600,6 +3602,7 @@ dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"intrusive-collections",
|
"intrusive-collections",
|
||||||
"inventory",
|
"inventory",
|
||||||
|
"ordermap",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
|
|
@ -3613,12 +3616,12 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "salsa-macro-rules"
|
name = "salsa-macro-rules"
|
||||||
version = "0.24.0"
|
version = "0.24.0"
|
||||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0#59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0"
|
source = "git+https://github.com/salsa-rs/salsa.git?rev=55e5e7d32fa3fc189276f35bb04c9438f9aedbd1#55e5e7d32fa3fc189276f35bb04c9438f9aedbd1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "salsa-macros"
|
name = "salsa-macros"
|
||||||
version = "0.24.0"
|
version = "0.24.0"
|
||||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0#59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0"
|
source = "git+https://github.com/salsa-rs/salsa.git?rev=55e5e7d32fa3fc189276f35bb04c9438f9aedbd1#55e5e7d32fa3fc189276f35bb04c9438f9aedbd1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
@ -3972,7 +3975,7 @@ dependencies = [
|
||||||
"getrandom 0.3.4",
|
"getrandom 0.3.4",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.61.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -4557,6 +4560,7 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"camino",
|
"camino",
|
||||||
"colored 3.0.0",
|
"colored 3.0.0",
|
||||||
|
"dunce",
|
||||||
"insta",
|
"insta",
|
||||||
"memchr",
|
"memchr",
|
||||||
"path-slash",
|
"path-slash",
|
||||||
|
|
@ -5025,7 +5029,7 @@ version = "0.1.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.61.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ etcetera = { version = "0.11.0" }
|
||||||
fern = { version = "0.7.0" }
|
fern = { version = "0.7.0" }
|
||||||
filetime = { version = "0.2.23" }
|
filetime = { version = "0.2.23" }
|
||||||
getrandom = { version = "0.3.1" }
|
getrandom = { version = "0.3.1" }
|
||||||
get-size2 = { version = "0.7.0", features = [
|
get-size2 = { version = "0.7.3", features = [
|
||||||
"derive",
|
"derive",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
|
|
@ -129,7 +129,7 @@ memchr = { version = "2.7.1" }
|
||||||
mimalloc = { version = "0.1.39" }
|
mimalloc = { version = "0.1.39" }
|
||||||
natord = { version = "1.0.9" }
|
natord = { version = "1.0.9" }
|
||||||
notify = { version = "8.0.0" }
|
notify = { version = "8.0.0" }
|
||||||
ordermap = { version = "0.5.0" }
|
ordermap = { version = "1.0.0" }
|
||||||
path-absolutize = { version = "3.1.1" }
|
path-absolutize = { version = "3.1.1" }
|
||||||
path-slash = { version = "0.2.1" }
|
path-slash = { version = "0.2.1" }
|
||||||
pathdiff = { version = "0.2.1" }
|
pathdiff = { version = "0.2.1" }
|
||||||
|
|
@ -146,7 +146,7 @@ regex-automata = { version = "0.4.9" }
|
||||||
rustc-hash = { version = "2.0.0" }
|
rustc-hash = { version = "2.0.0" }
|
||||||
rustc-stable-hash = { version = "0.1.2" }
|
rustc-stable-hash = { version = "0.1.2" }
|
||||||
# When updating salsa, make sure to also update the revision in `fuzz/Cargo.toml`
|
# When updating salsa, make sure to also update the revision in `fuzz/Cargo.toml`
|
||||||
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "59aa1075e837f5deb0d6ffb24b68fedc0f4bc5e0", default-features = false, features = [
|
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "55e5e7d32fa3fc189276f35bb04c9438f9aedbd1", default-features = false, features = [
|
||||||
"compact_str",
|
"compact_str",
|
||||||
"macros",
|
"macros",
|
||||||
"salsa_unstable",
|
"salsa_unstable",
|
||||||
|
|
|
||||||
|
|
@ -147,8 +147,8 @@ curl -LsSf https://astral.sh/ruff/install.sh | sh
|
||||||
powershell -c "irm https://astral.sh/ruff/install.ps1 | iex"
|
powershell -c "irm https://astral.sh/ruff/install.ps1 | iex"
|
||||||
|
|
||||||
# For a specific version.
|
# For a specific version.
|
||||||
curl -LsSf https://astral.sh/ruff/0.14.8/install.sh | sh
|
curl -LsSf https://astral.sh/ruff/0.14.9/install.sh | sh
|
||||||
powershell -c "irm https://astral.sh/ruff/0.14.8/install.ps1 | iex"
|
powershell -c "irm https://astral.sh/ruff/0.14.9/install.ps1 | iex"
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff),
|
You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff),
|
||||||
|
|
@ -181,7 +181,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff
|
||||||
```yaml
|
```yaml
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
# Ruff version.
|
# Ruff version.
|
||||||
rev: v0.14.8
|
rev: v0.14.9
|
||||||
hooks:
|
hooks:
|
||||||
# Run the linter.
|
# Run the linter.
|
||||||
- id: ruff-check
|
- id: ruff-check
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "ruff"
|
name = "ruff"
|
||||||
version = "0.14.8"
|
version = "0.14.9"
|
||||||
publish = true
|
publish = true
|
||||||
authors = { workspace = true }
|
authors = { workspace = true }
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -1440,6 +1440,78 @@ def function():
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ignore_noqa() -> Result<()> {
|
||||||
|
let fixture = CliTest::new()?;
|
||||||
|
fixture.write_file(
|
||||||
|
"ruff.toml",
|
||||||
|
r#"
|
||||||
|
[lint]
|
||||||
|
select = ["F401"]
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
fixture.write_file(
|
||||||
|
"noqa.py",
|
||||||
|
r#"
|
||||||
|
import os # noqa: F401
|
||||||
|
|
||||||
|
# ruff: disable[F401]
|
||||||
|
import sys
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// without --ignore-noqa
|
||||||
|
assert_cmd_snapshot!(fixture
|
||||||
|
.check_command()
|
||||||
|
.args(["--config", "ruff.toml"])
|
||||||
|
.arg("noqa.py"),
|
||||||
|
@r"
|
||||||
|
success: false
|
||||||
|
exit_code: 1
|
||||||
|
----- stdout -----
|
||||||
|
noqa.py:5:8: F401 [*] `sys` imported but unused
|
||||||
|
Found 1 error.
|
||||||
|
[*] 1 fixable with the `--fix` option.
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
");
|
||||||
|
|
||||||
|
assert_cmd_snapshot!(fixture
|
||||||
|
.check_command()
|
||||||
|
.args(["--config", "ruff.toml"])
|
||||||
|
.arg("noqa.py")
|
||||||
|
.args(["--preview"]),
|
||||||
|
@r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
All checks passed!
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
");
|
||||||
|
|
||||||
|
// with --ignore-noqa --preview
|
||||||
|
assert_cmd_snapshot!(fixture
|
||||||
|
.check_command()
|
||||||
|
.args(["--config", "ruff.toml"])
|
||||||
|
.arg("noqa.py")
|
||||||
|
.args(["--ignore-noqa", "--preview"]),
|
||||||
|
@r"
|
||||||
|
success: false
|
||||||
|
exit_code: 1
|
||||||
|
----- stdout -----
|
||||||
|
noqa.py:2:8: F401 [*] `os` imported but unused
|
||||||
|
noqa.py:5:8: F401 [*] `sys` imported but unused
|
||||||
|
Found 2 errors.
|
||||||
|
[*] 2 fixable with the `--fix` option.
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_noqa() -> Result<()> {
|
fn add_noqa() -> Result<()> {
|
||||||
let fixture = CliTest::new()?;
|
let fixture = CliTest::new()?;
|
||||||
|
|
@ -1632,6 +1704,100 @@ def unused(x): # noqa: ANN001, ARG001, D103
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_noqa_existing_file_level_noqa() -> Result<()> {
|
||||||
|
let fixture = CliTest::new()?;
|
||||||
|
fixture.write_file(
|
||||||
|
"ruff.toml",
|
||||||
|
r#"
|
||||||
|
[lint]
|
||||||
|
select = ["F401"]
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
fixture.write_file(
|
||||||
|
"noqa.py",
|
||||||
|
r#"
|
||||||
|
# ruff: noqa F401
|
||||||
|
import os
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
assert_cmd_snapshot!(fixture
|
||||||
|
.check_command()
|
||||||
|
.args(["--config", "ruff.toml"])
|
||||||
|
.arg("noqa.py")
|
||||||
|
.arg("--preview")
|
||||||
|
.args(["--add-noqa"])
|
||||||
|
.arg("-")
|
||||||
|
.pass_stdin(r#"
|
||||||
|
|
||||||
|
"#), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
");
|
||||||
|
|
||||||
|
let test_code =
|
||||||
|
fs::read_to_string(fixture.root().join("noqa.py")).expect("should read test file");
|
||||||
|
|
||||||
|
insta::assert_snapshot!(test_code, @r"
|
||||||
|
# ruff: noqa F401
|
||||||
|
import os
|
||||||
|
");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_noqa_existing_range_suppression() -> Result<()> {
|
||||||
|
let fixture = CliTest::new()?;
|
||||||
|
fixture.write_file(
|
||||||
|
"ruff.toml",
|
||||||
|
r#"
|
||||||
|
[lint]
|
||||||
|
select = ["F401"]
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
fixture.write_file(
|
||||||
|
"noqa.py",
|
||||||
|
r#"
|
||||||
|
# ruff: disable[F401]
|
||||||
|
import os
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
assert_cmd_snapshot!(fixture
|
||||||
|
.check_command()
|
||||||
|
.args(["--config", "ruff.toml"])
|
||||||
|
.arg("noqa.py")
|
||||||
|
.arg("--preview")
|
||||||
|
.args(["--add-noqa"])
|
||||||
|
.arg("-")
|
||||||
|
.pass_stdin(r#"
|
||||||
|
|
||||||
|
"#), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
");
|
||||||
|
|
||||||
|
let test_code =
|
||||||
|
fs::read_to_string(fixture.root().join("noqa.py")).expect("should read test file");
|
||||||
|
|
||||||
|
insta::assert_snapshot!(test_code, @r"
|
||||||
|
# ruff: disable[F401]
|
||||||
|
import os
|
||||||
|
");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_noqa_multiline_comment() -> Result<()> {
|
fn add_noqa_multiline_comment() -> Result<()> {
|
||||||
let fixture = CliTest::new()?;
|
let fixture = CliTest::new()?;
|
||||||
|
|
|
||||||
|
|
@ -194,7 +194,7 @@ static SYMPY: Benchmark = Benchmark::new(
|
||||||
max_dep_date: "2025-06-17",
|
max_dep_date: "2025-06-17",
|
||||||
python_version: PythonVersion::PY312,
|
python_version: PythonVersion::PY312,
|
||||||
},
|
},
|
||||||
13000,
|
13030,
|
||||||
);
|
);
|
||||||
|
|
||||||
static TANJUN: Benchmark = Benchmark::new(
|
static TANJUN: Benchmark = Benchmark::new(
|
||||||
|
|
|
||||||
|
|
@ -166,28 +166,8 @@ impl Diagnostic {
|
||||||
/// Returns the primary message for this diagnostic.
|
/// Returns the primary message for this diagnostic.
|
||||||
///
|
///
|
||||||
/// A diagnostic always has a message, but it may be empty.
|
/// A diagnostic always has a message, but it may be empty.
|
||||||
///
|
|
||||||
/// NOTE: At present, this routine will return the first primary
|
|
||||||
/// annotation's message as the primary message when the main diagnostic
|
|
||||||
/// message is empty. This is meant to facilitate an incremental migration
|
|
||||||
/// in ty over to the new diagnostic data model. (The old data model
|
|
||||||
/// didn't distinguish between messages on the entire diagnostic and
|
|
||||||
/// messages attached to a particular span.)
|
|
||||||
pub fn primary_message(&self) -> &str {
|
pub fn primary_message(&self) -> &str {
|
||||||
if !self.inner.message.as_str().is_empty() {
|
self.inner.message.as_str()
|
||||||
return self.inner.message.as_str();
|
|
||||||
}
|
|
||||||
// FIXME: As a special case, while we're migrating ty
|
|
||||||
// to the new diagnostic data model, we'll look for a primary
|
|
||||||
// message from the primary annotation. This is because most
|
|
||||||
// ty diagnostics are created with an empty diagnostic
|
|
||||||
// message and instead attach the message to the annotation.
|
|
||||||
// Fixing this will require touching basically every diagnostic
|
|
||||||
// in ty, so we do it this way for now to match the old
|
|
||||||
// semantics. ---AG
|
|
||||||
self.primary_annotation()
|
|
||||||
.and_then(|ann| ann.get_message())
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Introspects this diagnostic and returns what kind of "primary" message
|
/// Introspects this diagnostic and returns what kind of "primary" message
|
||||||
|
|
@ -199,18 +179,6 @@ impl Diagnostic {
|
||||||
/// contains *essential* information or context for understanding the
|
/// contains *essential* information or context for understanding the
|
||||||
/// diagnostic.
|
/// diagnostic.
|
||||||
///
|
///
|
||||||
/// The reason why we don't just always return both the main diagnostic
|
|
||||||
/// message and the primary annotation message is because this was written
|
|
||||||
/// in the midst of an incremental migration of ty over to the new
|
|
||||||
/// diagnostic data model. At time of writing, diagnostics were still
|
|
||||||
/// constructed in the old model where the main diagnostic message and the
|
|
||||||
/// primary annotation message were not distinguished from each other. So
|
|
||||||
/// for now, we carefully return what kind of messages this diagnostic
|
|
||||||
/// contains. In effect, if this diagnostic has a non-empty main message
|
|
||||||
/// *and* a non-empty primary annotation message, then the diagnostic is
|
|
||||||
/// 100% using the new diagnostic data model and we can format things
|
|
||||||
/// appropriately.
|
|
||||||
///
|
|
||||||
/// The type returned implements the `std::fmt::Display` trait. In most
|
/// The type returned implements the `std::fmt::Display` trait. In most
|
||||||
/// cases, just converting it to a string (or printing it) will do what
|
/// cases, just converting it to a string (or printing it) will do what
|
||||||
/// you want.
|
/// you want.
|
||||||
|
|
@ -224,11 +192,10 @@ impl Diagnostic {
|
||||||
.primary_annotation()
|
.primary_annotation()
|
||||||
.and_then(|ann| ann.get_message())
|
.and_then(|ann| ann.get_message())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
match (main.is_empty(), annotation.is_empty()) {
|
if annotation.is_empty() {
|
||||||
(false, true) => ConciseMessage::MainDiagnostic(main),
|
ConciseMessage::MainDiagnostic(main)
|
||||||
(true, false) => ConciseMessage::PrimaryAnnotation(annotation),
|
} else {
|
||||||
(false, false) => ConciseMessage::Both { main, annotation },
|
ConciseMessage::Both { main, annotation }
|
||||||
(true, true) => ConciseMessage::Empty,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -693,18 +660,6 @@ impl SubDiagnostic {
|
||||||
/// contains *essential* information or context for understanding the
|
/// contains *essential* information or context for understanding the
|
||||||
/// diagnostic.
|
/// diagnostic.
|
||||||
///
|
///
|
||||||
/// The reason why we don't just always return both the main diagnostic
|
|
||||||
/// message and the primary annotation message is because this was written
|
|
||||||
/// in the midst of an incremental migration of ty over to the new
|
|
||||||
/// diagnostic data model. At time of writing, diagnostics were still
|
|
||||||
/// constructed in the old model where the main diagnostic message and the
|
|
||||||
/// primary annotation message were not distinguished from each other. So
|
|
||||||
/// for now, we carefully return what kind of messages this diagnostic
|
|
||||||
/// contains. In effect, if this diagnostic has a non-empty main message
|
|
||||||
/// *and* a non-empty primary annotation message, then the diagnostic is
|
|
||||||
/// 100% using the new diagnostic data model and we can format things
|
|
||||||
/// appropriately.
|
|
||||||
///
|
|
||||||
/// The type returned implements the `std::fmt::Display` trait. In most
|
/// The type returned implements the `std::fmt::Display` trait. In most
|
||||||
/// cases, just converting it to a string (or printing it) will do what
|
/// cases, just converting it to a string (or printing it) will do what
|
||||||
/// you want.
|
/// you want.
|
||||||
|
|
@ -714,11 +669,10 @@ impl SubDiagnostic {
|
||||||
.primary_annotation()
|
.primary_annotation()
|
||||||
.and_then(|ann| ann.get_message())
|
.and_then(|ann| ann.get_message())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
match (main.is_empty(), annotation.is_empty()) {
|
if annotation.is_empty() {
|
||||||
(false, true) => ConciseMessage::MainDiagnostic(main),
|
ConciseMessage::MainDiagnostic(main)
|
||||||
(true, false) => ConciseMessage::PrimaryAnnotation(annotation),
|
} else {
|
||||||
(false, false) => ConciseMessage::Both { main, annotation },
|
ConciseMessage::Both { main, annotation }
|
||||||
(true, true) => ConciseMessage::Empty,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -888,6 +842,10 @@ impl Annotation {
|
||||||
pub fn hide_snippet(&mut self, yes: bool) {
|
pub fn hide_snippet(&mut self, yes: bool) {
|
||||||
self.hide_snippet = yes;
|
self.hide_snippet = yes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_primary(&self) -> bool {
|
||||||
|
self.is_primary
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tags that can be associated with an annotation.
|
/// Tags that can be associated with an annotation.
|
||||||
|
|
@ -1508,28 +1466,10 @@ pub enum DiagnosticFormat {
|
||||||
pub enum ConciseMessage<'a> {
|
pub enum ConciseMessage<'a> {
|
||||||
/// A diagnostic contains a non-empty main message and an empty
|
/// A diagnostic contains a non-empty main message and an empty
|
||||||
/// primary annotation message.
|
/// primary annotation message.
|
||||||
///
|
|
||||||
/// This strongly suggests that the diagnostic is using the
|
|
||||||
/// "new" data model.
|
|
||||||
MainDiagnostic(&'a str),
|
MainDiagnostic(&'a str),
|
||||||
/// A diagnostic contains an empty main message and a non-empty
|
|
||||||
/// primary annotation message.
|
|
||||||
///
|
|
||||||
/// This strongly suggests that the diagnostic is using the
|
|
||||||
/// "old" data model.
|
|
||||||
PrimaryAnnotation(&'a str),
|
|
||||||
/// A diagnostic contains a non-empty main message and a non-empty
|
/// A diagnostic contains a non-empty main message and a non-empty
|
||||||
/// primary annotation message.
|
/// primary annotation message.
|
||||||
///
|
|
||||||
/// This strongly suggests that the diagnostic is using the
|
|
||||||
/// "new" data model.
|
|
||||||
Both { main: &'a str, annotation: &'a str },
|
Both { main: &'a str, annotation: &'a str },
|
||||||
/// A diagnostic contains an empty main message and an empty
|
|
||||||
/// primary annotation message.
|
|
||||||
///
|
|
||||||
/// This indicates that the diagnostic is probably using the old
|
|
||||||
/// model.
|
|
||||||
Empty,
|
|
||||||
/// A custom concise message has been provided.
|
/// A custom concise message has been provided.
|
||||||
Custom(&'a str),
|
Custom(&'a str),
|
||||||
}
|
}
|
||||||
|
|
@ -1540,13 +1480,9 @@ impl std::fmt::Display for ConciseMessage<'_> {
|
||||||
ConciseMessage::MainDiagnostic(main) => {
|
ConciseMessage::MainDiagnostic(main) => {
|
||||||
write!(f, "{main}")
|
write!(f, "{main}")
|
||||||
}
|
}
|
||||||
ConciseMessage::PrimaryAnnotation(annotation) => {
|
|
||||||
write!(f, "{annotation}")
|
|
||||||
}
|
|
||||||
ConciseMessage::Both { main, annotation } => {
|
ConciseMessage::Both { main, annotation } => {
|
||||||
write!(f, "{main}: {annotation}")
|
write!(f, "{main}: {annotation}")
|
||||||
}
|
}
|
||||||
ConciseMessage::Empty => Ok(()),
|
|
||||||
ConciseMessage::Custom(message) => {
|
ConciseMessage::Custom(message) => {
|
||||||
write!(f, "{message}")
|
write!(f, "{message}")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "ruff_linter"
|
name = "ruff_linter"
|
||||||
version = "0.14.8"
|
version = "0.14.9"
|
||||||
publish = false
|
publish = false
|
||||||
authors = { workspace = true }
|
authors = { workspace = true }
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -199,6 +199,9 @@ def bytes_okay(value=bytes(1)):
|
||||||
def int_okay(value=int("12")):
|
def int_okay(value=int("12")):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# Allow immutable slice()
|
||||||
|
def slice_okay(value=slice(1,2)):
|
||||||
|
pass
|
||||||
|
|
||||||
# Allow immutable complex() value
|
# Allow immutable complex() value
|
||||||
def complex_okay(value=complex(1,2)):
|
def complex_okay(value=complex(1,2)):
|
||||||
|
|
|
||||||
|
|
@ -218,3 +218,26 @@ def should_not_fail(payload, Args):
|
||||||
Args:
|
Args:
|
||||||
The other arguments.
|
The other arguments.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# Test cases for Unpack[TypedDict] kwargs
|
||||||
|
from typing import TypedDict
|
||||||
|
from typing_extensions import Unpack
|
||||||
|
|
||||||
|
class User(TypedDict):
|
||||||
|
id: int
|
||||||
|
name: str
|
||||||
|
|
||||||
|
def function_with_unpack_args_should_not_fail(query: str, **kwargs: Unpack[User]):
|
||||||
|
"""Function with Unpack kwargs.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
query: some arg
|
||||||
|
"""
|
||||||
|
|
||||||
|
def function_with_unpack_and_missing_arg_doc_should_fail(query: str, **kwargs: Unpack[User]):
|
||||||
|
"""Function with Unpack kwargs but missing query arg documentation.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
**kwargs: keyword arguments
|
||||||
|
"""
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,40 @@ from abc import ABC, abstractmethod
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
|
|
||||||
|
|
||||||
|
class MyError(Exception):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class MySubError(MyError):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class MyValueError(ValueError):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class MyUserWarning(UserWarning):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
# Violation test cases with builtin errors: PLW0133
|
||||||
|
|
||||||
|
|
||||||
# Test case 1: Useless exception statement
|
# Test case 1: Useless exception statement
|
||||||
def func():
|
def func():
|
||||||
AssertionError("This is an assertion error") # PLW0133
|
AssertionError("This is an assertion error") # PLW0133
|
||||||
|
MyError("This is a custom error") # PLW0133
|
||||||
|
MySubError("This is a custom error") # PLW0133
|
||||||
|
MyValueError("This is a custom value error") # PLW0133
|
||||||
|
|
||||||
|
|
||||||
# Test case 2: Useless exception statement in try-except block
|
# Test case 2: Useless exception statement in try-except block
|
||||||
def func():
|
def func():
|
||||||
try:
|
try:
|
||||||
Exception("This is an exception") # PLW0133
|
Exception("This is an exception") # PLW0133
|
||||||
|
MyError("This is an exception") # PLW0133
|
||||||
|
MySubError("This is an exception") # PLW0133
|
||||||
|
MyValueError("This is an exception") # PLW0133
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
@ -19,6 +44,9 @@ def func():
|
||||||
def func():
|
def func():
|
||||||
if True:
|
if True:
|
||||||
RuntimeError("This is an exception") # PLW0133
|
RuntimeError("This is an exception") # PLW0133
|
||||||
|
MyError("This is an exception") # PLW0133
|
||||||
|
MySubError("This is an exception") # PLW0133
|
||||||
|
MyValueError("This is an exception") # PLW0133
|
||||||
|
|
||||||
|
|
||||||
# Test case 4: Useless exception statement in class
|
# Test case 4: Useless exception statement in class
|
||||||
|
|
@ -26,12 +54,18 @@ def func():
|
||||||
class Class:
|
class Class:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
TypeError("This is an exception") # PLW0133
|
TypeError("This is an exception") # PLW0133
|
||||||
|
MyError("This is an exception") # PLW0133
|
||||||
|
MySubError("This is an exception") # PLW0133
|
||||||
|
MyValueError("This is an exception") # PLW0133
|
||||||
|
|
||||||
|
|
||||||
# Test case 5: Useless exception statement in function
|
# Test case 5: Useless exception statement in function
|
||||||
def func():
|
def func():
|
||||||
def inner():
|
def inner():
|
||||||
IndexError("This is an exception") # PLW0133
|
IndexError("This is an exception") # PLW0133
|
||||||
|
MyError("This is an exception") # PLW0133
|
||||||
|
MySubError("This is an exception") # PLW0133
|
||||||
|
MyValueError("This is an exception") # PLW0133
|
||||||
|
|
||||||
inner()
|
inner()
|
||||||
|
|
||||||
|
|
@ -40,6 +74,9 @@ def func():
|
||||||
def func():
|
def func():
|
||||||
while True:
|
while True:
|
||||||
KeyError("This is an exception") # PLW0133
|
KeyError("This is an exception") # PLW0133
|
||||||
|
MyError("This is an exception") # PLW0133
|
||||||
|
MySubError("This is an exception") # PLW0133
|
||||||
|
MyValueError("This is an exception") # PLW0133
|
||||||
|
|
||||||
|
|
||||||
# Test case 7: Useless exception statement in abstract class
|
# Test case 7: Useless exception statement in abstract class
|
||||||
|
|
@ -48,27 +85,58 @@ def func():
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def method(self):
|
def method(self):
|
||||||
NotImplementedError("This is an exception") # PLW0133
|
NotImplementedError("This is an exception") # PLW0133
|
||||||
|
MyError("This is an exception") # PLW0133
|
||||||
|
MySubError("This is an exception") # PLW0133
|
||||||
|
MyValueError("This is an exception") # PLW0133
|
||||||
|
|
||||||
|
|
||||||
# Test case 8: Useless exception statement inside context manager
|
# Test case 8: Useless exception statement inside context manager
|
||||||
def func():
|
def func():
|
||||||
with suppress(AttributeError):
|
with suppress(Exception):
|
||||||
AttributeError("This is an exception") # PLW0133
|
AttributeError("This is an exception") # PLW0133
|
||||||
|
MyError("This is an exception") # PLW0133
|
||||||
|
MySubError("This is an exception") # PLW0133
|
||||||
|
MyValueError("This is an exception") # PLW0133
|
||||||
|
|
||||||
|
|
||||||
# Test case 9: Useless exception statement in parentheses
|
# Test case 9: Useless exception statement in parentheses
|
||||||
def func():
|
def func():
|
||||||
(RuntimeError("This is an exception")) # PLW0133
|
(RuntimeError("This is an exception")) # PLW0133
|
||||||
|
(MyError("This is an exception")) # PLW0133
|
||||||
|
(MySubError("This is an exception")) # PLW0133
|
||||||
|
(MyValueError("This is an exception")) # PLW0133
|
||||||
|
|
||||||
|
|
||||||
# Test case 10: Useless exception statement in continuation
|
# Test case 10: Useless exception statement in continuation
|
||||||
def func():
|
def func():
|
||||||
x = 1; (RuntimeError("This is an exception")); y = 2 # PLW0133
|
x = 1; (RuntimeError("This is an exception")); y = 2 # PLW0133
|
||||||
|
x = 1; (MyError("This is an exception")); y = 2 # PLW0133
|
||||||
|
x = 1; (MySubError("This is an exception")); y = 2 # PLW0133
|
||||||
|
x = 1; (MyValueError("This is an exception")); y = 2 # PLW0133
|
||||||
|
|
||||||
|
|
||||||
# Test case 11: Useless warning statement
|
# Test case 11: Useless warning statement
|
||||||
def func():
|
def func():
|
||||||
UserWarning("This is an assertion error") # PLW0133
|
UserWarning("This is a user warning") # PLW0133
|
||||||
|
MyUserWarning("This is a custom user warning") # PLW0133
|
||||||
|
|
||||||
|
|
||||||
|
# Test case 12: Useless exception statement at module level
|
||||||
|
import builtins
|
||||||
|
|
||||||
|
builtins.TypeError("still an exception even though it's an Attribute") # PLW0133
|
||||||
|
|
||||||
|
PythonFinalizationError("Added in Python 3.13") # PLW0133
|
||||||
|
|
||||||
|
MyError("This is an exception") # PLW0133
|
||||||
|
|
||||||
|
MySubError("This is an exception") # PLW0133
|
||||||
|
|
||||||
|
MyValueError("This is an exception") # PLW0133
|
||||||
|
|
||||||
|
UserWarning("This is a user warning") # PLW0133
|
||||||
|
|
||||||
|
MyUserWarning("This is a custom user warning") # PLW0133
|
||||||
|
|
||||||
|
|
||||||
# Non-violation test cases: PLW0133
|
# Non-violation test cases: PLW0133
|
||||||
|
|
@ -119,10 +187,3 @@ def func():
|
||||||
def func():
|
def func():
|
||||||
with suppress(AttributeError):
|
with suppress(AttributeError):
|
||||||
raise AttributeError("This is an exception") # OK
|
raise AttributeError("This is an exception") # OK
|
||||||
|
|
||||||
|
|
||||||
import builtins
|
|
||||||
|
|
||||||
builtins.TypeError("still an exception even though it's an Attribute")
|
|
||||||
|
|
||||||
PythonFinalizationError("Added in Python 3.13")
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
def f():
|
||||||
|
# These should both be ignored by the range suppression.
|
||||||
|
# ruff: disable[E741, F841]
|
||||||
|
I = 1
|
||||||
|
# ruff: enable[E741, F841]
|
||||||
|
|
||||||
|
|
||||||
|
def f():
|
||||||
|
# These should both be ignored by the implicit range suppression.
|
||||||
|
# Should also generate an "unmatched suppression" warning.
|
||||||
|
# ruff:disable[E741,F841]
|
||||||
|
I = 1
|
||||||
|
|
||||||
|
|
||||||
|
def f():
|
||||||
|
# Neither warning is ignored, and an "unmatched suppression"
|
||||||
|
# should be generated.
|
||||||
|
I = 1
|
||||||
|
# ruff: enable[E741, F841]
|
||||||
|
|
||||||
|
|
||||||
|
def f():
|
||||||
|
# One should be ignored by the range suppression, and
|
||||||
|
# the other logged to the user.
|
||||||
|
# ruff: disable[E741]
|
||||||
|
I = 1
|
||||||
|
# ruff: enable[E741]
|
||||||
|
|
||||||
|
|
||||||
|
def f():
|
||||||
|
# Test interleaved range suppressions. The first and last
|
||||||
|
# lines should each log a different warning, while the
|
||||||
|
# middle line should be completely silenced.
|
||||||
|
# ruff: disable[E741]
|
||||||
|
l = 0
|
||||||
|
# ruff: disable[F841]
|
||||||
|
O = 1
|
||||||
|
# ruff: enable[E741]
|
||||||
|
I = 2
|
||||||
|
# ruff: enable[F841]
|
||||||
|
|
||||||
|
|
||||||
|
def f():
|
||||||
|
# Neither of these are ignored and warnings are
|
||||||
|
# logged to user
|
||||||
|
# ruff: disable[E501]
|
||||||
|
I = 1
|
||||||
|
# ruff: enable[E501]
|
||||||
|
|
||||||
|
|
||||||
|
def f():
|
||||||
|
# These should both be ignored by the range suppression,
|
||||||
|
# and an unusued noqa diagnostic should be logged.
|
||||||
|
# ruff:disable[E741,F841]
|
||||||
|
I = 1 # noqa: E741,F841
|
||||||
|
# ruff:enable[E741,F841]
|
||||||
|
|
||||||
|
|
||||||
|
def f():
|
||||||
|
# TODO: Duplicate codes should be counted as duplicate, not unused
|
||||||
|
# ruff: disable[F841, F841]
|
||||||
|
foo = 0
|
||||||
|
|
||||||
|
|
||||||
|
def f():
|
||||||
|
# Overlapping range suppressions, one should be marked as used,
|
||||||
|
# and the other should trigger an unused suppression diagnostic
|
||||||
|
# ruff: disable[F841]
|
||||||
|
# ruff: disable[F841]
|
||||||
|
foo = 0
|
||||||
|
|
||||||
|
|
||||||
|
def f():
|
||||||
|
# Multiple codes but only one is used
|
||||||
|
# ruff: disable[E741, F401, F841]
|
||||||
|
foo = 0
|
||||||
|
|
||||||
|
|
||||||
|
def f():
|
||||||
|
# Multiple codes but only two are used
|
||||||
|
# ruff: disable[E741, F401, F841]
|
||||||
|
I = 0
|
||||||
|
|
||||||
|
|
||||||
|
def f():
|
||||||
|
# Multiple codes but none are used
|
||||||
|
# ruff: disable[E741, F401, F841]
|
||||||
|
print("hello")
|
||||||
|
|
@ -437,6 +437,15 @@ impl<'a> Checker<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the [`Tokens`] for the parsed source file.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// Unlike [`Self::tokens`], this method always returns
|
||||||
|
/// the tokens for the current file, even when within a parsed type annotation.
|
||||||
|
pub(crate) fn source_tokens(&self) -> &'a Tokens {
|
||||||
|
self.parsed.tokens()
|
||||||
|
}
|
||||||
|
|
||||||
/// The [`Locator`] for the current file, which enables extraction of source code from byte
|
/// The [`Locator`] for the current file, which enables extraction of source code from byte
|
||||||
/// offsets.
|
/// offsets.
|
||||||
pub(crate) const fn locator(&self) -> &'a Locator<'a> {
|
pub(crate) const fn locator(&self) -> &'a Locator<'a> {
|
||||||
|
|
|
||||||
|
|
@ -12,17 +12,20 @@ use crate::fix::edits::delete_comment;
|
||||||
use crate::noqa::{
|
use crate::noqa::{
|
||||||
Code, Directive, FileExemption, FileNoqaDirectives, NoqaDirectives, NoqaMapping,
|
Code, Directive, FileExemption, FileNoqaDirectives, NoqaDirectives, NoqaMapping,
|
||||||
};
|
};
|
||||||
|
use crate::preview::is_range_suppressions_enabled;
|
||||||
use crate::registry::Rule;
|
use crate::registry::Rule;
|
||||||
use crate::rule_redirects::get_redirect_target;
|
use crate::rule_redirects::get_redirect_target;
|
||||||
use crate::rules::pygrep_hooks;
|
use crate::rules::pygrep_hooks;
|
||||||
use crate::rules::ruff;
|
use crate::rules::ruff;
|
||||||
use crate::rules::ruff::rules::{UnusedCodes, UnusedNOQA};
|
use crate::rules::ruff::rules::{UnusedCodes, UnusedNOQA};
|
||||||
use crate::settings::LinterSettings;
|
use crate::settings::LinterSettings;
|
||||||
|
use crate::suppression::Suppressions;
|
||||||
use crate::{Edit, Fix, Locator};
|
use crate::{Edit, Fix, Locator};
|
||||||
|
|
||||||
use super::ast::LintContext;
|
use super::ast::LintContext;
|
||||||
|
|
||||||
/// RUF100
|
/// RUF100
|
||||||
|
#[expect(clippy::too_many_arguments)]
|
||||||
pub(crate) fn check_noqa(
|
pub(crate) fn check_noqa(
|
||||||
context: &mut LintContext,
|
context: &mut LintContext,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
|
|
@ -31,6 +34,7 @@ pub(crate) fn check_noqa(
|
||||||
noqa_line_for: &NoqaMapping,
|
noqa_line_for: &NoqaMapping,
|
||||||
analyze_directives: bool,
|
analyze_directives: bool,
|
||||||
settings: &LinterSettings,
|
settings: &LinterSettings,
|
||||||
|
suppressions: &Suppressions,
|
||||||
) -> Vec<usize> {
|
) -> Vec<usize> {
|
||||||
// Identify any codes that are globally exempted (within the current file).
|
// Identify any codes that are globally exempted (within the current file).
|
||||||
let file_noqa_directives =
|
let file_noqa_directives =
|
||||||
|
|
@ -40,7 +44,7 @@ pub(crate) fn check_noqa(
|
||||||
let mut noqa_directives =
|
let mut noqa_directives =
|
||||||
NoqaDirectives::from_commented_ranges(comment_ranges, &settings.external, path, locator);
|
NoqaDirectives::from_commented_ranges(comment_ranges, &settings.external, path, locator);
|
||||||
|
|
||||||
if file_noqa_directives.is_empty() && noqa_directives.is_empty() {
|
if file_noqa_directives.is_empty() && noqa_directives.is_empty() && suppressions.is_empty() {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,11 +64,19 @@ pub(crate) fn check_noqa(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply file-level suppressions first
|
||||||
if exemption.contains_secondary_code(code) {
|
if exemption.contains_secondary_code(code) {
|
||||||
ignored_diagnostics.push(index);
|
ignored_diagnostics.push(index);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply ranged suppressions next
|
||||||
|
if is_range_suppressions_enabled(settings) && suppressions.check_diagnostic(diagnostic) {
|
||||||
|
ignored_diagnostics.push(index);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply end-of-line noqa suppressions last
|
||||||
let noqa_offsets = diagnostic
|
let noqa_offsets = diagnostic
|
||||||
.parent()
|
.parent()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
@ -107,6 +119,9 @@ pub(crate) fn check_noqa(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Diagnostics for unused/invalid range suppressions
|
||||||
|
suppressions.check_suppressions(context, locator);
|
||||||
|
|
||||||
// Enforce that the noqa directive was actually used (RUF100), unless RUF100 was itself
|
// Enforce that the noqa directive was actually used (RUF100), unless RUF100 was itself
|
||||||
// suppressed.
|
// suppressed.
|
||||||
if context.is_rule_enabled(Rule::UnusedNOQA)
|
if context.is_rule_enabled(Rule::UnusedNOQA)
|
||||||
|
|
@ -128,8 +143,13 @@ pub(crate) fn check_noqa(
|
||||||
Directive::All(directive) => {
|
Directive::All(directive) => {
|
||||||
if matches.is_empty() {
|
if matches.is_empty() {
|
||||||
let edit = delete_comment(directive.range(), locator);
|
let edit = delete_comment(directive.range(), locator);
|
||||||
let mut diagnostic = context
|
let mut diagnostic = context.report_diagnostic(
|
||||||
.report_diagnostic(UnusedNOQA { codes: None }, directive.range());
|
UnusedNOQA {
|
||||||
|
codes: None,
|
||||||
|
kind: ruff::rules::UnusedNOQAKind::Noqa,
|
||||||
|
},
|
||||||
|
directive.range(),
|
||||||
|
);
|
||||||
diagnostic.add_primary_tag(ruff_db::diagnostic::DiagnosticTag::Unnecessary);
|
diagnostic.add_primary_tag(ruff_db::diagnostic::DiagnosticTag::Unnecessary);
|
||||||
diagnostic.set_fix(Fix::safe_edit(edit));
|
diagnostic.set_fix(Fix::safe_edit(edit));
|
||||||
}
|
}
|
||||||
|
|
@ -224,6 +244,7 @@ pub(crate) fn check_noqa(
|
||||||
.map(|code| (*code).to_string())
|
.map(|code| (*code).to_string())
|
||||||
.collect(),
|
.collect(),
|
||||||
}),
|
}),
|
||||||
|
kind: ruff::rules::UnusedNOQAKind::Noqa,
|
||||||
},
|
},
|
||||||
directive.range(),
|
directive.range(),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,13 @@
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
|
||||||
use ruff_python_ast::AnyNodeRef;
|
use ruff_python_ast::AnyNodeRef;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::{self, Tokens, parenthesized_range};
|
||||||
use ruff_python_ast::{self as ast, Arguments, ExceptHandler, Expr, ExprList, Parameters, Stmt};
|
use ruff_python_ast::{self as ast, Arguments, ExceptHandler, Expr, ExprList, Parameters, Stmt};
|
||||||
use ruff_python_codegen::Stylist;
|
use ruff_python_codegen::Stylist;
|
||||||
use ruff_python_index::Indexer;
|
use ruff_python_index::Indexer;
|
||||||
use ruff_python_trivia::textwrap::dedent_to;
|
use ruff_python_trivia::textwrap::dedent_to;
|
||||||
use ruff_python_trivia::{
|
use ruff_python_trivia::{
|
||||||
CommentRanges, PythonWhitespace, SimpleTokenKind, SimpleTokenizer, has_leading_content,
|
PythonWhitespace, SimpleTokenKind, SimpleTokenizer, has_leading_content, is_python_whitespace,
|
||||||
is_python_whitespace,
|
|
||||||
};
|
};
|
||||||
use ruff_source_file::{LineRanges, NewlineWithTrailingNewline, UniversalNewlines};
|
use ruff_source_file::{LineRanges, NewlineWithTrailingNewline, UniversalNewlines};
|
||||||
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
||||||
|
|
@ -209,7 +208,7 @@ pub(crate) fn remove_argument<T: Ranged>(
|
||||||
arguments: &Arguments,
|
arguments: &Arguments,
|
||||||
parentheses: Parentheses,
|
parentheses: Parentheses,
|
||||||
source: &str,
|
source: &str,
|
||||||
comment_ranges: &CommentRanges,
|
tokens: &Tokens,
|
||||||
) -> Result<Edit> {
|
) -> Result<Edit> {
|
||||||
// Partition into arguments before and after the argument to remove.
|
// Partition into arguments before and after the argument to remove.
|
||||||
let (before, after): (Vec<_>, Vec<_>) = arguments
|
let (before, after): (Vec<_>, Vec<_>) = arguments
|
||||||
|
|
@ -224,7 +223,7 @@ pub(crate) fn remove_argument<T: Ranged>(
|
||||||
.context("Unable to find argument")?;
|
.context("Unable to find argument")?;
|
||||||
|
|
||||||
let parenthesized_range =
|
let parenthesized_range =
|
||||||
parenthesized_range(arg.value().into(), arguments.into(), comment_ranges, source)
|
token::parenthesized_range(arg.value().into(), arguments.into(), tokens)
|
||||||
.unwrap_or(arg.range());
|
.unwrap_or(arg.range());
|
||||||
|
|
||||||
if !after.is_empty() {
|
if !after.is_empty() {
|
||||||
|
|
@ -270,25 +269,14 @@ pub(crate) fn remove_argument<T: Ranged>(
|
||||||
///
|
///
|
||||||
/// The new argument will be inserted before the first existing keyword argument in `arguments`, if
|
/// The new argument will be inserted before the first existing keyword argument in `arguments`, if
|
||||||
/// there are any present. Otherwise, the new argument is added to the end of the argument list.
|
/// there are any present. Otherwise, the new argument is added to the end of the argument list.
|
||||||
pub(crate) fn add_argument(
|
pub(crate) fn add_argument(argument: &str, arguments: &Arguments, tokens: &Tokens) -> Edit {
|
||||||
argument: &str,
|
|
||||||
arguments: &Arguments,
|
|
||||||
comment_ranges: &CommentRanges,
|
|
||||||
source: &str,
|
|
||||||
) -> Edit {
|
|
||||||
if let Some(ast::Keyword { range, value, .. }) = arguments.keywords.first() {
|
if let Some(ast::Keyword { range, value, .. }) = arguments.keywords.first() {
|
||||||
let keyword = parenthesized_range(value.into(), arguments.into(), comment_ranges, source)
|
let keyword = parenthesized_range(value.into(), arguments.into(), tokens).unwrap_or(*range);
|
||||||
.unwrap_or(*range);
|
|
||||||
Edit::insertion(format!("{argument}, "), keyword.start())
|
Edit::insertion(format!("{argument}, "), keyword.start())
|
||||||
} else if let Some(last) = arguments.arguments_source_order().last() {
|
} else if let Some(last) = arguments.arguments_source_order().last() {
|
||||||
// Case 1: existing arguments, so append after the last argument.
|
// Case 1: existing arguments, so append after the last argument.
|
||||||
let last = parenthesized_range(
|
let last = parenthesized_range(last.value().into(), arguments.into(), tokens)
|
||||||
last.value().into(),
|
.unwrap_or(last.range());
|
||||||
arguments.into(),
|
|
||||||
comment_ranges,
|
|
||||||
source,
|
|
||||||
)
|
|
||||||
.unwrap_or(last.range());
|
|
||||||
Edit::insertion(format!(", {argument}"), last.end())
|
Edit::insertion(format!(", {argument}"), last.end())
|
||||||
} else {
|
} else {
|
||||||
// Case 2: no arguments. Add argument, without any trailing comma.
|
// Case 2: no arguments. Add argument, without any trailing comma.
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ use crate::rules::ruff::rules::test_rules::{self, TEST_RULES, TestRule};
|
||||||
use crate::settings::types::UnsafeFixes;
|
use crate::settings::types::UnsafeFixes;
|
||||||
use crate::settings::{LinterSettings, TargetVersion, flags};
|
use crate::settings::{LinterSettings, TargetVersion, flags};
|
||||||
use crate::source_kind::SourceKind;
|
use crate::source_kind::SourceKind;
|
||||||
|
use crate::suppression::Suppressions;
|
||||||
use crate::{Locator, directives, fs};
|
use crate::{Locator, directives, fs};
|
||||||
|
|
||||||
pub(crate) mod float;
|
pub(crate) mod float;
|
||||||
|
|
@ -128,6 +129,7 @@ pub fn check_path(
|
||||||
source_type: PySourceType,
|
source_type: PySourceType,
|
||||||
parsed: &Parsed<ModModule>,
|
parsed: &Parsed<ModModule>,
|
||||||
target_version: TargetVersion,
|
target_version: TargetVersion,
|
||||||
|
suppressions: &Suppressions,
|
||||||
) -> Vec<Diagnostic> {
|
) -> Vec<Diagnostic> {
|
||||||
// Aggregate all diagnostics.
|
// Aggregate all diagnostics.
|
||||||
let mut context = LintContext::new(path, locator.contents(), settings);
|
let mut context = LintContext::new(path, locator.contents(), settings);
|
||||||
|
|
@ -339,6 +341,7 @@ pub fn check_path(
|
||||||
&directives.noqa_line_for,
|
&directives.noqa_line_for,
|
||||||
parsed.has_valid_syntax(),
|
parsed.has_valid_syntax(),
|
||||||
settings,
|
settings,
|
||||||
|
suppressions,
|
||||||
);
|
);
|
||||||
if noqa.is_enabled() {
|
if noqa.is_enabled() {
|
||||||
for index in ignored.iter().rev() {
|
for index in ignored.iter().rev() {
|
||||||
|
|
@ -400,6 +403,9 @@ pub fn add_noqa_to_path(
|
||||||
&indexer,
|
&indexer,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Parse range suppression comments
|
||||||
|
let suppressions = Suppressions::from_tokens(settings, locator.contents(), parsed.tokens());
|
||||||
|
|
||||||
// Generate diagnostics, ignoring any existing `noqa` directives.
|
// Generate diagnostics, ignoring any existing `noqa` directives.
|
||||||
let diagnostics = check_path(
|
let diagnostics = check_path(
|
||||||
path,
|
path,
|
||||||
|
|
@ -414,6 +420,7 @@ pub fn add_noqa_to_path(
|
||||||
source_type,
|
source_type,
|
||||||
&parsed,
|
&parsed,
|
||||||
target_version,
|
target_version,
|
||||||
|
&suppressions,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add any missing `# noqa` pragmas.
|
// Add any missing `# noqa` pragmas.
|
||||||
|
|
@ -427,6 +434,7 @@ pub fn add_noqa_to_path(
|
||||||
&directives.noqa_line_for,
|
&directives.noqa_line_for,
|
||||||
stylist.line_ending(),
|
stylist.line_ending(),
|
||||||
reason,
|
reason,
|
||||||
|
&suppressions,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -461,6 +469,9 @@ pub fn lint_only(
|
||||||
&indexer,
|
&indexer,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Parse range suppression comments
|
||||||
|
let suppressions = Suppressions::from_tokens(settings, locator.contents(), parsed.tokens());
|
||||||
|
|
||||||
// Generate diagnostics.
|
// Generate diagnostics.
|
||||||
let diagnostics = check_path(
|
let diagnostics = check_path(
|
||||||
path,
|
path,
|
||||||
|
|
@ -475,6 +486,7 @@ pub fn lint_only(
|
||||||
source_type,
|
source_type,
|
||||||
&parsed,
|
&parsed,
|
||||||
target_version,
|
target_version,
|
||||||
|
&suppressions,
|
||||||
);
|
);
|
||||||
|
|
||||||
LinterResult {
|
LinterResult {
|
||||||
|
|
@ -566,6 +578,9 @@ pub fn lint_fix<'a>(
|
||||||
&indexer,
|
&indexer,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Parse range suppression comments
|
||||||
|
let suppressions = Suppressions::from_tokens(settings, locator.contents(), parsed.tokens());
|
||||||
|
|
||||||
// Generate diagnostics.
|
// Generate diagnostics.
|
||||||
let diagnostics = check_path(
|
let diagnostics = check_path(
|
||||||
path,
|
path,
|
||||||
|
|
@ -580,6 +595,7 @@ pub fn lint_fix<'a>(
|
||||||
source_type,
|
source_type,
|
||||||
&parsed,
|
&parsed,
|
||||||
target_version,
|
target_version,
|
||||||
|
&suppressions,
|
||||||
);
|
);
|
||||||
|
|
||||||
if iterations == 0 {
|
if iterations == 0 {
|
||||||
|
|
@ -769,6 +785,7 @@ mod tests {
|
||||||
use crate::registry::Rule;
|
use crate::registry::Rule;
|
||||||
use crate::settings::LinterSettings;
|
use crate::settings::LinterSettings;
|
||||||
use crate::source_kind::SourceKind;
|
use crate::source_kind::SourceKind;
|
||||||
|
use crate::suppression::Suppressions;
|
||||||
use crate::test::{TestedNotebook, assert_notebook_path, test_contents, test_snippet};
|
use crate::test::{TestedNotebook, assert_notebook_path, test_contents, test_snippet};
|
||||||
use crate::{Locator, assert_diagnostics, directives, settings};
|
use crate::{Locator, assert_diagnostics, directives, settings};
|
||||||
|
|
||||||
|
|
@ -944,6 +961,7 @@ mod tests {
|
||||||
&locator,
|
&locator,
|
||||||
&indexer,
|
&indexer,
|
||||||
);
|
);
|
||||||
|
let suppressions = Suppressions::from_tokens(settings, locator.contents(), parsed.tokens());
|
||||||
let mut diagnostics = check_path(
|
let mut diagnostics = check_path(
|
||||||
path,
|
path,
|
||||||
None,
|
None,
|
||||||
|
|
@ -957,6 +975,7 @@ mod tests {
|
||||||
source_type,
|
source_type,
|
||||||
&parsed,
|
&parsed,
|
||||||
target_version,
|
target_version,
|
||||||
|
&suppressions,
|
||||||
);
|
);
|
||||||
diagnostics.sort_by(Diagnostic::ruff_start_ordering);
|
diagnostics.sort_by(Diagnostic::ruff_start_ordering);
|
||||||
diagnostics
|
diagnostics
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,14 @@ use crate::Locator;
|
||||||
use crate::fs::relativize_path;
|
use crate::fs::relativize_path;
|
||||||
use crate::registry::Rule;
|
use crate::registry::Rule;
|
||||||
use crate::rule_redirects::get_redirect_target;
|
use crate::rule_redirects::get_redirect_target;
|
||||||
|
use crate::suppression::Suppressions;
|
||||||
|
|
||||||
/// Generates an array of edits that matches the length of `messages`.
|
/// Generates an array of edits that matches the length of `messages`.
|
||||||
/// Each potential edit in the array is paired, in order, with the associated diagnostic.
|
/// Each potential edit in the array is paired, in order, with the associated diagnostic.
|
||||||
/// Each edit will add a `noqa` comment to the appropriate line in the source to hide
|
/// Each edit will add a `noqa` comment to the appropriate line in the source to hide
|
||||||
/// the diagnostic. These edits may conflict with each other and should not be applied
|
/// the diagnostic. These edits may conflict with each other and should not be applied
|
||||||
/// simultaneously.
|
/// simultaneously.
|
||||||
|
#[expect(clippy::too_many_arguments)]
|
||||||
pub fn generate_noqa_edits(
|
pub fn generate_noqa_edits(
|
||||||
path: &Path,
|
path: &Path,
|
||||||
diagnostics: &[Diagnostic],
|
diagnostics: &[Diagnostic],
|
||||||
|
|
@ -34,11 +36,19 @@ pub fn generate_noqa_edits(
|
||||||
external: &[String],
|
external: &[String],
|
||||||
noqa_line_for: &NoqaMapping,
|
noqa_line_for: &NoqaMapping,
|
||||||
line_ending: LineEnding,
|
line_ending: LineEnding,
|
||||||
|
suppressions: &Suppressions,
|
||||||
) -> Vec<Option<Edit>> {
|
) -> Vec<Option<Edit>> {
|
||||||
let file_directives = FileNoqaDirectives::extract(locator, comment_ranges, external, path);
|
let file_directives = FileNoqaDirectives::extract(locator, comment_ranges, external, path);
|
||||||
let exemption = FileExemption::from(&file_directives);
|
let exemption = FileExemption::from(&file_directives);
|
||||||
let directives = NoqaDirectives::from_commented_ranges(comment_ranges, external, path, locator);
|
let directives = NoqaDirectives::from_commented_ranges(comment_ranges, external, path, locator);
|
||||||
let comments = find_noqa_comments(diagnostics, locator, &exemption, &directives, noqa_line_for);
|
let comments = find_noqa_comments(
|
||||||
|
diagnostics,
|
||||||
|
locator,
|
||||||
|
&exemption,
|
||||||
|
&directives,
|
||||||
|
noqa_line_for,
|
||||||
|
suppressions,
|
||||||
|
);
|
||||||
build_noqa_edits_by_diagnostic(comments, locator, line_ending, None)
|
build_noqa_edits_by_diagnostic(comments, locator, line_ending, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -725,6 +735,7 @@ pub(crate) fn add_noqa(
|
||||||
noqa_line_for: &NoqaMapping,
|
noqa_line_for: &NoqaMapping,
|
||||||
line_ending: LineEnding,
|
line_ending: LineEnding,
|
||||||
reason: Option<&str>,
|
reason: Option<&str>,
|
||||||
|
suppressions: &Suppressions,
|
||||||
) -> Result<usize> {
|
) -> Result<usize> {
|
||||||
let (count, output) = add_noqa_inner(
|
let (count, output) = add_noqa_inner(
|
||||||
path,
|
path,
|
||||||
|
|
@ -735,6 +746,7 @@ pub(crate) fn add_noqa(
|
||||||
noqa_line_for,
|
noqa_line_for,
|
||||||
line_ending,
|
line_ending,
|
||||||
reason,
|
reason,
|
||||||
|
suppressions,
|
||||||
);
|
);
|
||||||
|
|
||||||
fs::write(path, output)?;
|
fs::write(path, output)?;
|
||||||
|
|
@ -751,6 +763,7 @@ fn add_noqa_inner(
|
||||||
noqa_line_for: &NoqaMapping,
|
noqa_line_for: &NoqaMapping,
|
||||||
line_ending: LineEnding,
|
line_ending: LineEnding,
|
||||||
reason: Option<&str>,
|
reason: Option<&str>,
|
||||||
|
suppressions: &Suppressions,
|
||||||
) -> (usize, String) {
|
) -> (usize, String) {
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
|
|
||||||
|
|
@ -760,7 +773,14 @@ fn add_noqa_inner(
|
||||||
|
|
||||||
let directives = NoqaDirectives::from_commented_ranges(comment_ranges, external, path, locator);
|
let directives = NoqaDirectives::from_commented_ranges(comment_ranges, external, path, locator);
|
||||||
|
|
||||||
let comments = find_noqa_comments(diagnostics, locator, &exemption, &directives, noqa_line_for);
|
let comments = find_noqa_comments(
|
||||||
|
diagnostics,
|
||||||
|
locator,
|
||||||
|
&exemption,
|
||||||
|
&directives,
|
||||||
|
noqa_line_for,
|
||||||
|
suppressions,
|
||||||
|
);
|
||||||
|
|
||||||
let edits = build_noqa_edits_by_line(comments, locator, line_ending, reason);
|
let edits = build_noqa_edits_by_line(comments, locator, line_ending, reason);
|
||||||
|
|
||||||
|
|
@ -859,6 +879,7 @@ fn find_noqa_comments<'a>(
|
||||||
exemption: &'a FileExemption,
|
exemption: &'a FileExemption,
|
||||||
directives: &'a NoqaDirectives,
|
directives: &'a NoqaDirectives,
|
||||||
noqa_line_for: &NoqaMapping,
|
noqa_line_for: &NoqaMapping,
|
||||||
|
suppressions: &'a Suppressions,
|
||||||
) -> Vec<Option<NoqaComment<'a>>> {
|
) -> Vec<Option<NoqaComment<'a>>> {
|
||||||
// List of noqa comments, ordered to match up with `messages`
|
// List of noqa comments, ordered to match up with `messages`
|
||||||
let mut comments_by_line: Vec<Option<NoqaComment<'a>>> = vec![];
|
let mut comments_by_line: Vec<Option<NoqaComment<'a>>> = vec![];
|
||||||
|
|
@ -875,6 +896,12 @@ fn find_noqa_comments<'a>(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply ranged suppressions next
|
||||||
|
if suppressions.check_diagnostic(message) {
|
||||||
|
comments_by_line.push(None);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Is the violation ignored by a `noqa` directive on the parent line?
|
// Is the violation ignored by a `noqa` directive on the parent line?
|
||||||
if let Some(parent) = message.parent() {
|
if let Some(parent) = message.parent() {
|
||||||
if let Some(directive_line) =
|
if let Some(directive_line) =
|
||||||
|
|
@ -1253,6 +1280,7 @@ mod tests {
|
||||||
use crate::rules::pycodestyle::rules::{AmbiguousVariableName, UselessSemicolon};
|
use crate::rules::pycodestyle::rules::{AmbiguousVariableName, UselessSemicolon};
|
||||||
use crate::rules::pyflakes::rules::UnusedVariable;
|
use crate::rules::pyflakes::rules::UnusedVariable;
|
||||||
use crate::rules::pyupgrade::rules::PrintfStringFormatting;
|
use crate::rules::pyupgrade::rules::PrintfStringFormatting;
|
||||||
|
use crate::suppression::Suppressions;
|
||||||
use crate::{Edit, Violation};
|
use crate::{Edit, Violation};
|
||||||
use crate::{Locator, generate_noqa_edits};
|
use crate::{Locator, generate_noqa_edits};
|
||||||
|
|
||||||
|
|
@ -2848,6 +2876,7 @@ mod tests {
|
||||||
&noqa_line_for,
|
&noqa_line_for,
|
||||||
LineEnding::Lf,
|
LineEnding::Lf,
|
||||||
None,
|
None,
|
||||||
|
&Suppressions::default(),
|
||||||
);
|
);
|
||||||
assert_eq!(count, 0);
|
assert_eq!(count, 0);
|
||||||
assert_eq!(output, format!("{contents}"));
|
assert_eq!(output, format!("{contents}"));
|
||||||
|
|
@ -2872,6 +2901,7 @@ mod tests {
|
||||||
&noqa_line_for,
|
&noqa_line_for,
|
||||||
LineEnding::Lf,
|
LineEnding::Lf,
|
||||||
None,
|
None,
|
||||||
|
&Suppressions::default(),
|
||||||
);
|
);
|
||||||
assert_eq!(count, 1);
|
assert_eq!(count, 1);
|
||||||
assert_eq!(output, "x = 1 # noqa: F841\n");
|
assert_eq!(output, "x = 1 # noqa: F841\n");
|
||||||
|
|
@ -2903,6 +2933,7 @@ mod tests {
|
||||||
&noqa_line_for,
|
&noqa_line_for,
|
||||||
LineEnding::Lf,
|
LineEnding::Lf,
|
||||||
None,
|
None,
|
||||||
|
&Suppressions::default(),
|
||||||
);
|
);
|
||||||
assert_eq!(count, 1);
|
assert_eq!(count, 1);
|
||||||
assert_eq!(output, "x = 1 # noqa: E741, F841\n");
|
assert_eq!(output, "x = 1 # noqa: E741, F841\n");
|
||||||
|
|
@ -2934,6 +2965,7 @@ mod tests {
|
||||||
&noqa_line_for,
|
&noqa_line_for,
|
||||||
LineEnding::Lf,
|
LineEnding::Lf,
|
||||||
None,
|
None,
|
||||||
|
&Suppressions::default(),
|
||||||
);
|
);
|
||||||
assert_eq!(count, 0);
|
assert_eq!(count, 0);
|
||||||
assert_eq!(output, "x = 1 # noqa");
|
assert_eq!(output, "x = 1 # noqa");
|
||||||
|
|
@ -2956,6 +2988,7 @@ print(
|
||||||
let messages = [PrintfStringFormatting
|
let messages = [PrintfStringFormatting
|
||||||
.into_diagnostic(TextRange::new(12.into(), 79.into()), &source_file)];
|
.into_diagnostic(TextRange::new(12.into(), 79.into()), &source_file)];
|
||||||
let comment_ranges = CommentRanges::default();
|
let comment_ranges = CommentRanges::default();
|
||||||
|
let suppressions = Suppressions::default();
|
||||||
let edits = generate_noqa_edits(
|
let edits = generate_noqa_edits(
|
||||||
path,
|
path,
|
||||||
&messages,
|
&messages,
|
||||||
|
|
@ -2964,6 +2997,7 @@ print(
|
||||||
&[],
|
&[],
|
||||||
&noqa_line_for,
|
&noqa_line_for,
|
||||||
LineEnding::Lf,
|
LineEnding::Lf,
|
||||||
|
&suppressions,
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edits,
|
edits,
|
||||||
|
|
@ -2987,6 +3021,7 @@ bar =
|
||||||
[UselessSemicolon.into_diagnostic(TextRange::new(4.into(), 5.into()), &source_file)];
|
[UselessSemicolon.into_diagnostic(TextRange::new(4.into(), 5.into()), &source_file)];
|
||||||
let noqa_line_for = NoqaMapping::default();
|
let noqa_line_for = NoqaMapping::default();
|
||||||
let comment_ranges = CommentRanges::default();
|
let comment_ranges = CommentRanges::default();
|
||||||
|
let suppressions = Suppressions::default();
|
||||||
let edits = generate_noqa_edits(
|
let edits = generate_noqa_edits(
|
||||||
path,
|
path,
|
||||||
&messages,
|
&messages,
|
||||||
|
|
@ -2995,6 +3030,7 @@ bar =
|
||||||
&[],
|
&[],
|
||||||
&noqa_line_for,
|
&noqa_line_for,
|
||||||
LineEnding::Lf,
|
LineEnding::Lf,
|
||||||
|
&suppressions,
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edits,
|
edits,
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,11 @@ use crate::settings::LinterSettings;
|
||||||
|
|
||||||
// Rule-specific behavior
|
// Rule-specific behavior
|
||||||
|
|
||||||
|
// https://github.com/astral-sh/ruff/pull/21382
|
||||||
|
pub(crate) const fn is_custom_exception_checking_enabled(settings: &LinterSettings) -> bool {
|
||||||
|
settings.preview.is_enabled()
|
||||||
|
}
|
||||||
|
|
||||||
// https://github.com/astral-sh/ruff/pull/15541
|
// https://github.com/astral-sh/ruff/pull/15541
|
||||||
pub(crate) const fn is_suspicious_function_reference_enabled(settings: &LinterSettings) -> bool {
|
pub(crate) const fn is_suspicious_function_reference_enabled(settings: &LinterSettings) -> bool {
|
||||||
settings.preview.is_enabled()
|
settings.preview.is_enabled()
|
||||||
|
|
@ -286,3 +291,8 @@ pub(crate) const fn is_s310_resolve_string_literal_bindings_enabled(
|
||||||
) -> bool {
|
) -> bool {
|
||||||
settings.preview.is_enabled()
|
settings.preview.is_enabled()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://github.com/astral-sh/ruff/pull/21623
|
||||||
|
pub(crate) const fn is_range_suppressions_enabled(settings: &LinterSettings) -> bool {
|
||||||
|
settings.preview.is_enabled()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -91,8 +91,8 @@ pub(crate) fn fastapi_redundant_response_model(checker: &Checker, function_def:
|
||||||
response_model_arg,
|
response_model_arg,
|
||||||
&call.arguments,
|
&call.arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
checker.locator().contents(),
|
checker.source(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)
|
)
|
||||||
.map(Fix::unsafe_edit)
|
.map(Fix::unsafe_edit)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -74,12 +74,7 @@ pub(crate) fn map_without_explicit_strict(checker: &Checker, call: &ast::ExprCal
|
||||||
checker
|
checker
|
||||||
.report_diagnostic(MapWithoutExplicitStrict, call.range())
|
.report_diagnostic(MapWithoutExplicitStrict, call.range())
|
||||||
.set_fix(Fix::applicable_edit(
|
.set_fix(Fix::applicable_edit(
|
||||||
add_argument(
|
add_argument("strict=False", &call.arguments, checker.tokens()),
|
||||||
"strict=False",
|
|
||||||
&call.arguments,
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
),
|
|
||||||
Applicability::Unsafe,
|
Applicability::Unsafe,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::fmt::Write;
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::helpers::is_docstring_stmt;
|
use ruff_python_ast::helpers::is_docstring_stmt;
|
||||||
use ruff_python_ast::name::QualifiedName;
|
use ruff_python_ast::name::QualifiedName;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{self as ast, Expr, ParameterWithDefault};
|
use ruff_python_ast::{self as ast, Expr, ParameterWithDefault};
|
||||||
use ruff_python_semantic::SemanticModel;
|
use ruff_python_semantic::SemanticModel;
|
||||||
use ruff_python_semantic::analyze::function_type::is_stub;
|
use ruff_python_semantic::analyze::function_type::is_stub;
|
||||||
|
|
@ -166,12 +166,7 @@ fn move_initialization(
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let range = match parenthesized_range(
|
let range = match parenthesized_range(default.into(), parameter.into(), checker.tokens()) {
|
||||||
default.into(),
|
|
||||||
parameter.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.source(),
|
|
||||||
) {
|
|
||||||
Some(range) => range,
|
Some(range) => range,
|
||||||
None => default.range(),
|
None => default.range(),
|
||||||
};
|
};
|
||||||
|
|
@ -194,13 +189,8 @@ fn move_initialization(
|
||||||
"{} = {}",
|
"{} = {}",
|
||||||
parameter.parameter.name(),
|
parameter.parameter.name(),
|
||||||
locator.slice(
|
locator.slice(
|
||||||
parenthesized_range(
|
parenthesized_range(default.into(), parameter.into(), checker.tokens())
|
||||||
default.into(),
|
.unwrap_or(default.range())
|
||||||
parameter.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.source()
|
|
||||||
)
|
|
||||||
.unwrap_or(default.range())
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -92,12 +92,7 @@ pub(crate) fn no_explicit_stacklevel(checker: &Checker, call: &ast::ExprCall) {
|
||||||
}
|
}
|
||||||
let mut diagnostic = checker.report_diagnostic(NoExplicitStacklevel, call.func.range());
|
let mut diagnostic = checker.report_diagnostic(NoExplicitStacklevel, call.func.range());
|
||||||
|
|
||||||
let edit = add_argument(
|
let edit = add_argument("stacklevel=2", &call.arguments, checker.tokens());
|
||||||
"stacklevel=2",
|
|
||||||
&call.arguments,
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
);
|
|
||||||
|
|
||||||
diagnostic.set_fix(Fix::unsafe_edit(edit));
|
diagnostic.set_fix(Fix::unsafe_edit(edit));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,12 +70,7 @@ pub(crate) fn zip_without_explicit_strict(checker: &Checker, call: &ast::ExprCal
|
||||||
checker
|
checker
|
||||||
.report_diagnostic(ZipWithoutExplicitStrict, call.range())
|
.report_diagnostic(ZipWithoutExplicitStrict, call.range())
|
||||||
.set_fix(Fix::applicable_edit(
|
.set_fix(Fix::applicable_edit(
|
||||||
add_argument(
|
add_argument("strict=False", &call.arguments, checker.tokens()),
|
||||||
"strict=False",
|
|
||||||
&call.arguments,
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
),
|
|
||||||
Applicability::Unsafe,
|
Applicability::Unsafe,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -236,227 +236,227 @@ help: Replace with `None`; initialize within function
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
B006 [*] Do not use mutable data structures for argument defaults
|
B006 [*] Do not use mutable data structures for argument defaults
|
||||||
--> B006_B008.py:239:20
|
--> B006_B008.py:242:20
|
||||||
|
|
|
|
||||||
237 | # B006 and B008
|
240 | # B006 and B008
|
||||||
238 | # We should handle arbitrary nesting of these B008.
|
241 | # We should handle arbitrary nesting of these B008.
|
||||||
239 | def nested_combo(a=[float(3), dt.datetime.now()]):
|
242 | def nested_combo(a=[float(3), dt.datetime.now()]):
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
240 | pass
|
243 | pass
|
||||||
|
|
|
|
||||||
help: Replace with `None`; initialize within function
|
help: Replace with `None`; initialize within function
|
||||||
236 |
|
239 |
|
||||||
237 | # B006 and B008
|
240 | # B006 and B008
|
||||||
238 | # We should handle arbitrary nesting of these B008.
|
241 | # We should handle arbitrary nesting of these B008.
|
||||||
- def nested_combo(a=[float(3), dt.datetime.now()]):
|
- def nested_combo(a=[float(3), dt.datetime.now()]):
|
||||||
239 + def nested_combo(a=None):
|
242 + def nested_combo(a=None):
|
||||||
240 | pass
|
243 | pass
|
||||||
241 |
|
244 |
|
||||||
242 |
|
245 |
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
B006 [*] Do not use mutable data structures for argument defaults
|
B006 [*] Do not use mutable data structures for argument defaults
|
||||||
--> B006_B008.py:276:27
|
--> B006_B008.py:279:27
|
||||||
|
|
|
|
||||||
275 | def mutable_annotations(
|
278 | def mutable_annotations(
|
||||||
276 | a: list[int] | None = [],
|
279 | a: list[int] | None = [],
|
||||||
| ^^
|
| ^^
|
||||||
277 | b: Optional[Dict[int, int]] = {},
|
280 | b: Optional[Dict[int, int]] = {},
|
||||||
278 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
281 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
|
|
|
|
||||||
help: Replace with `None`; initialize within function
|
help: Replace with `None`; initialize within function
|
||||||
273 |
|
276 |
|
||||||
274 |
|
277 |
|
||||||
275 | def mutable_annotations(
|
278 | def mutable_annotations(
|
||||||
- a: list[int] | None = [],
|
- a: list[int] | None = [],
|
||||||
276 + a: list[int] | None = None,
|
279 + a: list[int] | None = None,
|
||||||
277 | b: Optional[Dict[int, int]] = {},
|
280 | b: Optional[Dict[int, int]] = {},
|
||||||
278 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
281 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
279 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
282 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
B006 [*] Do not use mutable data structures for argument defaults
|
B006 [*] Do not use mutable data structures for argument defaults
|
||||||
--> B006_B008.py:277:35
|
--> B006_B008.py:280:35
|
||||||
|
|
|
|
||||||
275 | def mutable_annotations(
|
278 | def mutable_annotations(
|
||||||
276 | a: list[int] | None = [],
|
279 | a: list[int] | None = [],
|
||||||
277 | b: Optional[Dict[int, int]] = {},
|
280 | b: Optional[Dict[int, int]] = {},
|
||||||
| ^^
|
| ^^
|
||||||
278 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
281 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
279 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
282 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
|
|
|
|
||||||
help: Replace with `None`; initialize within function
|
help: Replace with `None`; initialize within function
|
||||||
274 |
|
277 |
|
||||||
275 | def mutable_annotations(
|
278 | def mutable_annotations(
|
||||||
276 | a: list[int] | None = [],
|
279 | a: list[int] | None = [],
|
||||||
- b: Optional[Dict[int, int]] = {},
|
- b: Optional[Dict[int, int]] = {},
|
||||||
277 + b: Optional[Dict[int, int]] = None,
|
280 + b: Optional[Dict[int, int]] = None,
|
||||||
278 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
281 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
279 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
282 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
280 | ):
|
283 | ):
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
B006 [*] Do not use mutable data structures for argument defaults
|
B006 [*] Do not use mutable data structures for argument defaults
|
||||||
--> B006_B008.py:278:62
|
--> B006_B008.py:281:62
|
||||||
|
|
|
|
||||||
276 | a: list[int] | None = [],
|
279 | a: list[int] | None = [],
|
||||||
277 | b: Optional[Dict[int, int]] = {},
|
280 | b: Optional[Dict[int, int]] = {},
|
||||||
278 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
281 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
| ^^^^^
|
| ^^^^^
|
||||||
279 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
282 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
280 | ):
|
283 | ):
|
||||||
|
|
|
|
||||||
help: Replace with `None`; initialize within function
|
help: Replace with `None`; initialize within function
|
||||||
275 | def mutable_annotations(
|
278 | def mutable_annotations(
|
||||||
276 | a: list[int] | None = [],
|
279 | a: list[int] | None = [],
|
||||||
277 | b: Optional[Dict[int, int]] = {},
|
280 | b: Optional[Dict[int, int]] = {},
|
||||||
- c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
- c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
278 + c: Annotated[Union[Set[str], abc.Sized], "annotation"] = None,
|
281 + c: Annotated[Union[Set[str], abc.Sized], "annotation"] = None,
|
||||||
279 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
282 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
280 | ):
|
283 | ):
|
||||||
281 | pass
|
284 | pass
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
B006 [*] Do not use mutable data structures for argument defaults
|
B006 [*] Do not use mutable data structures for argument defaults
|
||||||
--> B006_B008.py:279:80
|
--> B006_B008.py:282:80
|
||||||
|
|
|
|
||||||
277 | b: Optional[Dict[int, int]] = {},
|
280 | b: Optional[Dict[int, int]] = {},
|
||||||
278 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
281 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
279 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
282 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
| ^^^^^
|
| ^^^^^
|
||||||
280 | ):
|
283 | ):
|
||||||
281 | pass
|
284 | pass
|
||||||
|
|
|
|
||||||
help: Replace with `None`; initialize within function
|
help: Replace with `None`; initialize within function
|
||||||
276 | a: list[int] | None = [],
|
279 | a: list[int] | None = [],
|
||||||
277 | b: Optional[Dict[int, int]] = {},
|
280 | b: Optional[Dict[int, int]] = {},
|
||||||
278 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
281 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
- d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
- d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
279 + d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = None,
|
282 + d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = None,
|
||||||
280 | ):
|
283 | ):
|
||||||
281 | pass
|
284 | pass
|
||||||
282 |
|
285 |
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
B006 [*] Do not use mutable data structures for argument defaults
|
B006 [*] Do not use mutable data structures for argument defaults
|
||||||
--> B006_B008.py:284:52
|
--> B006_B008.py:287:52
|
||||||
|
|
|
|
||||||
284 | def single_line_func_wrong(value: dict[str, str] = {}):
|
287 | def single_line_func_wrong(value: dict[str, str] = {}):
|
||||||
| ^^
|
| ^^
|
||||||
285 | """Docstring"""
|
288 | """Docstring"""
|
||||||
|
|
|
|
||||||
help: Replace with `None`; initialize within function
|
help: Replace with `None`; initialize within function
|
||||||
281 | pass
|
284 | pass
|
||||||
282 |
|
285 |
|
||||||
283 |
|
|
||||||
- def single_line_func_wrong(value: dict[str, str] = {}):
|
|
||||||
284 + def single_line_func_wrong(value: dict[str, str] = None):
|
|
||||||
285 | """Docstring"""
|
|
||||||
286 |
|
286 |
|
||||||
287 |
|
- def single_line_func_wrong(value: dict[str, str] = {}):
|
||||||
|
287 + def single_line_func_wrong(value: dict[str, str] = None):
|
||||||
|
288 | """Docstring"""
|
||||||
|
289 |
|
||||||
|
290 |
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
B006 [*] Do not use mutable data structures for argument defaults
|
B006 [*] Do not use mutable data structures for argument defaults
|
||||||
--> B006_B008.py:288:52
|
--> B006_B008.py:291:52
|
||||||
|
|
|
|
||||||
288 | def single_line_func_wrong(value: dict[str, str] = {}):
|
291 | def single_line_func_wrong(value: dict[str, str] = {}):
|
||||||
| ^^
|
| ^^
|
||||||
289 | """Docstring"""
|
292 | """Docstring"""
|
||||||
290 | ...
|
293 | ...
|
||||||
|
|
|
|
||||||
help: Replace with `None`; initialize within function
|
help: Replace with `None`; initialize within function
|
||||||
285 | """Docstring"""
|
288 | """Docstring"""
|
||||||
286 |
|
289 |
|
||||||
287 |
|
290 |
|
||||||
- def single_line_func_wrong(value: dict[str, str] = {}):
|
- def single_line_func_wrong(value: dict[str, str] = {}):
|
||||||
288 + def single_line_func_wrong(value: dict[str, str] = None):
|
291 + def single_line_func_wrong(value: dict[str, str] = None):
|
||||||
289 | """Docstring"""
|
292 | """Docstring"""
|
||||||
290 | ...
|
293 | ...
|
||||||
291 |
|
294 |
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
B006 [*] Do not use mutable data structures for argument defaults
|
B006 [*] Do not use mutable data structures for argument defaults
|
||||||
--> B006_B008.py:293:52
|
--> B006_B008.py:296:52
|
||||||
|
|
|
|
||||||
293 | def single_line_func_wrong(value: dict[str, str] = {}):
|
296 | def single_line_func_wrong(value: dict[str, str] = {}):
|
||||||
| ^^
|
| ^^
|
||||||
294 | """Docstring"""; ...
|
297 | """Docstring"""; ...
|
||||||
|
|
|
|
||||||
help: Replace with `None`; initialize within function
|
help: Replace with `None`; initialize within function
|
||||||
290 | ...
|
293 | ...
|
||||||
291 |
|
294 |
|
||||||
292 |
|
|
||||||
- def single_line_func_wrong(value: dict[str, str] = {}):
|
|
||||||
293 + def single_line_func_wrong(value: dict[str, str] = None):
|
|
||||||
294 | """Docstring"""; ...
|
|
||||||
295 |
|
295 |
|
||||||
296 |
|
- def single_line_func_wrong(value: dict[str, str] = {}):
|
||||||
|
296 + def single_line_func_wrong(value: dict[str, str] = None):
|
||||||
|
297 | """Docstring"""; ...
|
||||||
|
298 |
|
||||||
|
299 |
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
B006 [*] Do not use mutable data structures for argument defaults
|
B006 [*] Do not use mutable data structures for argument defaults
|
||||||
--> B006_B008.py:297:52
|
--> B006_B008.py:300:52
|
||||||
|
|
|
|
||||||
297 | def single_line_func_wrong(value: dict[str, str] = {}):
|
300 | def single_line_func_wrong(value: dict[str, str] = {}):
|
||||||
| ^^
|
| ^^
|
||||||
298 | """Docstring"""; \
|
301 | """Docstring"""; \
|
||||||
299 | ...
|
302 | ...
|
||||||
|
|
|
|
||||||
help: Replace with `None`; initialize within function
|
help: Replace with `None`; initialize within function
|
||||||
294 | """Docstring"""; ...
|
297 | """Docstring"""; ...
|
||||||
295 |
|
298 |
|
||||||
296 |
|
299 |
|
||||||
- def single_line_func_wrong(value: dict[str, str] = {}):
|
- def single_line_func_wrong(value: dict[str, str] = {}):
|
||||||
297 + def single_line_func_wrong(value: dict[str, str] = None):
|
300 + def single_line_func_wrong(value: dict[str, str] = None):
|
||||||
298 | """Docstring"""; \
|
301 | """Docstring"""; \
|
||||||
299 | ...
|
302 | ...
|
||||||
300 |
|
303 |
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
B006 [*] Do not use mutable data structures for argument defaults
|
B006 [*] Do not use mutable data structures for argument defaults
|
||||||
--> B006_B008.py:302:52
|
--> B006_B008.py:305:52
|
||||||
|
|
|
|
||||||
302 | def single_line_func_wrong(value: dict[str, str] = {
|
305 | def single_line_func_wrong(value: dict[str, str] = {
|
||||||
| ____________________________________________________^
|
| ____________________________________________________^
|
||||||
303 | | # This is a comment
|
306 | | # This is a comment
|
||||||
304 | | }):
|
307 | | }):
|
||||||
| |_^
|
| |_^
|
||||||
305 | """Docstring"""
|
308 | """Docstring"""
|
||||||
|
|
|
|
||||||
help: Replace with `None`; initialize within function
|
help: Replace with `None`; initialize within function
|
||||||
299 | ...
|
302 | ...
|
||||||
300 |
|
303 |
|
||||||
301 |
|
304 |
|
||||||
- def single_line_func_wrong(value: dict[str, str] = {
|
- def single_line_func_wrong(value: dict[str, str] = {
|
||||||
- # This is a comment
|
- # This is a comment
|
||||||
- }):
|
- }):
|
||||||
302 + def single_line_func_wrong(value: dict[str, str] = None):
|
305 + def single_line_func_wrong(value: dict[str, str] = None):
|
||||||
303 | """Docstring"""
|
306 | """Docstring"""
|
||||||
304 |
|
307 |
|
||||||
305 |
|
308 |
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
B006 Do not use mutable data structures for argument defaults
|
B006 Do not use mutable data structures for argument defaults
|
||||||
--> B006_B008.py:308:52
|
--> B006_B008.py:311:52
|
||||||
|
|
|
|
||||||
308 | def single_line_func_wrong(value: dict[str, str] = {}) \
|
311 | def single_line_func_wrong(value: dict[str, str] = {}) \
|
||||||
| ^^
|
| ^^
|
||||||
309 | : \
|
312 | : \
|
||||||
310 | """Docstring"""
|
313 | """Docstring"""
|
||||||
|
|
|
|
||||||
help: Replace with `None`; initialize within function
|
help: Replace with `None`; initialize within function
|
||||||
|
|
||||||
B006 [*] Do not use mutable data structures for argument defaults
|
B006 [*] Do not use mutable data structures for argument defaults
|
||||||
--> B006_B008.py:313:52
|
--> B006_B008.py:316:52
|
||||||
|
|
|
|
||||||
313 | def single_line_func_wrong(value: dict[str, str] = {}):
|
316 | def single_line_func_wrong(value: dict[str, str] = {}):
|
||||||
| ^^
|
| ^^
|
||||||
314 | """Docstring without newline"""
|
317 | """Docstring without newline"""
|
||||||
|
|
|
|
||||||
help: Replace with `None`; initialize within function
|
help: Replace with `None`; initialize within function
|
||||||
310 | """Docstring"""
|
313 | """Docstring"""
|
||||||
311 |
|
314 |
|
||||||
312 |
|
315 |
|
||||||
- def single_line_func_wrong(value: dict[str, str] = {}):
|
- def single_line_func_wrong(value: dict[str, str] = {}):
|
||||||
313 + def single_line_func_wrong(value: dict[str, str] = None):
|
316 + def single_line_func_wrong(value: dict[str, str] = None):
|
||||||
314 | """Docstring without newline"""
|
317 | """Docstring without newline"""
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
|
||||||
|
|
@ -53,39 +53,39 @@ B008 Do not perform function call in argument defaults; instead, perform the cal
|
||||||
|
|
|
|
||||||
|
|
||||||
B008 Do not perform function call `dt.datetime.now` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
|
B008 Do not perform function call `dt.datetime.now` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
|
||||||
--> B006_B008.py:239:31
|
--> B006_B008.py:242:31
|
||||||
|
|
|
|
||||||
237 | # B006 and B008
|
240 | # B006 and B008
|
||||||
238 | # We should handle arbitrary nesting of these B008.
|
241 | # We should handle arbitrary nesting of these B008.
|
||||||
239 | def nested_combo(a=[float(3), dt.datetime.now()]):
|
242 | def nested_combo(a=[float(3), dt.datetime.now()]):
|
||||||
| ^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^
|
||||||
240 | pass
|
243 | pass
|
||||||
|
|
|
|
||||||
|
|
||||||
B008 Do not perform function call `map` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
|
B008 Do not perform function call `map` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
|
||||||
--> B006_B008.py:245:22
|
--> B006_B008.py:248:22
|
||||||
|
|
|
|
||||||
243 | # Don't flag nested B006 since we can't guarantee that
|
246 | # Don't flag nested B006 since we can't guarantee that
|
||||||
244 | # it isn't made mutable by the outer operation.
|
247 | # it isn't made mutable by the outer operation.
|
||||||
245 | def no_nested_b006(a=map(lambda s: s.upper(), ["a", "b", "c"])):
|
248 | def no_nested_b006(a=map(lambda s: s.upper(), ["a", "b", "c"])):
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
246 | pass
|
249 | pass
|
||||||
|
|
|
|
||||||
|
|
||||||
B008 Do not perform function call `random.randint` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
|
B008 Do not perform function call `random.randint` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
|
||||||
--> B006_B008.py:250:19
|
--> B006_B008.py:253:19
|
||||||
|
|
|
|
||||||
249 | # B008-ception.
|
252 | # B008-ception.
|
||||||
250 | def nested_b008(a=random.randint(0, dt.datetime.now().year)):
|
253 | def nested_b008(a=random.randint(0, dt.datetime.now().year)):
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
251 | pass
|
254 | pass
|
||||||
|
|
|
|
||||||
|
|
||||||
B008 Do not perform function call `dt.datetime.now` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
|
B008 Do not perform function call `dt.datetime.now` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
|
||||||
--> B006_B008.py:250:37
|
--> B006_B008.py:253:37
|
||||||
|
|
|
|
||||||
249 | # B008-ception.
|
252 | # B008-ception.
|
||||||
250 | def nested_b008(a=random.randint(0, dt.datetime.now().year)):
|
253 | def nested_b008(a=random.randint(0, dt.datetime.now().year)):
|
||||||
| ^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^
|
||||||
251 | pass
|
254 | pass
|
||||||
|
|
|
|
||||||
|
|
|
||||||
|
|
@ -236,227 +236,227 @@ help: Replace with `None`; initialize within function
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
B006 [*] Do not use mutable data structures for argument defaults
|
B006 [*] Do not use mutable data structures for argument defaults
|
||||||
--> B006_B008.py:239:20
|
--> B006_B008.py:242:20
|
||||||
|
|
|
|
||||||
237 | # B006 and B008
|
240 | # B006 and B008
|
||||||
238 | # We should handle arbitrary nesting of these B008.
|
241 | # We should handle arbitrary nesting of these B008.
|
||||||
239 | def nested_combo(a=[float(3), dt.datetime.now()]):
|
242 | def nested_combo(a=[float(3), dt.datetime.now()]):
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
240 | pass
|
243 | pass
|
||||||
|
|
|
|
||||||
help: Replace with `None`; initialize within function
|
help: Replace with `None`; initialize within function
|
||||||
236 |
|
239 |
|
||||||
237 | # B006 and B008
|
240 | # B006 and B008
|
||||||
238 | # We should handle arbitrary nesting of these B008.
|
241 | # We should handle arbitrary nesting of these B008.
|
||||||
- def nested_combo(a=[float(3), dt.datetime.now()]):
|
- def nested_combo(a=[float(3), dt.datetime.now()]):
|
||||||
239 + def nested_combo(a=None):
|
242 + def nested_combo(a=None):
|
||||||
240 | pass
|
243 | pass
|
||||||
241 |
|
244 |
|
||||||
242 |
|
245 |
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
B006 [*] Do not use mutable data structures for argument defaults
|
B006 [*] Do not use mutable data structures for argument defaults
|
||||||
--> B006_B008.py:276:27
|
--> B006_B008.py:279:27
|
||||||
|
|
|
|
||||||
275 | def mutable_annotations(
|
278 | def mutable_annotations(
|
||||||
276 | a: list[int] | None = [],
|
279 | a: list[int] | None = [],
|
||||||
| ^^
|
| ^^
|
||||||
277 | b: Optional[Dict[int, int]] = {},
|
280 | b: Optional[Dict[int, int]] = {},
|
||||||
278 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
281 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
|
|
|
|
||||||
help: Replace with `None`; initialize within function
|
help: Replace with `None`; initialize within function
|
||||||
273 |
|
276 |
|
||||||
274 |
|
277 |
|
||||||
275 | def mutable_annotations(
|
278 | def mutable_annotations(
|
||||||
- a: list[int] | None = [],
|
- a: list[int] | None = [],
|
||||||
276 + a: list[int] | None = None,
|
279 + a: list[int] | None = None,
|
||||||
277 | b: Optional[Dict[int, int]] = {},
|
280 | b: Optional[Dict[int, int]] = {},
|
||||||
278 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
281 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
279 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
282 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
B006 [*] Do not use mutable data structures for argument defaults
|
B006 [*] Do not use mutable data structures for argument defaults
|
||||||
--> B006_B008.py:277:35
|
--> B006_B008.py:280:35
|
||||||
|
|
|
|
||||||
275 | def mutable_annotations(
|
278 | def mutable_annotations(
|
||||||
276 | a: list[int] | None = [],
|
279 | a: list[int] | None = [],
|
||||||
277 | b: Optional[Dict[int, int]] = {},
|
280 | b: Optional[Dict[int, int]] = {},
|
||||||
| ^^
|
| ^^
|
||||||
278 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
281 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
279 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
282 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
|
|
|
|
||||||
help: Replace with `None`; initialize within function
|
help: Replace with `None`; initialize within function
|
||||||
274 |
|
277 |
|
||||||
275 | def mutable_annotations(
|
278 | def mutable_annotations(
|
||||||
276 | a: list[int] | None = [],
|
279 | a: list[int] | None = [],
|
||||||
- b: Optional[Dict[int, int]] = {},
|
- b: Optional[Dict[int, int]] = {},
|
||||||
277 + b: Optional[Dict[int, int]] = None,
|
280 + b: Optional[Dict[int, int]] = None,
|
||||||
278 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
281 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
279 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
282 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
280 | ):
|
283 | ):
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
B006 [*] Do not use mutable data structures for argument defaults
|
B006 [*] Do not use mutable data structures for argument defaults
|
||||||
--> B006_B008.py:278:62
|
--> B006_B008.py:281:62
|
||||||
|
|
|
|
||||||
276 | a: list[int] | None = [],
|
279 | a: list[int] | None = [],
|
||||||
277 | b: Optional[Dict[int, int]] = {},
|
280 | b: Optional[Dict[int, int]] = {},
|
||||||
278 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
281 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
| ^^^^^
|
| ^^^^^
|
||||||
279 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
282 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
280 | ):
|
283 | ):
|
||||||
|
|
|
|
||||||
help: Replace with `None`; initialize within function
|
help: Replace with `None`; initialize within function
|
||||||
275 | def mutable_annotations(
|
278 | def mutable_annotations(
|
||||||
276 | a: list[int] | None = [],
|
279 | a: list[int] | None = [],
|
||||||
277 | b: Optional[Dict[int, int]] = {},
|
280 | b: Optional[Dict[int, int]] = {},
|
||||||
- c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
- c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
278 + c: Annotated[Union[Set[str], abc.Sized], "annotation"] = None,
|
281 + c: Annotated[Union[Set[str], abc.Sized], "annotation"] = None,
|
||||||
279 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
282 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
280 | ):
|
283 | ):
|
||||||
281 | pass
|
284 | pass
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
B006 [*] Do not use mutable data structures for argument defaults
|
B006 [*] Do not use mutable data structures for argument defaults
|
||||||
--> B006_B008.py:279:80
|
--> B006_B008.py:282:80
|
||||||
|
|
|
|
||||||
277 | b: Optional[Dict[int, int]] = {},
|
280 | b: Optional[Dict[int, int]] = {},
|
||||||
278 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
281 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
279 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
282 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
| ^^^^^
|
| ^^^^^
|
||||||
280 | ):
|
283 | ):
|
||||||
281 | pass
|
284 | pass
|
||||||
|
|
|
|
||||||
help: Replace with `None`; initialize within function
|
help: Replace with `None`; initialize within function
|
||||||
276 | a: list[int] | None = [],
|
279 | a: list[int] | None = [],
|
||||||
277 | b: Optional[Dict[int, int]] = {},
|
280 | b: Optional[Dict[int, int]] = {},
|
||||||
278 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
281 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
- d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
- d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||||
279 + d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = None,
|
282 + d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = None,
|
||||||
280 | ):
|
283 | ):
|
||||||
281 | pass
|
284 | pass
|
||||||
282 |
|
285 |
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
B006 [*] Do not use mutable data structures for argument defaults
|
B006 [*] Do not use mutable data structures for argument defaults
|
||||||
--> B006_B008.py:284:52
|
--> B006_B008.py:287:52
|
||||||
|
|
|
|
||||||
284 | def single_line_func_wrong(value: dict[str, str] = {}):
|
287 | def single_line_func_wrong(value: dict[str, str] = {}):
|
||||||
| ^^
|
| ^^
|
||||||
285 | """Docstring"""
|
288 | """Docstring"""
|
||||||
|
|
|
|
||||||
help: Replace with `None`; initialize within function
|
help: Replace with `None`; initialize within function
|
||||||
281 | pass
|
284 | pass
|
||||||
282 |
|
285 |
|
||||||
283 |
|
|
||||||
- def single_line_func_wrong(value: dict[str, str] = {}):
|
|
||||||
284 + def single_line_func_wrong(value: dict[str, str] = None):
|
|
||||||
285 | """Docstring"""
|
|
||||||
286 |
|
286 |
|
||||||
287 |
|
- def single_line_func_wrong(value: dict[str, str] = {}):
|
||||||
|
287 + def single_line_func_wrong(value: dict[str, str] = None):
|
||||||
|
288 | """Docstring"""
|
||||||
|
289 |
|
||||||
|
290 |
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
B006 [*] Do not use mutable data structures for argument defaults
|
B006 [*] Do not use mutable data structures for argument defaults
|
||||||
--> B006_B008.py:288:52
|
--> B006_B008.py:291:52
|
||||||
|
|
|
|
||||||
288 | def single_line_func_wrong(value: dict[str, str] = {}):
|
291 | def single_line_func_wrong(value: dict[str, str] = {}):
|
||||||
| ^^
|
| ^^
|
||||||
289 | """Docstring"""
|
292 | """Docstring"""
|
||||||
290 | ...
|
293 | ...
|
||||||
|
|
|
|
||||||
help: Replace with `None`; initialize within function
|
help: Replace with `None`; initialize within function
|
||||||
285 | """Docstring"""
|
288 | """Docstring"""
|
||||||
286 |
|
289 |
|
||||||
287 |
|
290 |
|
||||||
- def single_line_func_wrong(value: dict[str, str] = {}):
|
- def single_line_func_wrong(value: dict[str, str] = {}):
|
||||||
288 + def single_line_func_wrong(value: dict[str, str] = None):
|
291 + def single_line_func_wrong(value: dict[str, str] = None):
|
||||||
289 | """Docstring"""
|
292 | """Docstring"""
|
||||||
290 | ...
|
293 | ...
|
||||||
291 |
|
294 |
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
B006 [*] Do not use mutable data structures for argument defaults
|
B006 [*] Do not use mutable data structures for argument defaults
|
||||||
--> B006_B008.py:293:52
|
--> B006_B008.py:296:52
|
||||||
|
|
|
|
||||||
293 | def single_line_func_wrong(value: dict[str, str] = {}):
|
296 | def single_line_func_wrong(value: dict[str, str] = {}):
|
||||||
| ^^
|
| ^^
|
||||||
294 | """Docstring"""; ...
|
297 | """Docstring"""; ...
|
||||||
|
|
|
|
||||||
help: Replace with `None`; initialize within function
|
help: Replace with `None`; initialize within function
|
||||||
290 | ...
|
293 | ...
|
||||||
291 |
|
294 |
|
||||||
292 |
|
|
||||||
- def single_line_func_wrong(value: dict[str, str] = {}):
|
|
||||||
293 + def single_line_func_wrong(value: dict[str, str] = None):
|
|
||||||
294 | """Docstring"""; ...
|
|
||||||
295 |
|
295 |
|
||||||
296 |
|
- def single_line_func_wrong(value: dict[str, str] = {}):
|
||||||
|
296 + def single_line_func_wrong(value: dict[str, str] = None):
|
||||||
|
297 | """Docstring"""; ...
|
||||||
|
298 |
|
||||||
|
299 |
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
B006 [*] Do not use mutable data structures for argument defaults
|
B006 [*] Do not use mutable data structures for argument defaults
|
||||||
--> B006_B008.py:297:52
|
--> B006_B008.py:300:52
|
||||||
|
|
|
|
||||||
297 | def single_line_func_wrong(value: dict[str, str] = {}):
|
300 | def single_line_func_wrong(value: dict[str, str] = {}):
|
||||||
| ^^
|
| ^^
|
||||||
298 | """Docstring"""; \
|
301 | """Docstring"""; \
|
||||||
299 | ...
|
302 | ...
|
||||||
|
|
|
|
||||||
help: Replace with `None`; initialize within function
|
help: Replace with `None`; initialize within function
|
||||||
294 | """Docstring"""; ...
|
297 | """Docstring"""; ...
|
||||||
295 |
|
298 |
|
||||||
296 |
|
299 |
|
||||||
- def single_line_func_wrong(value: dict[str, str] = {}):
|
- def single_line_func_wrong(value: dict[str, str] = {}):
|
||||||
297 + def single_line_func_wrong(value: dict[str, str] = None):
|
300 + def single_line_func_wrong(value: dict[str, str] = None):
|
||||||
298 | """Docstring"""; \
|
301 | """Docstring"""; \
|
||||||
299 | ...
|
302 | ...
|
||||||
300 |
|
303 |
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
B006 [*] Do not use mutable data structures for argument defaults
|
B006 [*] Do not use mutable data structures for argument defaults
|
||||||
--> B006_B008.py:302:52
|
--> B006_B008.py:305:52
|
||||||
|
|
|
|
||||||
302 | def single_line_func_wrong(value: dict[str, str] = {
|
305 | def single_line_func_wrong(value: dict[str, str] = {
|
||||||
| ____________________________________________________^
|
| ____________________________________________________^
|
||||||
303 | | # This is a comment
|
306 | | # This is a comment
|
||||||
304 | | }):
|
307 | | }):
|
||||||
| |_^
|
| |_^
|
||||||
305 | """Docstring"""
|
308 | """Docstring"""
|
||||||
|
|
|
|
||||||
help: Replace with `None`; initialize within function
|
help: Replace with `None`; initialize within function
|
||||||
299 | ...
|
302 | ...
|
||||||
300 |
|
303 |
|
||||||
301 |
|
304 |
|
||||||
- def single_line_func_wrong(value: dict[str, str] = {
|
- def single_line_func_wrong(value: dict[str, str] = {
|
||||||
- # This is a comment
|
- # This is a comment
|
||||||
- }):
|
- }):
|
||||||
302 + def single_line_func_wrong(value: dict[str, str] = None):
|
305 + def single_line_func_wrong(value: dict[str, str] = None):
|
||||||
303 | """Docstring"""
|
306 | """Docstring"""
|
||||||
304 |
|
307 |
|
||||||
305 |
|
308 |
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
B006 Do not use mutable data structures for argument defaults
|
B006 Do not use mutable data structures for argument defaults
|
||||||
--> B006_B008.py:308:52
|
--> B006_B008.py:311:52
|
||||||
|
|
|
|
||||||
308 | def single_line_func_wrong(value: dict[str, str] = {}) \
|
311 | def single_line_func_wrong(value: dict[str, str] = {}) \
|
||||||
| ^^
|
| ^^
|
||||||
309 | : \
|
312 | : \
|
||||||
310 | """Docstring"""
|
313 | """Docstring"""
|
||||||
|
|
|
|
||||||
help: Replace with `None`; initialize within function
|
help: Replace with `None`; initialize within function
|
||||||
|
|
||||||
B006 [*] Do not use mutable data structures for argument defaults
|
B006 [*] Do not use mutable data structures for argument defaults
|
||||||
--> B006_B008.py:313:52
|
--> B006_B008.py:316:52
|
||||||
|
|
|
|
||||||
313 | def single_line_func_wrong(value: dict[str, str] = {}):
|
316 | def single_line_func_wrong(value: dict[str, str] = {}):
|
||||||
| ^^
|
| ^^
|
||||||
314 | """Docstring without newline"""
|
317 | """Docstring without newline"""
|
||||||
|
|
|
|
||||||
help: Replace with `None`; initialize within function
|
help: Replace with `None`; initialize within function
|
||||||
310 | """Docstring"""
|
313 | """Docstring"""
|
||||||
311 |
|
314 |
|
||||||
312 |
|
315 |
|
||||||
- def single_line_func_wrong(value: dict[str, str] = {}):
|
- def single_line_func_wrong(value: dict[str, str] = {}):
|
||||||
313 + def single_line_func_wrong(value: dict[str, str] = None):
|
316 + def single_line_func_wrong(value: dict[str, str] = None):
|
||||||
314 | """Docstring without newline"""
|
317 | """Docstring without newline"""
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
use ruff_python_ast::ExprGenerator;
|
use ruff_python_ast::ExprGenerator;
|
||||||
use ruff_python_ast::comparable::ComparableExpr;
|
use ruff_python_ast::comparable::ComparableExpr;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
|
||||||
use ruff_python_ast::token::TokenKind;
|
use ruff_python_ast::token::TokenKind;
|
||||||
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
@ -142,13 +142,9 @@ pub(crate) fn unnecessary_generator_list(checker: &Checker, call: &ast::ExprCall
|
||||||
if *parenthesized {
|
if *parenthesized {
|
||||||
// The generator's range will include the innermost parentheses, but it could be
|
// The generator's range will include the innermost parentheses, but it could be
|
||||||
// surrounded by additional parentheses.
|
// surrounded by additional parentheses.
|
||||||
let range = parenthesized_range(
|
let range =
|
||||||
argument.into(),
|
parenthesized_range(argument.into(), (&call.arguments).into(), checker.tokens())
|
||||||
(&call.arguments).into(),
|
.unwrap_or(argument.range());
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(argument.range());
|
|
||||||
|
|
||||||
// The generator always parenthesizes the expression; trim the parentheses.
|
// The generator always parenthesizes the expression; trim the parentheses.
|
||||||
let generator = checker.generator().expr(argument);
|
let generator = checker.generator().expr(argument);
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
use ruff_python_ast::ExprGenerator;
|
use ruff_python_ast::ExprGenerator;
|
||||||
use ruff_python_ast::comparable::ComparableExpr;
|
use ruff_python_ast::comparable::ComparableExpr;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
|
||||||
use ruff_python_ast::token::TokenKind;
|
use ruff_python_ast::token::TokenKind;
|
||||||
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
@ -147,13 +147,9 @@ pub(crate) fn unnecessary_generator_set(checker: &Checker, call: &ast::ExprCall)
|
||||||
if *parenthesized {
|
if *parenthesized {
|
||||||
// The generator's range will include the innermost parentheses, but it could be
|
// The generator's range will include the innermost parentheses, but it could be
|
||||||
// surrounded by additional parentheses.
|
// surrounded by additional parentheses.
|
||||||
let range = parenthesized_range(
|
let range =
|
||||||
argument.into(),
|
parenthesized_range(argument.into(), (&call.arguments).into(), checker.tokens())
|
||||||
(&call.arguments).into(),
|
.unwrap_or(argument.range());
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(argument.range());
|
|
||||||
|
|
||||||
// The generator always parenthesizes the expression; trim the parentheses.
|
// The generator always parenthesizes the expression; trim the parentheses.
|
||||||
let generator = checker.generator().expr(argument);
|
let generator = checker.generator().expr(argument);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
|
||||||
use ruff_python_ast::token::TokenKind;
|
use ruff_python_ast::token::TokenKind;
|
||||||
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
@ -89,13 +89,9 @@ pub(crate) fn unnecessary_list_comprehension_set(checker: &Checker, call: &ast::
|
||||||
|
|
||||||
// If the list comprehension is parenthesized, remove the parentheses in addition to
|
// If the list comprehension is parenthesized, remove the parentheses in addition to
|
||||||
// removing the brackets.
|
// removing the brackets.
|
||||||
let replacement_range = parenthesized_range(
|
let replacement_range =
|
||||||
argument.into(),
|
parenthesized_range(argument.into(), (&call.arguments).into(), checker.tokens())
|
||||||
(&call.arguments).into(),
|
.unwrap_or_else(|| argument.range());
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or_else(|| argument.range());
|
|
||||||
|
|
||||||
let span = argument.range().add_start(one).sub_end(one);
|
let span = argument.range().add_start(one).sub_end(one);
|
||||||
let replacement =
|
let replacement =
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{self as ast, Expr, Operator};
|
use ruff_python_ast::{self as ast, Expr, Operator};
|
||||||
use ruff_python_trivia::is_python_whitespace;
|
use ruff_python_trivia::is_python_whitespace;
|
||||||
use ruff_source_file::LineRanges;
|
use ruff_source_file::LineRanges;
|
||||||
|
|
@ -88,13 +88,7 @@ pub(crate) fn explicit(checker: &Checker, expr: &Expr) {
|
||||||
checker.report_diagnostic(ExplicitStringConcatenation, expr.range());
|
checker.report_diagnostic(ExplicitStringConcatenation, expr.range());
|
||||||
|
|
||||||
let is_parenthesized = |expr: &Expr| {
|
let is_parenthesized = |expr: &Expr| {
|
||||||
parenthesized_range(
|
parenthesized_range(expr.into(), bin_op.into(), checker.tokens()).is_some()
|
||||||
expr.into(),
|
|
||||||
bin_op.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.source(),
|
|
||||||
)
|
|
||||||
.is_some()
|
|
||||||
};
|
};
|
||||||
// If either `left` or `right` is parenthesized, generating
|
// If either `left` or `right` is parenthesized, generating
|
||||||
// a fix would be too involved. Just report the diagnostic.
|
// a fix would be too involved. Just report the diagnostic.
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,6 @@ pub(crate) fn exc_info_outside_except_handler(checker: &Checker, call: &ExprCall
|
||||||
}
|
}
|
||||||
|
|
||||||
let arguments = &call.arguments;
|
let arguments = &call.arguments;
|
||||||
let source = checker.source();
|
|
||||||
|
|
||||||
let mut diagnostic = checker.report_diagnostic(ExcInfoOutsideExceptHandler, exc_info.range);
|
let mut diagnostic = checker.report_diagnostic(ExcInfoOutsideExceptHandler, exc_info.range);
|
||||||
|
|
||||||
|
|
@ -120,8 +119,8 @@ pub(crate) fn exc_info_outside_except_handler(checker: &Checker, call: &ExprCall
|
||||||
exc_info,
|
exc_info,
|
||||||
arguments,
|
arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
source,
|
checker.source(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)?;
|
)?;
|
||||||
Ok(Fix::unsafe_edit(edit))
|
Ok(Fix::unsafe_edit(edit))
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use itertools::Itertools;
|
||||||
use rustc_hash::{FxBuildHasher, FxHashSet};
|
use rustc_hash::{FxBuildHasher, FxHashSet};
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{self as ast, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
use ruff_python_stdlib::identifiers::is_identifier;
|
use ruff_python_stdlib::identifiers::is_identifier;
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
@ -129,8 +129,8 @@ pub(crate) fn unnecessary_dict_kwargs(checker: &Checker, call: &ast::ExprCall) {
|
||||||
keyword,
|
keyword,
|
||||||
&call.arguments,
|
&call.arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
checker.locator().contents(),
|
checker.source(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)
|
)
|
||||||
.map(Fix::safe_edit)
|
.map(Fix::safe_edit)
|
||||||
});
|
});
|
||||||
|
|
@ -158,8 +158,7 @@ pub(crate) fn unnecessary_dict_kwargs(checker: &Checker, call: &ast::ExprCall) {
|
||||||
parenthesized_range(
|
parenthesized_range(
|
||||||
value.into(),
|
value.into(),
|
||||||
dict.into(),
|
dict.into(),
|
||||||
checker.comment_ranges(),
|
checker.tokens()
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
)
|
||||||
.unwrap_or(value.range())
|
.unwrap_or(value.range())
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -73,11 +73,11 @@ pub(crate) fn unnecessary_range_start(checker: &Checker, call: &ast::ExprCall) {
|
||||||
let mut diagnostic = checker.report_diagnostic(UnnecessaryRangeStart, start.range());
|
let mut diagnostic = checker.report_diagnostic(UnnecessaryRangeStart, start.range());
|
||||||
diagnostic.try_set_fix(|| {
|
diagnostic.try_set_fix(|| {
|
||||||
remove_argument(
|
remove_argument(
|
||||||
&start,
|
start,
|
||||||
&call.arguments,
|
&call.arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
checker.locator().contents(),
|
checker.source(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)
|
)
|
||||||
.map(Fix::safe_edit)
|
.map(Fix::safe_edit)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -160,20 +160,16 @@ fn generate_fix(
|
||||||
) -> anyhow::Result<Fix> {
|
) -> anyhow::Result<Fix> {
|
||||||
let locator = checker.locator();
|
let locator = checker.locator();
|
||||||
let source = locator.contents();
|
let source = locator.contents();
|
||||||
|
let tokens = checker.tokens();
|
||||||
|
|
||||||
let deletion = remove_argument(
|
let deletion = remove_argument(
|
||||||
generic_base,
|
generic_base,
|
||||||
arguments,
|
arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
source,
|
source,
|
||||||
checker.comment_ranges(),
|
tokens,
|
||||||
)?;
|
)?;
|
||||||
let insertion = add_argument(
|
let insertion = add_argument(locator.slice(generic_base), arguments, tokens);
|
||||||
locator.slice(generic_base),
|
|
||||||
arguments,
|
|
||||||
checker.comment_ranges(),
|
|
||||||
source,
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(Fix::unsafe_edits(deletion, [insertion]))
|
Ok(Fix::unsafe_edits(deletion, [insertion]))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use ruff_python_ast::{
|
||||||
helpers::{pep_604_union, typing_optional},
|
helpers::{pep_604_union, typing_optional},
|
||||||
name::Name,
|
name::Name,
|
||||||
operator_precedence::OperatorPrecedence,
|
operator_precedence::OperatorPrecedence,
|
||||||
parenthesize::parenthesized_range,
|
token::{Tokens, parenthesized_range},
|
||||||
};
|
};
|
||||||
use ruff_python_semantic::analyze::typing::{traverse_literal, traverse_union};
|
use ruff_python_semantic::analyze::typing::{traverse_literal, traverse_union};
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
@ -243,16 +243,12 @@ fn create_fix(
|
||||||
let union_expr = pep_604_union(&[new_literal_expr, none_expr]);
|
let union_expr = pep_604_union(&[new_literal_expr, none_expr]);
|
||||||
|
|
||||||
// Check if we need parentheses to preserve operator precedence
|
// Check if we need parentheses to preserve operator precedence
|
||||||
let content = if needs_parentheses_for_precedence(
|
let content =
|
||||||
semantic,
|
if needs_parentheses_for_precedence(semantic, literal_expr, checker.tokens()) {
|
||||||
literal_expr,
|
format!("({})", checker.generator().expr(&union_expr))
|
||||||
checker.comment_ranges(),
|
} else {
|
||||||
checker.source(),
|
checker.generator().expr(&union_expr)
|
||||||
) {
|
};
|
||||||
format!("({})", checker.generator().expr(&union_expr))
|
|
||||||
} else {
|
|
||||||
checker.generator().expr(&union_expr)
|
|
||||||
};
|
|
||||||
|
|
||||||
let union_edit = Edit::range_replacement(content, literal_expr.range());
|
let union_edit = Edit::range_replacement(content, literal_expr.range());
|
||||||
Fix::applicable_edit(union_edit, applicability)
|
Fix::applicable_edit(union_edit, applicability)
|
||||||
|
|
@ -278,8 +274,7 @@ enum UnionKind {
|
||||||
fn needs_parentheses_for_precedence(
|
fn needs_parentheses_for_precedence(
|
||||||
semantic: &ruff_python_semantic::SemanticModel,
|
semantic: &ruff_python_semantic::SemanticModel,
|
||||||
literal_expr: &Expr,
|
literal_expr: &Expr,
|
||||||
comment_ranges: &ruff_python_trivia::CommentRanges,
|
tokens: &Tokens,
|
||||||
source: &str,
|
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// Get the parent expression to check if we're in a context that needs parentheses
|
// Get the parent expression to check if we're in a context that needs parentheses
|
||||||
let Some(parent_expr) = semantic.current_expression_parent() else {
|
let Some(parent_expr) = semantic.current_expression_parent() else {
|
||||||
|
|
@ -287,14 +282,7 @@ fn needs_parentheses_for_precedence(
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if the literal expression is already parenthesized
|
// Check if the literal expression is already parenthesized
|
||||||
if parenthesized_range(
|
if parenthesized_range(literal_expr.into(), parent_expr.into(), tokens).is_some() {
|
||||||
literal_expr.into(),
|
|
||||||
parent_expr.into(),
|
|
||||||
comment_ranges,
|
|
||||||
source,
|
|
||||||
)
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
return false; // Already parenthesized, don't add more
|
return false; // Already parenthesized, don't add more
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use libcst_native::{
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::helpers::Truthiness;
|
use ruff_python_ast::helpers::Truthiness;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::visitor::Visitor;
|
use ruff_python_ast::visitor::Visitor;
|
||||||
use ruff_python_ast::{
|
use ruff_python_ast::{
|
||||||
self as ast, AnyNodeRef, Arguments, BoolOp, ExceptHandler, Expr, Keyword, Stmt, UnaryOp,
|
self as ast, AnyNodeRef, Arguments, BoolOp, ExceptHandler, Expr, Keyword, Stmt, UnaryOp,
|
||||||
|
|
@ -303,8 +303,7 @@ pub(crate) fn unittest_assertion(
|
||||||
parenthesized_range(
|
parenthesized_range(
|
||||||
expr.into(),
|
expr.into(),
|
||||||
checker.semantic().current_statement().into(),
|
checker.semantic().current_statement().into(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
)
|
||||||
.unwrap_or(expr.range()),
|
.unwrap_or(expr.range()),
|
||||||
)));
|
)));
|
||||||
|
|
|
||||||
|
|
@ -768,8 +768,8 @@ fn check_fixture_decorator(checker: &Checker, func_name: &str, decorator: &Decor
|
||||||
keyword,
|
keyword,
|
||||||
arguments,
|
arguments,
|
||||||
edits::Parentheses::Preserve,
|
edits::Parentheses::Preserve,
|
||||||
checker.locator().contents(),
|
checker.source(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)
|
)
|
||||||
.map(Fix::unsafe_edit)
|
.map(Fix::unsafe_edit)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,9 @@ use rustc_hash::{FxBuildHasher, FxHashMap};
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::comparable::ComparableExpr;
|
use ruff_python_ast::comparable::ComparableExpr;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::{Tokens, parenthesized_range};
|
||||||
use ruff_python_ast::{self as ast, Expr, ExprCall, ExprContext, StringLiteralFlags};
|
use ruff_python_ast::{self as ast, Expr, ExprCall, ExprContext, StringLiteralFlags};
|
||||||
use ruff_python_codegen::Generator;
|
use ruff_python_codegen::Generator;
|
||||||
use ruff_python_trivia::CommentRanges;
|
|
||||||
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
||||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||||
|
|
||||||
|
|
@ -322,18 +321,8 @@ fn elts_to_csv(elts: &[Expr], generator: Generator, flags: StringLiteralFlags) -
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// This method assumes that the first argument is a string.
|
/// This method assumes that the first argument is a string.
|
||||||
fn get_parametrize_name_range(
|
fn get_parametrize_name_range(call: &ExprCall, expr: &Expr, tokens: &Tokens) -> Option<TextRange> {
|
||||||
call: &ExprCall,
|
parenthesized_range(expr.into(), (&call.arguments).into(), tokens)
|
||||||
expr: &Expr,
|
|
||||||
comment_ranges: &CommentRanges,
|
|
||||||
source: &str,
|
|
||||||
) -> Option<TextRange> {
|
|
||||||
parenthesized_range(
|
|
||||||
expr.into(),
|
|
||||||
(&call.arguments).into(),
|
|
||||||
comment_ranges,
|
|
||||||
source,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PT006
|
/// PT006
|
||||||
|
|
@ -349,13 +338,8 @@ fn check_names(checker: &Checker, call: &ExprCall, expr: &Expr, argvalues: &Expr
|
||||||
if names.len() > 1 {
|
if names.len() > 1 {
|
||||||
match names_type {
|
match names_type {
|
||||||
types::ParametrizeNameType::Tuple => {
|
types::ParametrizeNameType::Tuple => {
|
||||||
let name_range = get_parametrize_name_range(
|
let name_range = get_parametrize_name_range(call, expr, checker.tokens())
|
||||||
call,
|
.unwrap_or(expr.range());
|
||||||
expr,
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(expr.range());
|
|
||||||
let mut diagnostic = checker.report_diagnostic(
|
let mut diagnostic = checker.report_diagnostic(
|
||||||
PytestParametrizeNamesWrongType {
|
PytestParametrizeNamesWrongType {
|
||||||
single_argument: false,
|
single_argument: false,
|
||||||
|
|
@ -386,13 +370,8 @@ fn check_names(checker: &Checker, call: &ExprCall, expr: &Expr, argvalues: &Expr
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
types::ParametrizeNameType::List => {
|
types::ParametrizeNameType::List => {
|
||||||
let name_range = get_parametrize_name_range(
|
let name_range = get_parametrize_name_range(call, expr, checker.tokens())
|
||||||
call,
|
.unwrap_or(expr.range());
|
||||||
expr,
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(expr.range());
|
|
||||||
let mut diagnostic = checker.report_diagnostic(
|
let mut diagnostic = checker.report_diagnostic(
|
||||||
PytestParametrizeNamesWrongType {
|
PytestParametrizeNamesWrongType {
|
||||||
single_argument: false,
|
single_argument: false,
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::comparable::ComparableExpr;
|
use ruff_python_ast::comparable::ComparableExpr;
|
||||||
use ruff_python_ast::helpers::{Truthiness, contains_effect};
|
use ruff_python_ast::helpers::{Truthiness, contains_effect};
|
||||||
use ruff_python_ast::name::Name;
|
use ruff_python_ast::name::Name;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_codegen::Generator;
|
use ruff_python_codegen::Generator;
|
||||||
use ruff_python_semantic::SemanticModel;
|
use ruff_python_semantic::SemanticModel;
|
||||||
|
|
||||||
|
|
@ -800,14 +800,9 @@ fn is_short_circuit(
|
||||||
edit = Some(get_short_circuit_edit(
|
edit = Some(get_short_circuit_edit(
|
||||||
value,
|
value,
|
||||||
TextRange::new(
|
TextRange::new(
|
||||||
parenthesized_range(
|
parenthesized_range(furthest.into(), expr.into(), checker.tokens())
|
||||||
furthest.into(),
|
.unwrap_or(furthest.range())
|
||||||
expr.into(),
|
.start(),
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(furthest.range())
|
|
||||||
.start(),
|
|
||||||
expr.end(),
|
expr.end(),
|
||||||
),
|
),
|
||||||
short_circuit_truthiness,
|
short_circuit_truthiness,
|
||||||
|
|
@ -828,14 +823,9 @@ fn is_short_circuit(
|
||||||
edit = Some(get_short_circuit_edit(
|
edit = Some(get_short_circuit_edit(
|
||||||
next_value,
|
next_value,
|
||||||
TextRange::new(
|
TextRange::new(
|
||||||
parenthesized_range(
|
parenthesized_range(furthest.into(), expr.into(), checker.tokens())
|
||||||
furthest.into(),
|
.unwrap_or(furthest.range())
|
||||||
expr.into(),
|
.start(),
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(furthest.range())
|
|
||||||
.start(),
|
|
||||||
expr.end(),
|
expr.end(),
|
||||||
),
|
),
|
||||||
short_circuit_truthiness,
|
short_circuit_truthiness,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use ruff_text_size::{Ranged, TextRange};
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::helpers::{is_const_false, is_const_true};
|
use ruff_python_ast::helpers::{is_const_false, is_const_true};
|
||||||
use ruff_python_ast::name::Name;
|
use ruff_python_ast::name::Name;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::{AlwaysFixableViolation, Edit, Fix, FixAvailability, Violation};
|
use crate::{AlwaysFixableViolation, Edit, Fix, FixAvailability, Violation};
|
||||||
|
|
@ -171,13 +171,8 @@ pub(crate) fn if_expr_with_true_false(
|
||||||
checker
|
checker
|
||||||
.locator()
|
.locator()
|
||||||
.slice(
|
.slice(
|
||||||
parenthesized_range(
|
parenthesized_range(test.into(), expr.into(), checker.tokens())
|
||||||
test.into(),
|
.unwrap_or(test.range()),
|
||||||
expr.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(test.range()),
|
|
||||||
)
|
)
|
||||||
.to_string(),
|
.to_string(),
|
||||||
expr.range(),
|
expr.range(),
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ use anyhow::Result;
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::comparable::ComparableStmt;
|
use ruff_python_ast::comparable::ComparableStmt;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
|
||||||
use ruff_python_ast::stmt_if::{IfElifBranch, if_elif_branches};
|
use ruff_python_ast::stmt_if::{IfElifBranch, if_elif_branches};
|
||||||
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{self as ast, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
use ruff_python_trivia::{CommentRanges, SimpleTokenKind, SimpleTokenizer};
|
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
||||||
use ruff_source_file::LineRanges;
|
use ruff_source_file::LineRanges;
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
|
|
@ -99,7 +99,7 @@ pub(crate) fn if_with_same_arms(checker: &Checker, stmt_if: &ast::StmtIf) {
|
||||||
¤t_branch,
|
¤t_branch,
|
||||||
following_branch,
|
following_branch,
|
||||||
checker.locator(),
|
checker.locator(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -111,7 +111,7 @@ fn merge_branches(
|
||||||
current_branch: &IfElifBranch,
|
current_branch: &IfElifBranch,
|
||||||
following_branch: &IfElifBranch,
|
following_branch: &IfElifBranch,
|
||||||
locator: &Locator,
|
locator: &Locator,
|
||||||
comment_ranges: &CommentRanges,
|
tokens: &ruff_python_ast::token::Tokens,
|
||||||
) -> Result<Fix> {
|
) -> Result<Fix> {
|
||||||
// Identify the colon (`:`) at the end of the current branch's test.
|
// Identify the colon (`:`) at the end of the current branch's test.
|
||||||
let Some(current_branch_colon) =
|
let Some(current_branch_colon) =
|
||||||
|
|
@ -127,12 +127,9 @@ fn merge_branches(
|
||||||
);
|
);
|
||||||
|
|
||||||
// If the following test isn't parenthesized, consider parenthesizing it.
|
// If the following test isn't parenthesized, consider parenthesizing it.
|
||||||
let following_branch_test = if let Some(range) = parenthesized_range(
|
let following_branch_test = if let Some(range) =
|
||||||
following_branch.test.into(),
|
parenthesized_range(following_branch.test.into(), stmt_if.into(), tokens)
|
||||||
stmt_if.into(),
|
{
|
||||||
comment_ranges,
|
|
||||||
locator.contents(),
|
|
||||||
) {
|
|
||||||
Cow::Borrowed(locator.slice(range))
|
Cow::Borrowed(locator.slice(range))
|
||||||
} else if matches!(
|
} else if matches!(
|
||||||
following_branch.test,
|
following_branch.test,
|
||||||
|
|
@ -153,24 +150,19 @@ fn merge_branches(
|
||||||
//
|
//
|
||||||
// For example, if the current test is `x if x else y`, we should parenthesize it to
|
// For example, if the current test is `x if x else y`, we should parenthesize it to
|
||||||
// `(x if x else y) or ...`.
|
// `(x if x else y) or ...`.
|
||||||
let parenthesize_edit = if matches!(
|
let parenthesize_edit =
|
||||||
current_branch.test,
|
if matches!(
|
||||||
Expr::Lambda(_) | Expr::Named(_) | Expr::If(_)
|
current_branch.test,
|
||||||
) && parenthesized_range(
|
Expr::Lambda(_) | Expr::Named(_) | Expr::If(_)
|
||||||
current_branch.test.into(),
|
) && parenthesized_range(current_branch.test.into(), stmt_if.into(), tokens).is_none()
|
||||||
stmt_if.into(),
|
{
|
||||||
comment_ranges,
|
Some(Edit::range_replacement(
|
||||||
locator.contents(),
|
format!("({})", locator.slice(current_branch.test)),
|
||||||
)
|
current_branch.test.range(),
|
||||||
.is_none()
|
))
|
||||||
{
|
} else {
|
||||||
Some(Edit::range_replacement(
|
None
|
||||||
format!("({})", locator.slice(current_branch.test)),
|
};
|
||||||
current_branch.test.range(),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Fix::safe_edits(
|
Ok(Fix::safe_edits(
|
||||||
deletion_edit,
|
deletion_edit,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::AnyNodeRef;
|
use ruff_python_ast::AnyNodeRef;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{self as ast, Arguments, CmpOp, Comprehension, Expr};
|
use ruff_python_ast::{self as ast, Arguments, CmpOp, Comprehension, Expr};
|
||||||
use ruff_python_semantic::analyze::typing;
|
use ruff_python_semantic::analyze::typing;
|
||||||
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
||||||
|
|
@ -90,20 +90,10 @@ fn key_in_dict(checker: &Checker, left: &Expr, right: &Expr, operator: CmpOp, pa
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the exact range of the left and right expressions.
|
// Extract the exact range of the left and right expressions.
|
||||||
let left_range = parenthesized_range(
|
let left_range =
|
||||||
left.into(),
|
parenthesized_range(left.into(), parent, checker.tokens()).unwrap_or(left.range());
|
||||||
parent,
|
let right_range =
|
||||||
checker.comment_ranges(),
|
parenthesized_range(right.into(), parent, checker.tokens()).unwrap_or(right.range());
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(left.range());
|
|
||||||
let right_range = parenthesized_range(
|
|
||||||
right.into(),
|
|
||||||
parent,
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(right.range());
|
|
||||||
|
|
||||||
let mut diagnostic = checker.report_diagnostic(
|
let mut diagnostic = checker.report_diagnostic(
|
||||||
InDictKeys {
|
InDictKeys {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use crate::registry::Rule;
|
||||||
use crate::rules::flake8_type_checking::helpers::quote_type_expression;
|
use crate::rules::flake8_type_checking::helpers::quote_type_expression;
|
||||||
use crate::{AlwaysFixableViolation, Edit, Fix, FixAvailability, Violation};
|
use crate::{AlwaysFixableViolation, Edit, Fix, FixAvailability, Violation};
|
||||||
use ruff_python_ast::PythonVersion;
|
use ruff_python_ast::PythonVersion;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
|
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks if [PEP 613] explicit type aliases contain references to
|
/// Checks if [PEP 613] explicit type aliases contain references to
|
||||||
|
|
@ -295,21 +295,20 @@ pub(crate) fn quoted_type_alias(
|
||||||
let range = annotation_expr.range();
|
let range = annotation_expr.range();
|
||||||
let mut diagnostic = checker.report_diagnostic(QuotedTypeAlias, range);
|
let mut diagnostic = checker.report_diagnostic(QuotedTypeAlias, range);
|
||||||
let fix_string = annotation_expr.value.to_string();
|
let fix_string = annotation_expr.value.to_string();
|
||||||
|
|
||||||
let fix_string = if (fix_string.contains('\n') || fix_string.contains('\r'))
|
let fix_string = if (fix_string.contains('\n') || fix_string.contains('\r'))
|
||||||
&& parenthesized_range(
|
&& parenthesized_range(
|
||||||
// Check for parenthesis outside string ("""...""")
|
// Check for parentheses outside the string ("""...""")
|
||||||
annotation_expr.into(),
|
annotation_expr.into(),
|
||||||
checker.semantic().current_statement().into(),
|
checker.semantic().current_statement().into(),
|
||||||
checker.comment_ranges(),
|
checker.source_tokens(),
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
)
|
||||||
.is_none()
|
.is_none()
|
||||||
&& parenthesized_range(
|
&& parenthesized_range(
|
||||||
// Check for parenthesis inside string """(...)"""
|
// Check for parentheses inside the string """(...)"""
|
||||||
expr.into(),
|
expr.into(),
|
||||||
annotation_expr.into(),
|
annotation_expr.into(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
)
|
||||||
.is_none()
|
.is_none()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{Expr, ExprBinOp, ExprCall, Operator};
|
use ruff_python_ast::{Expr, ExprBinOp, ExprCall, Operator};
|
||||||
use ruff_python_semantic::SemanticModel;
|
use ruff_python_semantic::SemanticModel;
|
||||||
use ruff_python_trivia::CommentRanges;
|
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
@ -89,11 +88,7 @@ pub(crate) fn path_constructor_current_directory(
|
||||||
|
|
||||||
let mut diagnostic = checker.report_diagnostic(PathConstructorCurrentDirectory, arg.range());
|
let mut diagnostic = checker.report_diagnostic(PathConstructorCurrentDirectory, arg.range());
|
||||||
|
|
||||||
match parent_and_next_path_fragment_range(
|
match parent_and_next_path_fragment_range(checker.semantic(), checker.tokens()) {
|
||||||
checker.semantic(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.source(),
|
|
||||||
) {
|
|
||||||
Some((parent_range, next_fragment_range)) => {
|
Some((parent_range, next_fragment_range)) => {
|
||||||
let next_fragment_expr = checker.locator().slice(next_fragment_range);
|
let next_fragment_expr = checker.locator().slice(next_fragment_range);
|
||||||
let call_expr = checker.locator().slice(call.range());
|
let call_expr = checker.locator().slice(call.range());
|
||||||
|
|
@ -116,7 +111,7 @@ pub(crate) fn path_constructor_current_directory(
|
||||||
arguments,
|
arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
checker.source(),
|
checker.source(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)?;
|
)?;
|
||||||
Ok(Fix::applicable_edit(edit, applicability(call.range())))
|
Ok(Fix::applicable_edit(edit, applicability(call.range())))
|
||||||
}),
|
}),
|
||||||
|
|
@ -125,8 +120,7 @@ pub(crate) fn path_constructor_current_directory(
|
||||||
|
|
||||||
fn parent_and_next_path_fragment_range(
|
fn parent_and_next_path_fragment_range(
|
||||||
semantic: &SemanticModel,
|
semantic: &SemanticModel,
|
||||||
comment_ranges: &CommentRanges,
|
tokens: &ruff_python_ast::token::Tokens,
|
||||||
source: &str,
|
|
||||||
) -> Option<(TextRange, TextRange)> {
|
) -> Option<(TextRange, TextRange)> {
|
||||||
let parent = semantic.current_expression_parent()?;
|
let parent = semantic.current_expression_parent()?;
|
||||||
|
|
||||||
|
|
@ -142,6 +136,6 @@ fn parent_and_next_path_fragment_range(
|
||||||
|
|
||||||
Some((
|
Some((
|
||||||
parent.range(),
|
parent.range(),
|
||||||
parenthesized_range(right.into(), parent.into(), comment_ranges, source).unwrap_or(range),
|
parenthesized_range(right.into(), parent.into(), tokens).unwrap_or(range),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::helpers::is_const_true;
|
use ruff_python_ast::helpers::is_const_true;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::{Tokens, parenthesized_range};
|
||||||
use ruff_python_ast::{self as ast, Keyword, Stmt};
|
use ruff_python_ast::{self as ast, Keyword, Stmt};
|
||||||
use ruff_python_trivia::CommentRanges;
|
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::Locator;
|
use crate::Locator;
|
||||||
|
|
@ -91,7 +90,7 @@ pub(crate) fn inplace_argument(checker: &Checker, call: &ast::ExprCall) {
|
||||||
call,
|
call,
|
||||||
keyword,
|
keyword,
|
||||||
statement,
|
statement,
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.locator(),
|
checker.locator(),
|
||||||
) {
|
) {
|
||||||
diagnostic.set_fix(fix);
|
diagnostic.set_fix(fix);
|
||||||
|
|
@ -111,21 +110,16 @@ fn convert_inplace_argument_to_assignment(
|
||||||
call: &ast::ExprCall,
|
call: &ast::ExprCall,
|
||||||
keyword: &Keyword,
|
keyword: &Keyword,
|
||||||
statement: &Stmt,
|
statement: &Stmt,
|
||||||
comment_ranges: &CommentRanges,
|
tokens: &Tokens,
|
||||||
locator: &Locator,
|
locator: &Locator,
|
||||||
) -> Option<Fix> {
|
) -> Option<Fix> {
|
||||||
// Add the assignment.
|
// Add the assignment.
|
||||||
let attr = call.func.as_attribute_expr()?;
|
let attr = call.func.as_attribute_expr()?;
|
||||||
let insert_assignment = Edit::insertion(
|
let insert_assignment = Edit::insertion(
|
||||||
format!("{name} = ", name = locator.slice(attr.value.range())),
|
format!("{name} = ", name = locator.slice(attr.value.range())),
|
||||||
parenthesized_range(
|
parenthesized_range(call.into(), statement.into(), tokens)
|
||||||
call.into(),
|
.unwrap_or(call.range())
|
||||||
statement.into(),
|
.start(),
|
||||||
comment_ranges,
|
|
||||||
locator.contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(call.range())
|
|
||||||
.start(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Remove the `inplace` argument.
|
// Remove the `inplace` argument.
|
||||||
|
|
@ -134,7 +128,7 @@ fn convert_inplace_argument_to_assignment(
|
||||||
&call.arguments,
|
&call.arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
locator.contents(),
|
locator.contents(),
|
||||||
comment_ranges,
|
tokens,
|
||||||
)
|
)
|
||||||
.ok()?;
|
.ok()?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{
|
use ruff_python_ast::{
|
||||||
self as ast, Expr, ExprEllipsisLiteral, ExprLambda, Identifier, Parameter,
|
self as ast, Expr, ExprEllipsisLiteral, ExprLambda, Identifier, Parameter,
|
||||||
ParameterWithDefault, Parameters, Stmt,
|
ParameterWithDefault, Parameters, Stmt,
|
||||||
|
|
@ -265,29 +265,19 @@ fn replace_trailing_ellipsis_with_original_expr(
|
||||||
stmt: &Stmt,
|
stmt: &Stmt,
|
||||||
checker: &Checker,
|
checker: &Checker,
|
||||||
) -> String {
|
) -> String {
|
||||||
let original_expr_range = parenthesized_range(
|
let original_expr_range =
|
||||||
(&lambda.body).into(),
|
parenthesized_range((&lambda.body).into(), lambda.into(), checker.tokens())
|
||||||
lambda.into(),
|
.unwrap_or(lambda.body.range());
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.source(),
|
|
||||||
)
|
|
||||||
.unwrap_or(lambda.body.range());
|
|
||||||
|
|
||||||
// This prevents the autofix of introducing a syntax error if the lambda's body is an
|
// This prevents the autofix of introducing a syntax error if the lambda's body is an
|
||||||
// expression spanned across multiple lines. To avoid the syntax error we preserve
|
// expression spanned across multiple lines. To avoid the syntax error we preserve
|
||||||
// the parenthesis around the body.
|
// the parenthesis around the body.
|
||||||
let original_expr_in_source = if parenthesized_range(
|
let original_expr_in_source =
|
||||||
lambda.into(),
|
if parenthesized_range(lambda.into(), stmt.into(), checker.tokens()).is_some() {
|
||||||
stmt.into(),
|
format!("({})", checker.locator().slice(original_expr_range))
|
||||||
checker.comment_ranges(),
|
} else {
|
||||||
checker.source(),
|
checker.locator().slice(original_expr_range).to_string()
|
||||||
)
|
};
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
format!("({})", checker.locator().slice(original_expr_range))
|
|
||||||
} else {
|
|
||||||
checker.locator().slice(original_expr_range).to_string()
|
|
||||||
};
|
|
||||||
|
|
||||||
let placeholder_ellipsis_start = generated.rfind("...").unwrap();
|
let placeholder_ellipsis_start = generated.rfind("...").unwrap();
|
||||||
let placeholder_ellipsis_end = placeholder_ellipsis_start + "...".len();
|
let placeholder_ellipsis_end = placeholder_ellipsis_start + "...".len();
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::{Tokens, parenthesized_range};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
|
|
@ -179,15 +179,14 @@ fn is_redundant_boolean_comparison(op: CmpOp, comparator: &Expr) -> Option<bool>
|
||||||
|
|
||||||
fn generate_redundant_comparison(
|
fn generate_redundant_comparison(
|
||||||
compare: &ast::ExprCompare,
|
compare: &ast::ExprCompare,
|
||||||
comment_ranges: &ruff_python_trivia::CommentRanges,
|
tokens: &Tokens,
|
||||||
source: &str,
|
source: &str,
|
||||||
comparator: &Expr,
|
comparator: &Expr,
|
||||||
kind: bool,
|
kind: bool,
|
||||||
needs_wrap: bool,
|
needs_wrap: bool,
|
||||||
) -> String {
|
) -> String {
|
||||||
let comparator_range =
|
let comparator_range = parenthesized_range(comparator.into(), compare.into(), tokens)
|
||||||
parenthesized_range(comparator.into(), compare.into(), comment_ranges, source)
|
.unwrap_or(comparator.range());
|
||||||
.unwrap_or(comparator.range());
|
|
||||||
|
|
||||||
let comparator_str = &source[comparator_range];
|
let comparator_str = &source[comparator_range];
|
||||||
|
|
||||||
|
|
@ -379,7 +378,7 @@ pub(crate) fn literal_comparisons(checker: &Checker, compare: &ast::ExprCompare)
|
||||||
.copied()
|
.copied()
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let comment_ranges = checker.comment_ranges();
|
let tokens = checker.tokens();
|
||||||
let source = checker.source();
|
let source = checker.source();
|
||||||
|
|
||||||
let content = match (&*compare.ops, &*compare.comparators) {
|
let content = match (&*compare.ops, &*compare.comparators) {
|
||||||
|
|
@ -387,18 +386,13 @@ pub(crate) fn literal_comparisons(checker: &Checker, compare: &ast::ExprCompare)
|
||||||
if let Some(kind) = is_redundant_boolean_comparison(*op, &compare.left) {
|
if let Some(kind) = is_redundant_boolean_comparison(*op, &compare.left) {
|
||||||
let needs_wrap = compare.left.range().start() != compare.range().start();
|
let needs_wrap = compare.left.range().start() != compare.range().start();
|
||||||
generate_redundant_comparison(
|
generate_redundant_comparison(
|
||||||
compare,
|
compare, tokens, source, comparator, kind, needs_wrap,
|
||||||
comment_ranges,
|
|
||||||
source,
|
|
||||||
comparator,
|
|
||||||
kind,
|
|
||||||
needs_wrap,
|
|
||||||
)
|
)
|
||||||
} else if let Some(kind) = is_redundant_boolean_comparison(*op, comparator) {
|
} else if let Some(kind) = is_redundant_boolean_comparison(*op, comparator) {
|
||||||
let needs_wrap = comparator.range().end() != compare.range().end();
|
let needs_wrap = comparator.range().end() != compare.range().end();
|
||||||
generate_redundant_comparison(
|
generate_redundant_comparison(
|
||||||
compare,
|
compare,
|
||||||
comment_ranges,
|
tokens,
|
||||||
source,
|
source,
|
||||||
&compare.left,
|
&compare.left,
|
||||||
kind,
|
kind,
|
||||||
|
|
@ -410,7 +404,7 @@ pub(crate) fn literal_comparisons(checker: &Checker, compare: &ast::ExprCompare)
|
||||||
&ops,
|
&ops,
|
||||||
&compare.comparators,
|
&compare.comparators,
|
||||||
compare.into(),
|
compare.into(),
|
||||||
comment_ranges,
|
tokens,
|
||||||
source,
|
source,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -420,7 +414,7 @@ pub(crate) fn literal_comparisons(checker: &Checker, compare: &ast::ExprCompare)
|
||||||
&ops,
|
&ops,
|
||||||
&compare.comparators,
|
&compare.comparators,
|
||||||
compare.into(),
|
compare.into(),
|
||||||
comment_ranges,
|
tokens,
|
||||||
source,
|
source,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ pub(crate) fn not_tests(checker: &Checker, unary_op: &ast::ExprUnaryOp) {
|
||||||
&[CmpOp::NotIn],
|
&[CmpOp::NotIn],
|
||||||
comparators,
|
comparators,
|
||||||
unary_op.into(),
|
unary_op.into(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.source(),
|
checker.source(),
|
||||||
),
|
),
|
||||||
unary_op.range(),
|
unary_op.range(),
|
||||||
|
|
@ -127,7 +127,7 @@ pub(crate) fn not_tests(checker: &Checker, unary_op: &ast::ExprUnaryOp) {
|
||||||
&[CmpOp::IsNot],
|
&[CmpOp::IsNot],
|
||||||
comparators,
|
comparators,
|
||||||
unary_op.into(),
|
unary_op.into(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.source(),
|
checker.source(),
|
||||||
),
|
),
|
||||||
unary_op.range(),
|
unary_op.range(),
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@ use rustc_hash::FxHashSet;
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
|
use ruff_python_ast::Parameter;
|
||||||
use ruff_python_ast::docstrings::{clean_space, leading_space};
|
use ruff_python_ast::docstrings::{clean_space, leading_space};
|
||||||
|
use ruff_python_ast::helpers::map_subscript;
|
||||||
use ruff_python_ast::identifier::Identifier;
|
use ruff_python_ast::identifier::Identifier;
|
||||||
use ruff_python_semantic::analyze::visibility::is_staticmethod;
|
use ruff_python_semantic::analyze::visibility::is_staticmethod;
|
||||||
use ruff_python_trivia::textwrap::dedent;
|
use ruff_python_trivia::textwrap::dedent;
|
||||||
|
|
@ -1184,6 +1186,9 @@ impl AlwaysFixableViolation for MissingSectionNameColon {
|
||||||
/// This rule is enabled when using the `google` convention, and disabled when
|
/// This rule is enabled when using the `google` convention, and disabled when
|
||||||
/// using the `pep257` and `numpy` conventions.
|
/// using the `pep257` and `numpy` conventions.
|
||||||
///
|
///
|
||||||
|
/// Parameters annotated with `typing.Unpack` are exempt from this rule.
|
||||||
|
/// This follows the Python typing specification for unpacking keyword arguments.
|
||||||
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```python
|
/// ```python
|
||||||
/// def calculate_speed(distance: float, time: float) -> float:
|
/// def calculate_speed(distance: float, time: float) -> float:
|
||||||
|
|
@ -1233,6 +1238,7 @@ impl AlwaysFixableViolation for MissingSectionNameColon {
|
||||||
/// - [PEP 257 – Docstring Conventions](https://peps.python.org/pep-0257/)
|
/// - [PEP 257 – Docstring Conventions](https://peps.python.org/pep-0257/)
|
||||||
/// - [PEP 287 – reStructuredText Docstring Format](https://peps.python.org/pep-0287/)
|
/// - [PEP 287 – reStructuredText Docstring Format](https://peps.python.org/pep-0287/)
|
||||||
/// - [Google Python Style Guide - Docstrings](https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings)
|
/// - [Google Python Style Guide - Docstrings](https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings)
|
||||||
|
/// - [Python - Unpack for keyword arguments](https://typing.python.org/en/latest/spec/callables.html#unpack-kwargs)
|
||||||
#[derive(ViolationMetadata)]
|
#[derive(ViolationMetadata)]
|
||||||
#[violation_metadata(stable_since = "v0.0.73")]
|
#[violation_metadata(stable_since = "v0.0.73")]
|
||||||
pub(crate) struct UndocumentedParam {
|
pub(crate) struct UndocumentedParam {
|
||||||
|
|
@ -1808,7 +1814,9 @@ fn missing_args(checker: &Checker, docstring: &Docstring, docstrings_args: &FxHa
|
||||||
missing_arg_names.insert(starred_arg_name);
|
missing_arg_names.insert(starred_arg_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(arg) = function.parameters.kwarg.as_ref() {
|
if let Some(arg) = function.parameters.kwarg.as_ref()
|
||||||
|
&& !has_unpack_annotation(checker, arg)
|
||||||
|
{
|
||||||
let arg_name = arg.name.as_str();
|
let arg_name = arg.name.as_str();
|
||||||
let starred_arg_name = format!("**{arg_name}");
|
let starred_arg_name = format!("**{arg_name}");
|
||||||
if !arg_name.starts_with('_')
|
if !arg_name.starts_with('_')
|
||||||
|
|
@ -1834,6 +1842,15 @@ fn missing_args(checker: &Checker, docstring: &Docstring, docstrings_args: &FxHa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the parameter is annotated with `typing.Unpack`
|
||||||
|
fn has_unpack_annotation(checker: &Checker, parameter: &Parameter) -> bool {
|
||||||
|
parameter.annotation.as_ref().is_some_and(|annotation| {
|
||||||
|
checker
|
||||||
|
.semantic()
|
||||||
|
.match_typing_expr(map_subscript(annotation), "Unpack")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// See: `GOOGLE_ARGS_REGEX` in `pydocstyle/checker.py`.
|
// See: `GOOGLE_ARGS_REGEX` in `pydocstyle/checker.py`.
|
||||||
static GOOGLE_ARGS_REGEX: LazyLock<Regex> =
|
static GOOGLE_ARGS_REGEX: LazyLock<Regex> =
|
||||||
LazyLock::new(|| Regex::new(r"^\s*(\*?\*?\w+)\s*(\(.*?\))?\s*:(\r\n|\n)?\s*.+").unwrap());
|
LazyLock::new(|| Regex::new(r"^\s*(\*?\*?\w+)\s*(\(.*?\))?\s*:(\r\n|\n)?\s*.+").unwrap());
|
||||||
|
|
|
||||||
|
|
@ -101,3 +101,13 @@ D417 Missing argument description in the docstring for `should_fail`: `Args`
|
||||||
200 | """
|
200 | """
|
||||||
201 | Send a message.
|
201 | Send a message.
|
||||||
|
|
|
|
||||||
|
|
||||||
|
D417 Missing argument description in the docstring for `function_with_unpack_and_missing_arg_doc_should_fail`: `query`
|
||||||
|
--> D417.py:238:5
|
||||||
|
|
|
||||||
|
236 | """
|
||||||
|
237 |
|
||||||
|
238 | def function_with_unpack_and_missing_arg_doc_should_fail(query: str, **kwargs: Unpack[User]):
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
239 | """Function with Unpack kwargs but missing query arg documentation.
|
||||||
|
|
|
||||||
|
|
|
||||||
|
|
@ -83,3 +83,13 @@ D417 Missing argument description in the docstring for `should_fail`: `Args`
|
||||||
200 | """
|
200 | """
|
||||||
201 | Send a message.
|
201 | Send a message.
|
||||||
|
|
|
|
||||||
|
|
||||||
|
D417 Missing argument description in the docstring for `function_with_unpack_and_missing_arg_doc_should_fail`: `query`
|
||||||
|
--> D417.py:238:5
|
||||||
|
|
|
||||||
|
236 | """
|
||||||
|
237 |
|
||||||
|
238 | def function_with_unpack_and_missing_arg_doc_should_fail(query: str, **kwargs: Unpack[User]):
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
239 | """Function with Unpack kwargs but missing query arg documentation.
|
||||||
|
|
|
||||||
|
|
|
||||||
|
|
@ -101,3 +101,13 @@ D417 Missing argument description in the docstring for `should_fail`: `Args`
|
||||||
200 | """
|
200 | """
|
||||||
201 | Send a message.
|
201 | Send a message.
|
||||||
|
|
|
|
||||||
|
|
||||||
|
D417 Missing argument description in the docstring for `function_with_unpack_and_missing_arg_doc_should_fail`: `query`
|
||||||
|
--> D417.py:238:5
|
||||||
|
|
|
||||||
|
236 | """
|
||||||
|
237 |
|
||||||
|
238 | def function_with_unpack_and_missing_arg_doc_should_fail(query: str, **kwargs: Unpack[User]):
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
239 | """Function with Unpack kwargs but missing query arg documentation.
|
||||||
|
|
|
||||||
|
|
|
||||||
|
|
@ -101,3 +101,13 @@ D417 Missing argument description in the docstring for `should_fail`: `Args`
|
||||||
200 | """
|
200 | """
|
||||||
201 | Send a message.
|
201 | Send a message.
|
||||||
|
|
|
|
||||||
|
|
||||||
|
D417 Missing argument description in the docstring for `function_with_unpack_and_missing_arg_doc_should_fail`: `query`
|
||||||
|
--> D417.py:238:5
|
||||||
|
|
|
||||||
|
236 | """
|
||||||
|
237 |
|
||||||
|
238 | def function_with_unpack_and_missing_arg_doc_should_fail(query: str, **kwargs: Unpack[User]):
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
239 | """Function with Unpack kwargs but missing query arg documentation.
|
||||||
|
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ mod tests {
|
||||||
use crate::settings::types::PreviewMode;
|
use crate::settings::types::PreviewMode;
|
||||||
use crate::settings::{LinterSettings, flags};
|
use crate::settings::{LinterSettings, flags};
|
||||||
use crate::source_kind::SourceKind;
|
use crate::source_kind::SourceKind;
|
||||||
|
use crate::suppression::Suppressions;
|
||||||
use crate::test::{test_contents, test_path, test_snippet};
|
use crate::test::{test_contents, test_path, test_snippet};
|
||||||
use crate::{Locator, assert_diagnostics, assert_diagnostics_diff, directives};
|
use crate::{Locator, assert_diagnostics, assert_diagnostics_diff, directives};
|
||||||
|
|
||||||
|
|
@ -955,6 +956,8 @@ mod tests {
|
||||||
&locator,
|
&locator,
|
||||||
&indexer,
|
&indexer,
|
||||||
);
|
);
|
||||||
|
let suppressions =
|
||||||
|
Suppressions::from_tokens(&settings, locator.contents(), parsed.tokens());
|
||||||
let mut messages = check_path(
|
let mut messages = check_path(
|
||||||
Path::new("<filename>"),
|
Path::new("<filename>"),
|
||||||
None,
|
None,
|
||||||
|
|
@ -968,6 +971,7 @@ mod tests {
|
||||||
source_type,
|
source_type,
|
||||||
&parsed,
|
&parsed,
|
||||||
target_version,
|
target_version,
|
||||||
|
&suppressions,
|
||||||
);
|
);
|
||||||
messages.sort_by(Diagnostic::ruff_start_ordering);
|
messages.sort_by(Diagnostic::ruff_start_ordering);
|
||||||
let actual = messages
|
let actual = messages
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::collections::hash_map::Entry;
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::comparable::{ComparableExpr, HashableExpr};
|
use ruff_python_ast::comparable::{ComparableExpr, HashableExpr};
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{self as ast, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
|
|
@ -193,16 +193,14 @@ pub(crate) fn repeated_keys(checker: &Checker, dict: &ast::ExprDict) {
|
||||||
parenthesized_range(
|
parenthesized_range(
|
||||||
dict.value(i - 1).into(),
|
dict.value(i - 1).into(),
|
||||||
dict.into(),
|
dict.into(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
)
|
||||||
.unwrap_or_else(|| dict.value(i - 1).range())
|
.unwrap_or_else(|| dict.value(i - 1).range())
|
||||||
.end(),
|
.end(),
|
||||||
parenthesized_range(
|
parenthesized_range(
|
||||||
dict.value(i).into(),
|
dict.value(i).into(),
|
||||||
dict.into(),
|
dict.into(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
)
|
||||||
.unwrap_or_else(|| dict.value(i).range())
|
.unwrap_or_else(|| dict.value(i).range())
|
||||||
.end(),
|
.end(),
|
||||||
|
|
@ -224,16 +222,14 @@ pub(crate) fn repeated_keys(checker: &Checker, dict: &ast::ExprDict) {
|
||||||
parenthesized_range(
|
parenthesized_range(
|
||||||
dict.value(i - 1).into(),
|
dict.value(i - 1).into(),
|
||||||
dict.into(),
|
dict.into(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
)
|
||||||
.unwrap_or_else(|| dict.value(i - 1).range())
|
.unwrap_or_else(|| dict.value(i - 1).range())
|
||||||
.end(),
|
.end(),
|
||||||
parenthesized_range(
|
parenthesized_range(
|
||||||
dict.value(i).into(),
|
dict.value(i).into(),
|
||||||
dict.into(),
|
dict.into(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
)
|
||||||
.unwrap_or_else(|| dict.value(i).range())
|
.unwrap_or_else(|| dict.value(i).range())
|
||||||
.end(),
|
.end(),
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use itertools::Itertools;
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::helpers::contains_effect;
|
use ruff_python_ast::helpers::contains_effect;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::token::{TokenKind, Tokens};
|
use ruff_python_ast::token::{TokenKind, Tokens};
|
||||||
use ruff_python_ast::{self as ast, Stmt};
|
use ruff_python_ast::{self as ast, Stmt};
|
||||||
use ruff_python_semantic::Binding;
|
use ruff_python_semantic::Binding;
|
||||||
|
|
@ -172,14 +172,10 @@ fn remove_unused_variable(binding: &Binding, checker: &Checker) -> Option<Fix> {
|
||||||
{
|
{
|
||||||
// If the expression is complex (`x = foo()`), remove the assignment,
|
// If the expression is complex (`x = foo()`), remove the assignment,
|
||||||
// but preserve the right-hand side.
|
// but preserve the right-hand side.
|
||||||
let start = parenthesized_range(
|
let start =
|
||||||
target.into(),
|
parenthesized_range(target.into(), statement.into(), checker.tokens())
|
||||||
statement.into(),
|
.unwrap_or(target.range())
|
||||||
checker.comment_ranges(),
|
.start();
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(target.range())
|
|
||||||
.start();
|
|
||||||
let end = match_token_after(checker.tokens(), target.end(), |token| {
|
let end = match_token_after(checker.tokens(), target.end(), |token| {
|
||||||
token == TokenKind::Equal
|
token == TokenKind::Equal
|
||||||
})?
|
})?
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,10 @@ mod tests {
|
||||||
use crate::registry::Rule;
|
use crate::registry::Rule;
|
||||||
use crate::rules::{flake8_tidy_imports, pylint};
|
use crate::rules::{flake8_tidy_imports, pylint};
|
||||||
|
|
||||||
use crate::assert_diagnostics;
|
|
||||||
use crate::settings::LinterSettings;
|
use crate::settings::LinterSettings;
|
||||||
use crate::settings::types::PreviewMode;
|
use crate::settings::types::PreviewMode;
|
||||||
use crate::test::test_path;
|
use crate::test::test_path;
|
||||||
|
use crate::{assert_diagnostics, assert_diagnostics_diff};
|
||||||
|
|
||||||
#[test_case(Rule::SingledispatchMethod, Path::new("singledispatch_method.py"))]
|
#[test_case(Rule::SingledispatchMethod, Path::new("singledispatch_method.py"))]
|
||||||
#[test_case(
|
#[test_case(
|
||||||
|
|
@ -253,6 +253,32 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test_case(
|
||||||
|
Rule::UselessExceptionStatement,
|
||||||
|
Path::new("useless_exception_statement.py")
|
||||||
|
)]
|
||||||
|
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||||
|
let snapshot = format!(
|
||||||
|
"preview__{}_{}",
|
||||||
|
rule_code.noqa_code(),
|
||||||
|
path.to_string_lossy()
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_diagnostics_diff!(
|
||||||
|
snapshot,
|
||||||
|
Path::new("pylint").join(path).as_path(),
|
||||||
|
&LinterSettings {
|
||||||
|
preview: PreviewMode::Disabled,
|
||||||
|
..LinterSettings::for_rule(rule_code)
|
||||||
|
},
|
||||||
|
&LinterSettings {
|
||||||
|
preview: PreviewMode::Enabled,
|
||||||
|
..LinterSettings::for_rule(rule_code)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn continue_in_finally() -> Result<()> {
|
fn continue_in_finally() -> Result<()> {
|
||||||
let diagnostics = test_path(
|
let diagnostics = test_path(
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use itertools::Itertools;
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::{
|
use ruff_python_ast::{
|
||||||
BoolOp, CmpOp, Expr, ExprBoolOp, ExprCompare,
|
BoolOp, CmpOp, Expr, ExprBoolOp, ExprCompare,
|
||||||
parenthesize::{parentheses_iterator, parenthesized_range},
|
token::{parentheses_iterator, parenthesized_range},
|
||||||
};
|
};
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
|
|
@ -62,7 +62,7 @@ pub(crate) fn boolean_chained_comparison(checker: &Checker, expr_bool_op: &ExprB
|
||||||
}
|
}
|
||||||
|
|
||||||
let locator = checker.locator();
|
let locator = checker.locator();
|
||||||
let comment_ranges = checker.comment_ranges();
|
let tokens = checker.tokens();
|
||||||
|
|
||||||
// retrieve all compare expressions from boolean expression
|
// retrieve all compare expressions from boolean expression
|
||||||
let compare_expressions = expr_bool_op
|
let compare_expressions = expr_bool_op
|
||||||
|
|
@ -89,40 +89,22 @@ pub(crate) fn boolean_chained_comparison(checker: &Checker, expr_bool_op: &ExprB
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let left_paren_count = parentheses_iterator(
|
let left_paren_count =
|
||||||
left_compare.into(),
|
parentheses_iterator(left_compare.into(), Some(expr_bool_op.into()), tokens).count();
|
||||||
Some(expr_bool_op.into()),
|
|
||||||
comment_ranges,
|
|
||||||
locator.contents(),
|
|
||||||
)
|
|
||||||
.count();
|
|
||||||
|
|
||||||
let right_paren_count = parentheses_iterator(
|
let right_paren_count =
|
||||||
right_compare.into(),
|
parentheses_iterator(right_compare.into(), Some(expr_bool_op.into()), tokens).count();
|
||||||
Some(expr_bool_op.into()),
|
|
||||||
comment_ranges,
|
|
||||||
locator.contents(),
|
|
||||||
)
|
|
||||||
.count();
|
|
||||||
|
|
||||||
// Create the edit that removes the comparison operator
|
// Create the edit that removes the comparison operator
|
||||||
|
|
||||||
// In `a<(b) and ((b))<c`, we need to handle the
|
// In `a<(b) and ((b))<c`, we need to handle the
|
||||||
// parentheses when specifying the fix range.
|
// parentheses when specifying the fix range.
|
||||||
let left_compare_right_range = parenthesized_range(
|
let left_compare_right_range =
|
||||||
left_compare_right.into(),
|
parenthesized_range(left_compare_right.into(), left_compare.into(), tokens)
|
||||||
left_compare.into(),
|
.unwrap_or(left_compare_right.range());
|
||||||
comment_ranges,
|
let right_compare_left_range =
|
||||||
locator.contents(),
|
parenthesized_range(right_compare_left.into(), right_compare.into(), tokens)
|
||||||
)
|
.unwrap_or(right_compare_left.range());
|
||||||
.unwrap_or(left_compare_right.range());
|
|
||||||
let right_compare_left_range = parenthesized_range(
|
|
||||||
right_compare_left.into(),
|
|
||||||
right_compare.into(),
|
|
||||||
comment_ranges,
|
|
||||||
locator.contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(right_compare_left.range());
|
|
||||||
let edit = Edit::range_replacement(
|
let edit = Edit::range_replacement(
|
||||||
locator.slice(left_compare_right_range).to_string(),
|
locator.slice(left_compare_right_range).to_string(),
|
||||||
TextRange::new(
|
TextRange::new(
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ pub(crate) fn duplicate_bases(checker: &Checker, name: &str, arguments: Option<&
|
||||||
arguments,
|
arguments,
|
||||||
Parentheses::Remove,
|
Parentheses::Remove,
|
||||||
checker.locator().contents(),
|
checker.locator().contents(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)
|
)
|
||||||
.map(|edit| {
|
.map(|edit| {
|
||||||
Fix::applicable_edit(
|
Fix::applicable_edit(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::comparable::ComparableExpr;
|
use ruff_python_ast::comparable::ComparableExpr;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{self as ast, CmpOp, Stmt};
|
use ruff_python_ast::{self as ast, CmpOp, Stmt};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
|
|
@ -166,13 +166,8 @@ pub(crate) fn if_stmt_min_max(checker: &Checker, stmt_if: &ast::StmtIf) {
|
||||||
let replacement = format!(
|
let replacement = format!(
|
||||||
"{} = {min_max}({}, {})",
|
"{} = {min_max}({}, {})",
|
||||||
checker.locator().slice(
|
checker.locator().slice(
|
||||||
parenthesized_range(
|
parenthesized_range(body_target.into(), body.into(), checker.tokens())
|
||||||
body_target.into(),
|
.unwrap_or(body_target.range())
|
||||||
body.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents()
|
|
||||||
)
|
|
||||||
.unwrap_or(body_target.range())
|
|
||||||
),
|
),
|
||||||
checker.locator().slice(arg1),
|
checker.locator().slice(arg1),
|
||||||
checker.locator().slice(arg2),
|
checker.locator().slice(arg2),
|
||||||
|
|
|
||||||
|
|
@ -174,12 +174,8 @@ pub(crate) fn missing_maxsplit_arg(checker: &Checker, value: &Expr, slice: &Expr
|
||||||
SliceBoundary::Last => "rsplit",
|
SliceBoundary::Last => "rsplit",
|
||||||
};
|
};
|
||||||
|
|
||||||
let maxsplit_argument_edit = fix::edits::add_argument(
|
let maxsplit_argument_edit =
|
||||||
"maxsplit=1",
|
fix::edits::add_argument("maxsplit=1", arguments, checker.tokens());
|
||||||
arguments,
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Only change `actual_split_type` if it doesn't match `suggested_split_type`
|
// Only change `actual_split_type` if it doesn't match `suggested_split_type`
|
||||||
let split_type_edit: Option<Edit> = if actual_split_type == suggested_split_type {
|
let split_type_edit: Option<Edit> = if actual_split_type == suggested_split_type {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use ast::Expr;
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
use ruff_python_ast::comparable::ComparableExpr;
|
use ruff_python_ast::comparable::ComparableExpr;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{ExprBinOp, ExprRef, Operator};
|
use ruff_python_ast::{ExprBinOp, ExprRef, Operator};
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
|
|
@ -150,12 +150,10 @@ fn augmented_assignment(
|
||||||
|
|
||||||
let right_operand_ref = ExprRef::from(right_operand);
|
let right_operand_ref = ExprRef::from(right_operand);
|
||||||
let parent = original_expr.into();
|
let parent = original_expr.into();
|
||||||
let comment_ranges = checker.comment_ranges();
|
let tokens = checker.tokens();
|
||||||
let source = checker.source();
|
|
||||||
|
|
||||||
let right_operand_range =
|
let right_operand_range =
|
||||||
parenthesized_range(right_operand_ref, parent, comment_ranges, source)
|
parenthesized_range(right_operand_ref, parent, tokens).unwrap_or(right_operand.range());
|
||||||
.unwrap_or(right_operand.range());
|
|
||||||
let right_operand_expr = locator.slice(right_operand_range);
|
let right_operand_expr = locator.slice(right_operand_range);
|
||||||
|
|
||||||
let target_expr = locator.slice(target);
|
let target_expr = locator.slice(target);
|
||||||
|
|
|
||||||
|
|
@ -75,12 +75,7 @@ pub(crate) fn subprocess_run_without_check(checker: &Checker, call: &ast::ExprCa
|
||||||
let mut diagnostic =
|
let mut diagnostic =
|
||||||
checker.report_diagnostic(SubprocessRunWithoutCheck, call.func.range());
|
checker.report_diagnostic(SubprocessRunWithoutCheck, call.func.range());
|
||||||
diagnostic.set_fix(Fix::applicable_edit(
|
diagnostic.set_fix(Fix::applicable_edit(
|
||||||
add_argument(
|
add_argument("check=False", &call.arguments, checker.tokens()),
|
||||||
"check=False",
|
|
||||||
&call.arguments,
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
),
|
|
||||||
// If the function call contains `**kwargs`, mark the fix as unsafe.
|
// If the function call contains `**kwargs`, mark the fix as unsafe.
|
||||||
if call
|
if call
|
||||||
.arguments
|
.arguments
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::name::QualifiedName;
|
use ruff_python_ast::{self as ast, Expr, name::QualifiedName};
|
||||||
use ruff_python_ast::{self as ast, Expr};
|
|
||||||
use ruff_python_semantic::SemanticModel;
|
use ruff_python_semantic::SemanticModel;
|
||||||
use ruff_python_semantic::analyze::typing;
|
use ruff_python_semantic::analyze::typing;
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
@ -193,8 +192,7 @@ fn generate_keyword_fix(checker: &Checker, call: &ast::ExprCall) -> Fix {
|
||||||
}))
|
}))
|
||||||
),
|
),
|
||||||
&call.arguments,
|
&call.arguments,
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.locator().contents(),
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::{self as ast, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
use ruff_python_semantic::SemanticModel;
|
use ruff_python_semantic::{SemanticModel, analyze};
|
||||||
use ruff_python_stdlib::builtins;
|
use ruff_python_stdlib::builtins;
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
use crate::preview::is_custom_exception_checking_enabled;
|
||||||
use crate::{Edit, Fix, FixAvailability, Violation};
|
use crate::{Edit, Fix, FixAvailability, Violation};
|
||||||
use ruff_python_ast::PythonVersion;
|
use ruff_python_ast::PythonVersion;
|
||||||
|
|
||||||
|
|
@ -20,6 +21,9 @@ use ruff_python_ast::PythonVersion;
|
||||||
/// This rule only detects built-in exceptions, like `ValueError`, and does
|
/// This rule only detects built-in exceptions, like `ValueError`, and does
|
||||||
/// not catch user-defined exceptions.
|
/// not catch user-defined exceptions.
|
||||||
///
|
///
|
||||||
|
/// In [preview], this rule will also detect user-defined exceptions, but only
|
||||||
|
/// the ones defined in the file being checked.
|
||||||
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```python
|
/// ```python
|
||||||
/// ValueError("...")
|
/// ValueError("...")
|
||||||
|
|
@ -32,7 +36,8 @@ use ruff_python_ast::PythonVersion;
|
||||||
///
|
///
|
||||||
/// ## Fix safety
|
/// ## Fix safety
|
||||||
/// This rule's fix is marked as unsafe, as converting a useless exception
|
/// This rule's fix is marked as unsafe, as converting a useless exception
|
||||||
/// statement to a `raise` statement will change the program's behavior.
|
///
|
||||||
|
/// [preview]: https://docs.astral.sh/ruff/preview/
|
||||||
#[derive(ViolationMetadata)]
|
#[derive(ViolationMetadata)]
|
||||||
#[violation_metadata(stable_since = "0.5.0")]
|
#[violation_metadata(stable_since = "0.5.0")]
|
||||||
pub(crate) struct UselessExceptionStatement;
|
pub(crate) struct UselessExceptionStatement;
|
||||||
|
|
@ -56,7 +61,10 @@ pub(crate) fn useless_exception_statement(checker: &Checker, expr: &ast::StmtExp
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_builtin_exception(func, checker.semantic(), checker.target_version()) {
|
if is_builtin_exception(func, checker.semantic(), checker.target_version())
|
||||||
|
|| (is_custom_exception_checking_enabled(checker.settings())
|
||||||
|
&& is_custom_exception(func, checker.semantic(), checker.target_version()))
|
||||||
|
{
|
||||||
let mut diagnostic = checker.report_diagnostic(UselessExceptionStatement, expr.range());
|
let mut diagnostic = checker.report_diagnostic(UselessExceptionStatement, expr.range());
|
||||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
|
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
|
||||||
"raise ".to_string(),
|
"raise ".to_string(),
|
||||||
|
|
@ -78,3 +86,34 @@ fn is_builtin_exception(
|
||||||
if builtins::is_exception(name, target_version.minor))
|
if builtins::is_exception(name, target_version.minor))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the given expression is a custom exception.
|
||||||
|
fn is_custom_exception(
|
||||||
|
expr: &Expr,
|
||||||
|
semantic: &SemanticModel,
|
||||||
|
target_version: PythonVersion,
|
||||||
|
) -> bool {
|
||||||
|
let Some(qualified_name) = semantic.resolve_qualified_name(expr) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let Some(symbol) = qualified_name.segments().last() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let Some(binding_id) = semantic.lookup_symbol(symbol) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let binding = semantic.binding(binding_id);
|
||||||
|
let Some(source) = binding.source else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let statement = semantic.statement(source);
|
||||||
|
if let ast::Stmt::ClassDef(class_def) = statement {
|
||||||
|
return analyze::class::any_qualified_base_class(class_def, semantic, &|qualified_name| {
|
||||||
|
if let ["" | "builtins", name] = qualified_name.segments() {
|
||||||
|
return builtins::is_exception(name, target_version.minor);
|
||||||
|
}
|
||||||
|
false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,250 +2,294 @@
|
||||||
source: crates/ruff_linter/src/rules/pylint/mod.rs
|
source: crates/ruff_linter/src/rules/pylint/mod.rs
|
||||||
---
|
---
|
||||||
PLW0133 [*] Missing `raise` statement on exception
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
--> useless_exception_statement.py:7:5
|
--> useless_exception_statement.py:26:5
|
||||||
|
|
|
||||||
5 | # Test case 1: Useless exception statement
|
|
||||||
6 | def func():
|
|
||||||
7 | AssertionError("This is an assertion error") # PLW0133
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
help: Add `raise` keyword
|
|
||||||
4 |
|
|
||||||
5 | # Test case 1: Useless exception statement
|
|
||||||
6 | def func():
|
|
||||||
- AssertionError("This is an assertion error") # PLW0133
|
|
||||||
7 + raise AssertionError("This is an assertion error") # PLW0133
|
|
||||||
8 |
|
|
||||||
9 |
|
|
||||||
10 | # Test case 2: Useless exception statement in try-except block
|
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
|
||||||
|
|
||||||
PLW0133 [*] Missing `raise` statement on exception
|
|
||||||
--> useless_exception_statement.py:13:9
|
|
||||||
|
|
|
|
||||||
11 | def func():
|
24 | # Test case 1: Useless exception statement
|
||||||
12 | try:
|
|
||||||
13 | Exception("This is an exception") # PLW0133
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
14 | except Exception as err:
|
|
||||||
15 | pass
|
|
||||||
|
|
|
||||||
help: Add `raise` keyword
|
|
||||||
10 | # Test case 2: Useless exception statement in try-except block
|
|
||||||
11 | def func():
|
|
||||||
12 | try:
|
|
||||||
- Exception("This is an exception") # PLW0133
|
|
||||||
13 + raise Exception("This is an exception") # PLW0133
|
|
||||||
14 | except Exception as err:
|
|
||||||
15 | pass
|
|
||||||
16 |
|
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
|
||||||
|
|
||||||
PLW0133 [*] Missing `raise` statement on exception
|
|
||||||
--> useless_exception_statement.py:21:9
|
|
||||||
|
|
|
||||||
19 | def func():
|
|
||||||
20 | if True:
|
|
||||||
21 | RuntimeError("This is an exception") # PLW0133
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
help: Add `raise` keyword
|
|
||||||
18 | # Test case 3: Useless exception statement in if statement
|
|
||||||
19 | def func():
|
|
||||||
20 | if True:
|
|
||||||
- RuntimeError("This is an exception") # PLW0133
|
|
||||||
21 + raise RuntimeError("This is an exception") # PLW0133
|
|
||||||
22 |
|
|
||||||
23 |
|
|
||||||
24 | # Test case 4: Useless exception statement in class
|
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
|
||||||
|
|
||||||
PLW0133 [*] Missing `raise` statement on exception
|
|
||||||
--> useless_exception_statement.py:28:13
|
|
||||||
|
|
|
||||||
26 | class Class:
|
|
||||||
27 | def __init__(self):
|
|
||||||
28 | TypeError("This is an exception") # PLW0133
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
help: Add `raise` keyword
|
|
||||||
25 | def func():
|
25 | def func():
|
||||||
26 | class Class:
|
26 | AssertionError("This is an assertion error") # PLW0133
|
||||||
27 | def __init__(self):
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
27 | MyError("This is a custom error") # PLW0133
|
||||||
|
28 | MySubError("This is a custom error") # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
23 |
|
||||||
|
24 | # Test case 1: Useless exception statement
|
||||||
|
25 | def func():
|
||||||
|
- AssertionError("This is an assertion error") # PLW0133
|
||||||
|
26 + raise AssertionError("This is an assertion error") # PLW0133
|
||||||
|
27 | MyError("This is a custom error") # PLW0133
|
||||||
|
28 | MySubError("This is a custom error") # PLW0133
|
||||||
|
29 | MyValueError("This is a custom value error") # PLW0133
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:35:9
|
||||||
|
|
|
||||||
|
33 | def func():
|
||||||
|
34 | try:
|
||||||
|
35 | Exception("This is an exception") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
36 | MyError("This is an exception") # PLW0133
|
||||||
|
37 | MySubError("This is an exception") # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
32 | # Test case 2: Useless exception statement in try-except block
|
||||||
|
33 | def func():
|
||||||
|
34 | try:
|
||||||
|
- Exception("This is an exception") # PLW0133
|
||||||
|
35 + raise Exception("This is an exception") # PLW0133
|
||||||
|
36 | MyError("This is an exception") # PLW0133
|
||||||
|
37 | MySubError("This is an exception") # PLW0133
|
||||||
|
38 | MyValueError("This is an exception") # PLW0133
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:46:9
|
||||||
|
|
|
||||||
|
44 | def func():
|
||||||
|
45 | if True:
|
||||||
|
46 | RuntimeError("This is an exception") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
47 | MyError("This is an exception") # PLW0133
|
||||||
|
48 | MySubError("This is an exception") # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
43 | # Test case 3: Useless exception statement in if statement
|
||||||
|
44 | def func():
|
||||||
|
45 | if True:
|
||||||
|
- RuntimeError("This is an exception") # PLW0133
|
||||||
|
46 + raise RuntimeError("This is an exception") # PLW0133
|
||||||
|
47 | MyError("This is an exception") # PLW0133
|
||||||
|
48 | MySubError("This is an exception") # PLW0133
|
||||||
|
49 | MyValueError("This is an exception") # PLW0133
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:56:13
|
||||||
|
|
|
||||||
|
54 | class Class:
|
||||||
|
55 | def __init__(self):
|
||||||
|
56 | TypeError("This is an exception") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
57 | MyError("This is an exception") # PLW0133
|
||||||
|
58 | MySubError("This is an exception") # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
53 | def func():
|
||||||
|
54 | class Class:
|
||||||
|
55 | def __init__(self):
|
||||||
- TypeError("This is an exception") # PLW0133
|
- TypeError("This is an exception") # PLW0133
|
||||||
28 + raise TypeError("This is an exception") # PLW0133
|
56 + raise TypeError("This is an exception") # PLW0133
|
||||||
29 |
|
57 | MyError("This is an exception") # PLW0133
|
||||||
30 |
|
58 | MySubError("This is an exception") # PLW0133
|
||||||
31 | # Test case 5: Useless exception statement in function
|
59 | MyValueError("This is an exception") # PLW0133
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
PLW0133 [*] Missing `raise` statement on exception
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
--> useless_exception_statement.py:34:9
|
--> useless_exception_statement.py:65:9
|
||||||
|
|
|
|
||||||
32 | def func():
|
63 | def func():
|
||||||
33 | def inner():
|
64 | def inner():
|
||||||
34 | IndexError("This is an exception") # PLW0133
|
65 | IndexError("This is an exception") # PLW0133
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
35 |
|
66 | MyError("This is an exception") # PLW0133
|
||||||
36 | inner()
|
67 | MySubError("This is an exception") # PLW0133
|
||||||
|
|
|
|
||||||
help: Add `raise` keyword
|
help: Add `raise` keyword
|
||||||
31 | # Test case 5: Useless exception statement in function
|
62 | # Test case 5: Useless exception statement in function
|
||||||
32 | def func():
|
63 | def func():
|
||||||
33 | def inner():
|
64 | def inner():
|
||||||
- IndexError("This is an exception") # PLW0133
|
- IndexError("This is an exception") # PLW0133
|
||||||
34 + raise IndexError("This is an exception") # PLW0133
|
65 + raise IndexError("This is an exception") # PLW0133
|
||||||
35 |
|
66 | MyError("This is an exception") # PLW0133
|
||||||
36 | inner()
|
67 | MySubError("This is an exception") # PLW0133
|
||||||
37 |
|
68 | MyValueError("This is an exception") # PLW0133
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
PLW0133 [*] Missing `raise` statement on exception
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
--> useless_exception_statement.py:42:9
|
--> useless_exception_statement.py:76:9
|
||||||
|
|
|
|
||||||
40 | def func():
|
74 | def func():
|
||||||
41 | while True:
|
75 | while True:
|
||||||
42 | KeyError("This is an exception") # PLW0133
|
76 | KeyError("This is an exception") # PLW0133
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
77 | MyError("This is an exception") # PLW0133
|
||||||
|
78 | MySubError("This is an exception") # PLW0133
|
||||||
|
|
|
|
||||||
help: Add `raise` keyword
|
help: Add `raise` keyword
|
||||||
39 | # Test case 6: Useless exception statement in while loop
|
73 | # Test case 6: Useless exception statement in while loop
|
||||||
40 | def func():
|
74 | def func():
|
||||||
41 | while True:
|
75 | while True:
|
||||||
- KeyError("This is an exception") # PLW0133
|
- KeyError("This is an exception") # PLW0133
|
||||||
42 + raise KeyError("This is an exception") # PLW0133
|
76 + raise KeyError("This is an exception") # PLW0133
|
||||||
43 |
|
77 | MyError("This is an exception") # PLW0133
|
||||||
44 |
|
78 | MySubError("This is an exception") # PLW0133
|
||||||
45 | # Test case 7: Useless exception statement in abstract class
|
79 | MyValueError("This is an exception") # PLW0133
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
PLW0133 [*] Missing `raise` statement on exception
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
--> useless_exception_statement.py:50:13
|
--> useless_exception_statement.py:87:13
|
||||||
|
|
|
|
||||||
48 | @abstractmethod
|
85 | @abstractmethod
|
||||||
49 | def method(self):
|
86 | def method(self):
|
||||||
50 | NotImplementedError("This is an exception") # PLW0133
|
87 | NotImplementedError("This is an exception") # PLW0133
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
88 | MyError("This is an exception") # PLW0133
|
||||||
|
89 | MySubError("This is an exception") # PLW0133
|
||||||
|
|
|
|
||||||
help: Add `raise` keyword
|
help: Add `raise` keyword
|
||||||
47 | class Class(ABC):
|
84 | class Class(ABC):
|
||||||
48 | @abstractmethod
|
85 | @abstractmethod
|
||||||
49 | def method(self):
|
86 | def method(self):
|
||||||
- NotImplementedError("This is an exception") # PLW0133
|
- NotImplementedError("This is an exception") # PLW0133
|
||||||
50 + raise NotImplementedError("This is an exception") # PLW0133
|
87 + raise NotImplementedError("This is an exception") # PLW0133
|
||||||
51 |
|
88 | MyError("This is an exception") # PLW0133
|
||||||
52 |
|
89 | MySubError("This is an exception") # PLW0133
|
||||||
53 | # Test case 8: Useless exception statement inside context manager
|
90 | MyValueError("This is an exception") # PLW0133
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
PLW0133 [*] Missing `raise` statement on exception
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
--> useless_exception_statement.py:56:9
|
--> useless_exception_statement.py:96:9
|
||||||
|
|
|
|
||||||
54 | def func():
|
94 | def func():
|
||||||
55 | with suppress(AttributeError):
|
95 | with suppress(Exception):
|
||||||
56 | AttributeError("This is an exception") # PLW0133
|
96 | AttributeError("This is an exception") # PLW0133
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
97 | MyError("This is an exception") # PLW0133
|
||||||
|
98 | MySubError("This is an exception") # PLW0133
|
||||||
|
|
|
|
||||||
help: Add `raise` keyword
|
help: Add `raise` keyword
|
||||||
53 | # Test case 8: Useless exception statement inside context manager
|
93 | # Test case 8: Useless exception statement inside context manager
|
||||||
54 | def func():
|
94 | def func():
|
||||||
55 | with suppress(AttributeError):
|
95 | with suppress(Exception):
|
||||||
- AttributeError("This is an exception") # PLW0133
|
- AttributeError("This is an exception") # PLW0133
|
||||||
56 + raise AttributeError("This is an exception") # PLW0133
|
96 + raise AttributeError("This is an exception") # PLW0133
|
||||||
57 |
|
97 | MyError("This is an exception") # PLW0133
|
||||||
58 |
|
98 | MySubError("This is an exception") # PLW0133
|
||||||
59 | # Test case 9: Useless exception statement in parentheses
|
99 | MyValueError("This is an exception") # PLW0133
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
PLW0133 [*] Missing `raise` statement on exception
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
--> useless_exception_statement.py:61:5
|
--> useless_exception_statement.py:104:5
|
||||||
|
|
|
||||||
59 | # Test case 9: Useless exception statement in parentheses
|
|
||||||
60 | def func():
|
|
||||||
61 | (RuntimeError("This is an exception")) # PLW0133
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
help: Add `raise` keyword
|
|
||||||
58 |
|
|
||||||
59 | # Test case 9: Useless exception statement in parentheses
|
|
||||||
60 | def func():
|
|
||||||
- (RuntimeError("This is an exception")) # PLW0133
|
|
||||||
61 + raise (RuntimeError("This is an exception")) # PLW0133
|
|
||||||
62 |
|
|
||||||
63 |
|
|
||||||
64 | # Test case 10: Useless exception statement in continuation
|
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
|
||||||
|
|
||||||
PLW0133 [*] Missing `raise` statement on exception
|
|
||||||
--> useless_exception_statement.py:66:12
|
|
||||||
|
|
|
||||||
64 | # Test case 10: Useless exception statement in continuation
|
|
||||||
65 | def func():
|
|
||||||
66 | x = 1; (RuntimeError("This is an exception")); y = 2 # PLW0133
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
help: Add `raise` keyword
|
|
||||||
63 |
|
|
||||||
64 | # Test case 10: Useless exception statement in continuation
|
|
||||||
65 | def func():
|
|
||||||
- x = 1; (RuntimeError("This is an exception")); y = 2 # PLW0133
|
|
||||||
66 + x = 1; raise (RuntimeError("This is an exception")); y = 2 # PLW0133
|
|
||||||
67 |
|
|
||||||
68 |
|
|
||||||
69 | # Test case 11: Useless warning statement
|
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
|
||||||
|
|
||||||
PLW0133 [*] Missing `raise` statement on exception
|
|
||||||
--> useless_exception_statement.py:71:5
|
|
||||||
|
|
|
||||||
69 | # Test case 11: Useless warning statement
|
|
||||||
70 | def func():
|
|
||||||
71 | UserWarning("This is an assertion error") # PLW0133
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
help: Add `raise` keyword
|
|
||||||
68 |
|
|
||||||
69 | # Test case 11: Useless warning statement
|
|
||||||
70 | def func():
|
|
||||||
- UserWarning("This is an assertion error") # PLW0133
|
|
||||||
71 + raise UserWarning("This is an assertion error") # PLW0133
|
|
||||||
72 |
|
|
||||||
73 |
|
|
||||||
74 | # Non-violation test cases: PLW0133
|
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
|
||||||
|
|
||||||
PLW0133 [*] Missing `raise` statement on exception
|
|
||||||
--> useless_exception_statement.py:126:1
|
|
||||||
|
|
|
|
||||||
124 | import builtins
|
102 | # Test case 9: Useless exception statement in parentheses
|
||||||
125 |
|
103 | def func():
|
||||||
126 | builtins.TypeError("still an exception even though it's an Attribute")
|
104 | (RuntimeError("This is an exception")) # PLW0133
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
127 |
|
105 | (MyError("This is an exception")) # PLW0133
|
||||||
128 | PythonFinalizationError("Added in Python 3.13")
|
106 | (MySubError("This is an exception")) # PLW0133
|
||||||
|
|
|
|
||||||
help: Add `raise` keyword
|
help: Add `raise` keyword
|
||||||
|
101 |
|
||||||
|
102 | # Test case 9: Useless exception statement in parentheses
|
||||||
|
103 | def func():
|
||||||
|
- (RuntimeError("This is an exception")) # PLW0133
|
||||||
|
104 + raise (RuntimeError("This is an exception")) # PLW0133
|
||||||
|
105 | (MyError("This is an exception")) # PLW0133
|
||||||
|
106 | (MySubError("This is an exception")) # PLW0133
|
||||||
|
107 | (MyValueError("This is an exception")) # PLW0133
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:112:12
|
||||||
|
|
|
||||||
|
110 | # Test case 10: Useless exception statement in continuation
|
||||||
|
111 | def func():
|
||||||
|
112 | x = 1; (RuntimeError("This is an exception")); y = 2 # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
113 | x = 1; (MyError("This is an exception")); y = 2 # PLW0133
|
||||||
|
114 | x = 1; (MySubError("This is an exception")); y = 2 # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
109 |
|
||||||
|
110 | # Test case 10: Useless exception statement in continuation
|
||||||
|
111 | def func():
|
||||||
|
- x = 1; (RuntimeError("This is an exception")); y = 2 # PLW0133
|
||||||
|
112 + x = 1; raise (RuntimeError("This is an exception")); y = 2 # PLW0133
|
||||||
|
113 | x = 1; (MyError("This is an exception")); y = 2 # PLW0133
|
||||||
|
114 | x = 1; (MySubError("This is an exception")); y = 2 # PLW0133
|
||||||
|
115 | x = 1; (MyValueError("This is an exception")); y = 2 # PLW0133
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:120:5
|
||||||
|
|
|
||||||
|
118 | # Test case 11: Useless warning statement
|
||||||
|
119 | def func():
|
||||||
|
120 | UserWarning("This is a user warning") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
121 | MyUserWarning("This is a custom user warning") # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
117 |
|
||||||
|
118 | # Test case 11: Useless warning statement
|
||||||
|
119 | def func():
|
||||||
|
- UserWarning("This is a user warning") # PLW0133
|
||||||
|
120 + raise UserWarning("This is a user warning") # PLW0133
|
||||||
|
121 | MyUserWarning("This is a custom user warning") # PLW0133
|
||||||
|
122 |
|
||||||
123 |
|
123 |
|
||||||
124 | import builtins
|
|
||||||
125 |
|
|
||||||
- builtins.TypeError("still an exception even though it's an Attribute")
|
|
||||||
126 + raise builtins.TypeError("still an exception even though it's an Attribute")
|
|
||||||
127 |
|
|
||||||
128 | PythonFinalizationError("Added in Python 3.13")
|
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
PLW0133 [*] Missing `raise` statement on exception
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
--> useless_exception_statement.py:128:1
|
--> useless_exception_statement.py:127:1
|
||||||
|
|
|
|
||||||
126 | builtins.TypeError("still an exception even though it's an Attribute")
|
125 | import builtins
|
||||||
127 |
|
126 |
|
||||||
128 | PythonFinalizationError("Added in Python 3.13")
|
127 | builtins.TypeError("still an exception even though it's an Attribute") # PLW0133
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
128 |
|
||||||
|
129 | PythonFinalizationError("Added in Python 3.13") # PLW0133
|
||||||
|
|
|
|
||||||
help: Add `raise` keyword
|
help: Add `raise` keyword
|
||||||
125 |
|
124 | # Test case 12: Useless exception statement at module level
|
||||||
126 | builtins.TypeError("still an exception even though it's an Attribute")
|
125 | import builtins
|
||||||
127 |
|
126 |
|
||||||
- PythonFinalizationError("Added in Python 3.13")
|
- builtins.TypeError("still an exception even though it's an Attribute") # PLW0133
|
||||||
128 + raise PythonFinalizationError("Added in Python 3.13")
|
127 + raise builtins.TypeError("still an exception even though it's an Attribute") # PLW0133
|
||||||
|
128 |
|
||||||
|
129 | PythonFinalizationError("Added in Python 3.13") # PLW0133
|
||||||
|
130 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:129:1
|
||||||
|
|
|
||||||
|
127 | builtins.TypeError("still an exception even though it's an Attribute") # PLW0133
|
||||||
|
128 |
|
||||||
|
129 | PythonFinalizationError("Added in Python 3.13") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
130 |
|
||||||
|
131 | MyError("This is an exception") # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
126 |
|
||||||
|
127 | builtins.TypeError("still an exception even though it's an Attribute") # PLW0133
|
||||||
|
128 |
|
||||||
|
- PythonFinalizationError("Added in Python 3.13") # PLW0133
|
||||||
|
129 + raise PythonFinalizationError("Added in Python 3.13") # PLW0133
|
||||||
|
130 |
|
||||||
|
131 | MyError("This is an exception") # PLW0133
|
||||||
|
132 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:137:1
|
||||||
|
|
|
||||||
|
135 | MyValueError("This is an exception") # PLW0133
|
||||||
|
136 |
|
||||||
|
137 | UserWarning("This is a user warning") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
138 |
|
||||||
|
139 | MyUserWarning("This is a custom user warning") # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
134 |
|
||||||
|
135 | MyValueError("This is an exception") # PLW0133
|
||||||
|
136 |
|
||||||
|
- UserWarning("This is a user warning") # PLW0133
|
||||||
|
137 + raise UserWarning("This is a user warning") # PLW0133
|
||||||
|
138 |
|
||||||
|
139 | MyUserWarning("This is a custom user warning") # PLW0133
|
||||||
|
140 |
|
||||||
note: This is an unsafe fix and may change runtime behavior
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,751 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_linter/src/rules/pylint/mod.rs
|
||||||
|
---
|
||||||
|
--- Linter settings ---
|
||||||
|
-linter.preview = disabled
|
||||||
|
+linter.preview = enabled
|
||||||
|
|
||||||
|
--- Summary ---
|
||||||
|
Removed: 0
|
||||||
|
Added: 35
|
||||||
|
|
||||||
|
--- Added ---
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:27:5
|
||||||
|
|
|
||||||
|
25 | def func():
|
||||||
|
26 | AssertionError("This is an assertion error") # PLW0133
|
||||||
|
27 | MyError("This is a custom error") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
28 | MySubError("This is a custom error") # PLW0133
|
||||||
|
29 | MyValueError("This is a custom value error") # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
24 | # Test case 1: Useless exception statement
|
||||||
|
25 | def func():
|
||||||
|
26 | AssertionError("This is an assertion error") # PLW0133
|
||||||
|
- MyError("This is a custom error") # PLW0133
|
||||||
|
27 + raise MyError("This is a custom error") # PLW0133
|
||||||
|
28 | MySubError("This is a custom error") # PLW0133
|
||||||
|
29 | MyValueError("This is a custom value error") # PLW0133
|
||||||
|
30 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:28:5
|
||||||
|
|
|
||||||
|
26 | AssertionError("This is an assertion error") # PLW0133
|
||||||
|
27 | MyError("This is a custom error") # PLW0133
|
||||||
|
28 | MySubError("This is a custom error") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
29 | MyValueError("This is a custom value error") # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
25 | def func():
|
||||||
|
26 | AssertionError("This is an assertion error") # PLW0133
|
||||||
|
27 | MyError("This is a custom error") # PLW0133
|
||||||
|
- MySubError("This is a custom error") # PLW0133
|
||||||
|
28 + raise MySubError("This is a custom error") # PLW0133
|
||||||
|
29 | MyValueError("This is a custom value error") # PLW0133
|
||||||
|
30 |
|
||||||
|
31 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:29:5
|
||||||
|
|
|
||||||
|
27 | MyError("This is a custom error") # PLW0133
|
||||||
|
28 | MySubError("This is a custom error") # PLW0133
|
||||||
|
29 | MyValueError("This is a custom value error") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
26 | AssertionError("This is an assertion error") # PLW0133
|
||||||
|
27 | MyError("This is a custom error") # PLW0133
|
||||||
|
28 | MySubError("This is a custom error") # PLW0133
|
||||||
|
- MyValueError("This is a custom value error") # PLW0133
|
||||||
|
29 + raise MyValueError("This is a custom value error") # PLW0133
|
||||||
|
30 |
|
||||||
|
31 |
|
||||||
|
32 | # Test case 2: Useless exception statement in try-except block
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:36:9
|
||||||
|
|
|
||||||
|
34 | try:
|
||||||
|
35 | Exception("This is an exception") # PLW0133
|
||||||
|
36 | MyError("This is an exception") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
37 | MySubError("This is an exception") # PLW0133
|
||||||
|
38 | MyValueError("This is an exception") # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
33 | def func():
|
||||||
|
34 | try:
|
||||||
|
35 | Exception("This is an exception") # PLW0133
|
||||||
|
- MyError("This is an exception") # PLW0133
|
||||||
|
36 + raise MyError("This is an exception") # PLW0133
|
||||||
|
37 | MySubError("This is an exception") # PLW0133
|
||||||
|
38 | MyValueError("This is an exception") # PLW0133
|
||||||
|
39 | except Exception as err:
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:37:9
|
||||||
|
|
|
||||||
|
35 | Exception("This is an exception") # PLW0133
|
||||||
|
36 | MyError("This is an exception") # PLW0133
|
||||||
|
37 | MySubError("This is an exception") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
38 | MyValueError("This is an exception") # PLW0133
|
||||||
|
39 | except Exception as err:
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
34 | try:
|
||||||
|
35 | Exception("This is an exception") # PLW0133
|
||||||
|
36 | MyError("This is an exception") # PLW0133
|
||||||
|
- MySubError("This is an exception") # PLW0133
|
||||||
|
37 + raise MySubError("This is an exception") # PLW0133
|
||||||
|
38 | MyValueError("This is an exception") # PLW0133
|
||||||
|
39 | except Exception as err:
|
||||||
|
40 | pass
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:38:9
|
||||||
|
|
|
||||||
|
36 | MyError("This is an exception") # PLW0133
|
||||||
|
37 | MySubError("This is an exception") # PLW0133
|
||||||
|
38 | MyValueError("This is an exception") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
39 | except Exception as err:
|
||||||
|
40 | pass
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
35 | Exception("This is an exception") # PLW0133
|
||||||
|
36 | MyError("This is an exception") # PLW0133
|
||||||
|
37 | MySubError("This is an exception") # PLW0133
|
||||||
|
- MyValueError("This is an exception") # PLW0133
|
||||||
|
38 + raise MyValueError("This is an exception") # PLW0133
|
||||||
|
39 | except Exception as err:
|
||||||
|
40 | pass
|
||||||
|
41 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:47:9
|
||||||
|
|
|
||||||
|
45 | if True:
|
||||||
|
46 | RuntimeError("This is an exception") # PLW0133
|
||||||
|
47 | MyError("This is an exception") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
48 | MySubError("This is an exception") # PLW0133
|
||||||
|
49 | MyValueError("This is an exception") # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
44 | def func():
|
||||||
|
45 | if True:
|
||||||
|
46 | RuntimeError("This is an exception") # PLW0133
|
||||||
|
- MyError("This is an exception") # PLW0133
|
||||||
|
47 + raise MyError("This is an exception") # PLW0133
|
||||||
|
48 | MySubError("This is an exception") # PLW0133
|
||||||
|
49 | MyValueError("This is an exception") # PLW0133
|
||||||
|
50 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:48:9
|
||||||
|
|
|
||||||
|
46 | RuntimeError("This is an exception") # PLW0133
|
||||||
|
47 | MyError("This is an exception") # PLW0133
|
||||||
|
48 | MySubError("This is an exception") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
49 | MyValueError("This is an exception") # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
45 | if True:
|
||||||
|
46 | RuntimeError("This is an exception") # PLW0133
|
||||||
|
47 | MyError("This is an exception") # PLW0133
|
||||||
|
- MySubError("This is an exception") # PLW0133
|
||||||
|
48 + raise MySubError("This is an exception") # PLW0133
|
||||||
|
49 | MyValueError("This is an exception") # PLW0133
|
||||||
|
50 |
|
||||||
|
51 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:49:9
|
||||||
|
|
|
||||||
|
47 | MyError("This is an exception") # PLW0133
|
||||||
|
48 | MySubError("This is an exception") # PLW0133
|
||||||
|
49 | MyValueError("This is an exception") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
46 | RuntimeError("This is an exception") # PLW0133
|
||||||
|
47 | MyError("This is an exception") # PLW0133
|
||||||
|
48 | MySubError("This is an exception") # PLW0133
|
||||||
|
- MyValueError("This is an exception") # PLW0133
|
||||||
|
49 + raise MyValueError("This is an exception") # PLW0133
|
||||||
|
50 |
|
||||||
|
51 |
|
||||||
|
52 | # Test case 4: Useless exception statement in class
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:57:13
|
||||||
|
|
|
||||||
|
55 | def __init__(self):
|
||||||
|
56 | TypeError("This is an exception") # PLW0133
|
||||||
|
57 | MyError("This is an exception") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
58 | MySubError("This is an exception") # PLW0133
|
||||||
|
59 | MyValueError("This is an exception") # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
54 | class Class:
|
||||||
|
55 | def __init__(self):
|
||||||
|
56 | TypeError("This is an exception") # PLW0133
|
||||||
|
- MyError("This is an exception") # PLW0133
|
||||||
|
57 + raise MyError("This is an exception") # PLW0133
|
||||||
|
58 | MySubError("This is an exception") # PLW0133
|
||||||
|
59 | MyValueError("This is an exception") # PLW0133
|
||||||
|
60 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:58:13
|
||||||
|
|
|
||||||
|
56 | TypeError("This is an exception") # PLW0133
|
||||||
|
57 | MyError("This is an exception") # PLW0133
|
||||||
|
58 | MySubError("This is an exception") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
59 | MyValueError("This is an exception") # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
55 | def __init__(self):
|
||||||
|
56 | TypeError("This is an exception") # PLW0133
|
||||||
|
57 | MyError("This is an exception") # PLW0133
|
||||||
|
- MySubError("This is an exception") # PLW0133
|
||||||
|
58 + raise MySubError("This is an exception") # PLW0133
|
||||||
|
59 | MyValueError("This is an exception") # PLW0133
|
||||||
|
60 |
|
||||||
|
61 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:59:13
|
||||||
|
|
|
||||||
|
57 | MyError("This is an exception") # PLW0133
|
||||||
|
58 | MySubError("This is an exception") # PLW0133
|
||||||
|
59 | MyValueError("This is an exception") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
56 | TypeError("This is an exception") # PLW0133
|
||||||
|
57 | MyError("This is an exception") # PLW0133
|
||||||
|
58 | MySubError("This is an exception") # PLW0133
|
||||||
|
- MyValueError("This is an exception") # PLW0133
|
||||||
|
59 + raise MyValueError("This is an exception") # PLW0133
|
||||||
|
60 |
|
||||||
|
61 |
|
||||||
|
62 | # Test case 5: Useless exception statement in function
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:66:9
|
||||||
|
|
|
||||||
|
64 | def inner():
|
||||||
|
65 | IndexError("This is an exception") # PLW0133
|
||||||
|
66 | MyError("This is an exception") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
67 | MySubError("This is an exception") # PLW0133
|
||||||
|
68 | MyValueError("This is an exception") # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
63 | def func():
|
||||||
|
64 | def inner():
|
||||||
|
65 | IndexError("This is an exception") # PLW0133
|
||||||
|
- MyError("This is an exception") # PLW0133
|
||||||
|
66 + raise MyError("This is an exception") # PLW0133
|
||||||
|
67 | MySubError("This is an exception") # PLW0133
|
||||||
|
68 | MyValueError("This is an exception") # PLW0133
|
||||||
|
69 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:67:9
|
||||||
|
|
|
||||||
|
65 | IndexError("This is an exception") # PLW0133
|
||||||
|
66 | MyError("This is an exception") # PLW0133
|
||||||
|
67 | MySubError("This is an exception") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
68 | MyValueError("This is an exception") # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
64 | def inner():
|
||||||
|
65 | IndexError("This is an exception") # PLW0133
|
||||||
|
66 | MyError("This is an exception") # PLW0133
|
||||||
|
- MySubError("This is an exception") # PLW0133
|
||||||
|
67 + raise MySubError("This is an exception") # PLW0133
|
||||||
|
68 | MyValueError("This is an exception") # PLW0133
|
||||||
|
69 |
|
||||||
|
70 | inner()
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:68:9
|
||||||
|
|
|
||||||
|
66 | MyError("This is an exception") # PLW0133
|
||||||
|
67 | MySubError("This is an exception") # PLW0133
|
||||||
|
68 | MyValueError("This is an exception") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
69 |
|
||||||
|
70 | inner()
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
65 | IndexError("This is an exception") # PLW0133
|
||||||
|
66 | MyError("This is an exception") # PLW0133
|
||||||
|
67 | MySubError("This is an exception") # PLW0133
|
||||||
|
- MyValueError("This is an exception") # PLW0133
|
||||||
|
68 + raise MyValueError("This is an exception") # PLW0133
|
||||||
|
69 |
|
||||||
|
70 | inner()
|
||||||
|
71 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:77:9
|
||||||
|
|
|
||||||
|
75 | while True:
|
||||||
|
76 | KeyError("This is an exception") # PLW0133
|
||||||
|
77 | MyError("This is an exception") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
78 | MySubError("This is an exception") # PLW0133
|
||||||
|
79 | MyValueError("This is an exception") # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
74 | def func():
|
||||||
|
75 | while True:
|
||||||
|
76 | KeyError("This is an exception") # PLW0133
|
||||||
|
- MyError("This is an exception") # PLW0133
|
||||||
|
77 + raise MyError("This is an exception") # PLW0133
|
||||||
|
78 | MySubError("This is an exception") # PLW0133
|
||||||
|
79 | MyValueError("This is an exception") # PLW0133
|
||||||
|
80 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:78:9
|
||||||
|
|
|
||||||
|
76 | KeyError("This is an exception") # PLW0133
|
||||||
|
77 | MyError("This is an exception") # PLW0133
|
||||||
|
78 | MySubError("This is an exception") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
79 | MyValueError("This is an exception") # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
75 | while True:
|
||||||
|
76 | KeyError("This is an exception") # PLW0133
|
||||||
|
77 | MyError("This is an exception") # PLW0133
|
||||||
|
- MySubError("This is an exception") # PLW0133
|
||||||
|
78 + raise MySubError("This is an exception") # PLW0133
|
||||||
|
79 | MyValueError("This is an exception") # PLW0133
|
||||||
|
80 |
|
||||||
|
81 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:79:9
|
||||||
|
|
|
||||||
|
77 | MyError("This is an exception") # PLW0133
|
||||||
|
78 | MySubError("This is an exception") # PLW0133
|
||||||
|
79 | MyValueError("This is an exception") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
76 | KeyError("This is an exception") # PLW0133
|
||||||
|
77 | MyError("This is an exception") # PLW0133
|
||||||
|
78 | MySubError("This is an exception") # PLW0133
|
||||||
|
- MyValueError("This is an exception") # PLW0133
|
||||||
|
79 + raise MyValueError("This is an exception") # PLW0133
|
||||||
|
80 |
|
||||||
|
81 |
|
||||||
|
82 | # Test case 7: Useless exception statement in abstract class
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:88:13
|
||||||
|
|
|
||||||
|
86 | def method(self):
|
||||||
|
87 | NotImplementedError("This is an exception") # PLW0133
|
||||||
|
88 | MyError("This is an exception") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
89 | MySubError("This is an exception") # PLW0133
|
||||||
|
90 | MyValueError("This is an exception") # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
85 | @abstractmethod
|
||||||
|
86 | def method(self):
|
||||||
|
87 | NotImplementedError("This is an exception") # PLW0133
|
||||||
|
- MyError("This is an exception") # PLW0133
|
||||||
|
88 + raise MyError("This is an exception") # PLW0133
|
||||||
|
89 | MySubError("This is an exception") # PLW0133
|
||||||
|
90 | MyValueError("This is an exception") # PLW0133
|
||||||
|
91 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:89:13
|
||||||
|
|
|
||||||
|
87 | NotImplementedError("This is an exception") # PLW0133
|
||||||
|
88 | MyError("This is an exception") # PLW0133
|
||||||
|
89 | MySubError("This is an exception") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
90 | MyValueError("This is an exception") # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
86 | def method(self):
|
||||||
|
87 | NotImplementedError("This is an exception") # PLW0133
|
||||||
|
88 | MyError("This is an exception") # PLW0133
|
||||||
|
- MySubError("This is an exception") # PLW0133
|
||||||
|
89 + raise MySubError("This is an exception") # PLW0133
|
||||||
|
90 | MyValueError("This is an exception") # PLW0133
|
||||||
|
91 |
|
||||||
|
92 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:90:13
|
||||||
|
|
|
||||||
|
88 | MyError("This is an exception") # PLW0133
|
||||||
|
89 | MySubError("This is an exception") # PLW0133
|
||||||
|
90 | MyValueError("This is an exception") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
87 | NotImplementedError("This is an exception") # PLW0133
|
||||||
|
88 | MyError("This is an exception") # PLW0133
|
||||||
|
89 | MySubError("This is an exception") # PLW0133
|
||||||
|
- MyValueError("This is an exception") # PLW0133
|
||||||
|
90 + raise MyValueError("This is an exception") # PLW0133
|
||||||
|
91 |
|
||||||
|
92 |
|
||||||
|
93 | # Test case 8: Useless exception statement inside context manager
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:97:9
|
||||||
|
|
|
||||||
|
95 | with suppress(Exception):
|
||||||
|
96 | AttributeError("This is an exception") # PLW0133
|
||||||
|
97 | MyError("This is an exception") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
98 | MySubError("This is an exception") # PLW0133
|
||||||
|
99 | MyValueError("This is an exception") # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
94 | def func():
|
||||||
|
95 | with suppress(Exception):
|
||||||
|
96 | AttributeError("This is an exception") # PLW0133
|
||||||
|
- MyError("This is an exception") # PLW0133
|
||||||
|
97 + raise MyError("This is an exception") # PLW0133
|
||||||
|
98 | MySubError("This is an exception") # PLW0133
|
||||||
|
99 | MyValueError("This is an exception") # PLW0133
|
||||||
|
100 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:98:9
|
||||||
|
|
|
||||||
|
96 | AttributeError("This is an exception") # PLW0133
|
||||||
|
97 | MyError("This is an exception") # PLW0133
|
||||||
|
98 | MySubError("This is an exception") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
99 | MyValueError("This is an exception") # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
95 | with suppress(Exception):
|
||||||
|
96 | AttributeError("This is an exception") # PLW0133
|
||||||
|
97 | MyError("This is an exception") # PLW0133
|
||||||
|
- MySubError("This is an exception") # PLW0133
|
||||||
|
98 + raise MySubError("This is an exception") # PLW0133
|
||||||
|
99 | MyValueError("This is an exception") # PLW0133
|
||||||
|
100 |
|
||||||
|
101 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:99:9
|
||||||
|
|
|
||||||
|
97 | MyError("This is an exception") # PLW0133
|
||||||
|
98 | MySubError("This is an exception") # PLW0133
|
||||||
|
99 | MyValueError("This is an exception") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
96 | AttributeError("This is an exception") # PLW0133
|
||||||
|
97 | MyError("This is an exception") # PLW0133
|
||||||
|
98 | MySubError("This is an exception") # PLW0133
|
||||||
|
- MyValueError("This is an exception") # PLW0133
|
||||||
|
99 + raise MyValueError("This is an exception") # PLW0133
|
||||||
|
100 |
|
||||||
|
101 |
|
||||||
|
102 | # Test case 9: Useless exception statement in parentheses
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:105:5
|
||||||
|
|
|
||||||
|
103 | def func():
|
||||||
|
104 | (RuntimeError("This is an exception")) # PLW0133
|
||||||
|
105 | (MyError("This is an exception")) # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
106 | (MySubError("This is an exception")) # PLW0133
|
||||||
|
107 | (MyValueError("This is an exception")) # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
102 | # Test case 9: Useless exception statement in parentheses
|
||||||
|
103 | def func():
|
||||||
|
104 | (RuntimeError("This is an exception")) # PLW0133
|
||||||
|
- (MyError("This is an exception")) # PLW0133
|
||||||
|
105 + raise (MyError("This is an exception")) # PLW0133
|
||||||
|
106 | (MySubError("This is an exception")) # PLW0133
|
||||||
|
107 | (MyValueError("This is an exception")) # PLW0133
|
||||||
|
108 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:106:5
|
||||||
|
|
|
||||||
|
104 | (RuntimeError("This is an exception")) # PLW0133
|
||||||
|
105 | (MyError("This is an exception")) # PLW0133
|
||||||
|
106 | (MySubError("This is an exception")) # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
107 | (MyValueError("This is an exception")) # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
103 | def func():
|
||||||
|
104 | (RuntimeError("This is an exception")) # PLW0133
|
||||||
|
105 | (MyError("This is an exception")) # PLW0133
|
||||||
|
- (MySubError("This is an exception")) # PLW0133
|
||||||
|
106 + raise (MySubError("This is an exception")) # PLW0133
|
||||||
|
107 | (MyValueError("This is an exception")) # PLW0133
|
||||||
|
108 |
|
||||||
|
109 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:107:5
|
||||||
|
|
|
||||||
|
105 | (MyError("This is an exception")) # PLW0133
|
||||||
|
106 | (MySubError("This is an exception")) # PLW0133
|
||||||
|
107 | (MyValueError("This is an exception")) # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
104 | (RuntimeError("This is an exception")) # PLW0133
|
||||||
|
105 | (MyError("This is an exception")) # PLW0133
|
||||||
|
106 | (MySubError("This is an exception")) # PLW0133
|
||||||
|
- (MyValueError("This is an exception")) # PLW0133
|
||||||
|
107 + raise (MyValueError("This is an exception")) # PLW0133
|
||||||
|
108 |
|
||||||
|
109 |
|
||||||
|
110 | # Test case 10: Useless exception statement in continuation
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:113:12
|
||||||
|
|
|
||||||
|
111 | def func():
|
||||||
|
112 | x = 1; (RuntimeError("This is an exception")); y = 2 # PLW0133
|
||||||
|
113 | x = 1; (MyError("This is an exception")); y = 2 # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
114 | x = 1; (MySubError("This is an exception")); y = 2 # PLW0133
|
||||||
|
115 | x = 1; (MyValueError("This is an exception")); y = 2 # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
110 | # Test case 10: Useless exception statement in continuation
|
||||||
|
111 | def func():
|
||||||
|
112 | x = 1; (RuntimeError("This is an exception")); y = 2 # PLW0133
|
||||||
|
- x = 1; (MyError("This is an exception")); y = 2 # PLW0133
|
||||||
|
113 + x = 1; raise (MyError("This is an exception")); y = 2 # PLW0133
|
||||||
|
114 | x = 1; (MySubError("This is an exception")); y = 2 # PLW0133
|
||||||
|
115 | x = 1; (MyValueError("This is an exception")); y = 2 # PLW0133
|
||||||
|
116 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:114:12
|
||||||
|
|
|
||||||
|
112 | x = 1; (RuntimeError("This is an exception")); y = 2 # PLW0133
|
||||||
|
113 | x = 1; (MyError("This is an exception")); y = 2 # PLW0133
|
||||||
|
114 | x = 1; (MySubError("This is an exception")); y = 2 # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
115 | x = 1; (MyValueError("This is an exception")); y = 2 # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
111 | def func():
|
||||||
|
112 | x = 1; (RuntimeError("This is an exception")); y = 2 # PLW0133
|
||||||
|
113 | x = 1; (MyError("This is an exception")); y = 2 # PLW0133
|
||||||
|
- x = 1; (MySubError("This is an exception")); y = 2 # PLW0133
|
||||||
|
114 + x = 1; raise (MySubError("This is an exception")); y = 2 # PLW0133
|
||||||
|
115 | x = 1; (MyValueError("This is an exception")); y = 2 # PLW0133
|
||||||
|
116 |
|
||||||
|
117 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:115:12
|
||||||
|
|
|
||||||
|
113 | x = 1; (MyError("This is an exception")); y = 2 # PLW0133
|
||||||
|
114 | x = 1; (MySubError("This is an exception")); y = 2 # PLW0133
|
||||||
|
115 | x = 1; (MyValueError("This is an exception")); y = 2 # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
112 | x = 1; (RuntimeError("This is an exception")); y = 2 # PLW0133
|
||||||
|
113 | x = 1; (MyError("This is an exception")); y = 2 # PLW0133
|
||||||
|
114 | x = 1; (MySubError("This is an exception")); y = 2 # PLW0133
|
||||||
|
- x = 1; (MyValueError("This is an exception")); y = 2 # PLW0133
|
||||||
|
115 + x = 1; raise (MyValueError("This is an exception")); y = 2 # PLW0133
|
||||||
|
116 |
|
||||||
|
117 |
|
||||||
|
118 | # Test case 11: Useless warning statement
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:121:5
|
||||||
|
|
|
||||||
|
119 | def func():
|
||||||
|
120 | UserWarning("This is a user warning") # PLW0133
|
||||||
|
121 | MyUserWarning("This is a custom user warning") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
118 | # Test case 11: Useless warning statement
|
||||||
|
119 | def func():
|
||||||
|
120 | UserWarning("This is a user warning") # PLW0133
|
||||||
|
- MyUserWarning("This is a custom user warning") # PLW0133
|
||||||
|
121 + raise MyUserWarning("This is a custom user warning") # PLW0133
|
||||||
|
122 |
|
||||||
|
123 |
|
||||||
|
124 | # Test case 12: Useless exception statement at module level
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:131:1
|
||||||
|
|
|
||||||
|
129 | PythonFinalizationError("Added in Python 3.13") # PLW0133
|
||||||
|
130 |
|
||||||
|
131 | MyError("This is an exception") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
132 |
|
||||||
|
133 | MySubError("This is an exception") # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
128 |
|
||||||
|
129 | PythonFinalizationError("Added in Python 3.13") # PLW0133
|
||||||
|
130 |
|
||||||
|
- MyError("This is an exception") # PLW0133
|
||||||
|
131 + raise MyError("This is an exception") # PLW0133
|
||||||
|
132 |
|
||||||
|
133 | MySubError("This is an exception") # PLW0133
|
||||||
|
134 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:133:1
|
||||||
|
|
|
||||||
|
131 | MyError("This is an exception") # PLW0133
|
||||||
|
132 |
|
||||||
|
133 | MySubError("This is an exception") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
134 |
|
||||||
|
135 | MyValueError("This is an exception") # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
130 |
|
||||||
|
131 | MyError("This is an exception") # PLW0133
|
||||||
|
132 |
|
||||||
|
- MySubError("This is an exception") # PLW0133
|
||||||
|
133 + raise MySubError("This is an exception") # PLW0133
|
||||||
|
134 |
|
||||||
|
135 | MyValueError("This is an exception") # PLW0133
|
||||||
|
136 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:135:1
|
||||||
|
|
|
||||||
|
133 | MySubError("This is an exception") # PLW0133
|
||||||
|
134 |
|
||||||
|
135 | MyValueError("This is an exception") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
136 |
|
||||||
|
137 | UserWarning("This is a user warning") # PLW0133
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
132 |
|
||||||
|
133 | MySubError("This is an exception") # PLW0133
|
||||||
|
134 |
|
||||||
|
- MyValueError("This is an exception") # PLW0133
|
||||||
|
135 + raise MyValueError("This is an exception") # PLW0133
|
||||||
|
136 |
|
||||||
|
137 | UserWarning("This is a user warning") # PLW0133
|
||||||
|
138 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
PLW0133 [*] Missing `raise` statement on exception
|
||||||
|
--> useless_exception_statement.py:139:1
|
||||||
|
|
|
||||||
|
137 | UserWarning("This is a user warning") # PLW0133
|
||||||
|
138 |
|
||||||
|
139 | MyUserWarning("This is a custom user warning") # PLW0133
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: Add `raise` keyword
|
||||||
|
136 |
|
||||||
|
137 | UserWarning("This is a user warning") # PLW0133
|
||||||
|
138 |
|
||||||
|
- MyUserWarning("This is a custom user warning") # PLW0133
|
||||||
|
139 + raise MyUserWarning("This is a custom user warning") # PLW0133
|
||||||
|
140 |
|
||||||
|
141 |
|
||||||
|
142 | # Non-violation test cases: PLW0133
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
@ -204,7 +204,7 @@ pub(crate) fn non_pep695_generic_class(checker: &Checker, class_def: &StmtClassD
|
||||||
arguments,
|
arguments,
|
||||||
Parentheses::Remove,
|
Parentheses::Remove,
|
||||||
checker.source(),
|
checker.source(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)?;
|
)?;
|
||||||
Ok(Fix::unsafe_edits(
|
Ok(Fix::unsafe_edits(
|
||||||
Edit::insertion(type_params.to_string(), name.end()),
|
Edit::insertion(type_params.to_string(), name.end()),
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use itertools::Itertools;
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::name::Name;
|
use ruff_python_ast::name::Name;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::visitor::Visitor;
|
use ruff_python_ast::visitor::Visitor;
|
||||||
use ruff_python_ast::{Expr, ExprCall, ExprName, Keyword, StmtAnnAssign, StmtAssign, StmtRef};
|
use ruff_python_ast::{Expr, ExprCall, ExprName, Keyword, StmtAnnAssign, StmtAssign, StmtRef};
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
@ -261,11 +261,11 @@ fn create_diagnostic(
|
||||||
type_alias_kind: TypeAliasKind,
|
type_alias_kind: TypeAliasKind,
|
||||||
) {
|
) {
|
||||||
let source = checker.source();
|
let source = checker.source();
|
||||||
|
let tokens = checker.tokens();
|
||||||
let comment_ranges = checker.comment_ranges();
|
let comment_ranges = checker.comment_ranges();
|
||||||
|
|
||||||
let range_with_parentheses =
|
let range_with_parentheses =
|
||||||
parenthesized_range(value.into(), stmt.into(), comment_ranges, source)
|
parenthesized_range(value.into(), stmt.into(), tokens).unwrap_or(value.range());
|
||||||
.unwrap_or(value.range());
|
|
||||||
|
|
||||||
let content = format!(
|
let content = format!(
|
||||||
"type {name}{type_params} = {value}",
|
"type {name}{type_params} = {value}",
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::{self as ast, Keyword};
|
use ruff_python_ast::{self as ast, Keyword, token::Tokens};
|
||||||
use ruff_python_semantic::Modules;
|
use ruff_python_semantic::Modules;
|
||||||
use ruff_python_trivia::CommentRanges;
|
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
@ -104,7 +103,7 @@ pub(crate) fn replace_stdout_stderr(checker: &Checker, call: &ast::ExprCall) {
|
||||||
stderr,
|
stderr,
|
||||||
call,
|
call,
|
||||||
checker.locator().contents(),
|
checker.locator().contents(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -117,7 +116,7 @@ fn generate_fix(
|
||||||
stderr: &Keyword,
|
stderr: &Keyword,
|
||||||
call: &ast::ExprCall,
|
call: &ast::ExprCall,
|
||||||
source: &str,
|
source: &str,
|
||||||
comment_ranges: &CommentRanges,
|
tokens: &Tokens,
|
||||||
) -> Result<Fix> {
|
) -> Result<Fix> {
|
||||||
let (first, second) = if stdout.start() < stderr.start() {
|
let (first, second) = if stdout.start() < stderr.start() {
|
||||||
(stdout, stderr)
|
(stdout, stderr)
|
||||||
|
|
@ -132,7 +131,7 @@ fn generate_fix(
|
||||||
&call.arguments,
|
&call.arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
source,
|
source,
|
||||||
comment_ranges,
|
tokens,
|
||||||
)?],
|
)?],
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ pub(crate) fn replace_universal_newlines(checker: &Checker, call: &ast::ExprCall
|
||||||
&call.arguments,
|
&call.arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
checker.locator().contents(),
|
checker.locator().contents(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)
|
)
|
||||||
.map(Fix::safe_edit)
|
.map(Fix::safe_edit)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -188,7 +188,7 @@ pub(crate) fn unnecessary_encode_utf8(checker: &Checker, call: &ast::ExprCall) {
|
||||||
&call.arguments,
|
&call.arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
checker.locator().contents(),
|
checker.locator().contents(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)
|
)
|
||||||
.map(Fix::safe_edit)
|
.map(Fix::safe_edit)
|
||||||
});
|
});
|
||||||
|
|
@ -206,7 +206,7 @@ pub(crate) fn unnecessary_encode_utf8(checker: &Checker, call: &ast::ExprCall) {
|
||||||
&call.arguments,
|
&call.arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
checker.locator().contents(),
|
checker.locator().contents(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)
|
)
|
||||||
.map(Fix::safe_edit)
|
.map(Fix::safe_edit)
|
||||||
});
|
});
|
||||||
|
|
@ -231,7 +231,7 @@ pub(crate) fn unnecessary_encode_utf8(checker: &Checker, call: &ast::ExprCall) {
|
||||||
&call.arguments,
|
&call.arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
checker.locator().contents(),
|
checker.locator().contents(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)
|
)
|
||||||
.map(Fix::safe_edit)
|
.map(Fix::safe_edit)
|
||||||
});
|
});
|
||||||
|
|
@ -249,7 +249,7 @@ pub(crate) fn unnecessary_encode_utf8(checker: &Checker, call: &ast::ExprCall) {
|
||||||
&call.arguments,
|
&call.arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
checker.locator().contents(),
|
checker.locator().contents(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)
|
)
|
||||||
.map(Fix::safe_edit)
|
.map(Fix::safe_edit)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ pub(crate) fn useless_class_metaclass_type(checker: &Checker, class_def: &StmtCl
|
||||||
arguments,
|
arguments,
|
||||||
Parentheses::Remove,
|
Parentheses::Remove,
|
||||||
checker.locator().contents(),
|
checker.locator().contents(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let range = edit.range();
|
let range = edit.range();
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ pub(crate) fn useless_object_inheritance(checker: &Checker, class_def: &ast::Stm
|
||||||
arguments,
|
arguments,
|
||||||
Parentheses::Remove,
|
Parentheses::Remove,
|
||||||
checker.locator().contents(),
|
checker.locator().contents(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let range = edit.range();
|
let range = edit.range();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{self as ast, Expr, Stmt};
|
use ruff_python_ast::{self as ast, Expr, Stmt};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
|
|
@ -139,13 +139,8 @@ pub(crate) fn yield_in_for_loop(checker: &Checker, stmt_for: &ast::StmtFor) {
|
||||||
let mut diagnostic = checker.report_diagnostic(YieldInForLoop, stmt_for.range());
|
let mut diagnostic = checker.report_diagnostic(YieldInForLoop, stmt_for.range());
|
||||||
|
|
||||||
let contents = checker.locator().slice(
|
let contents = checker.locator().slice(
|
||||||
parenthesized_range(
|
parenthesized_range(iter.as_ref().into(), stmt_for.into(), checker.tokens())
|
||||||
iter.as_ref().into(),
|
.unwrap_or(iter.range()),
|
||||||
stmt_for.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(iter.range()),
|
|
||||||
);
|
);
|
||||||
let contents = if iter.as_tuple_expr().is_some_and(|it| !it.parenthesized) {
|
let contents = if iter.as_tuple_expr().is_some_and(|it| !it.parenthesized) {
|
||||||
format!("yield from ({contents})")
|
format!("yield from ({contents})")
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use ruff_python_ast::PythonVersion;
|
use ruff_python_ast::PythonVersion;
|
||||||
use ruff_python_ast::{self as ast, Expr, name::Name, parenthesize::parenthesized_range};
|
use ruff_python_ast::{self as ast, Expr, name::Name, token::parenthesized_range};
|
||||||
use ruff_python_codegen::Generator;
|
use ruff_python_codegen::Generator;
|
||||||
use ruff_python_semantic::{BindingId, ResolvedReference, SemanticModel};
|
use ruff_python_semantic::{BindingId, ResolvedReference, SemanticModel};
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
@ -330,12 +330,8 @@ pub(super) fn parenthesize_loop_iter_if_necessary<'a>(
|
||||||
let locator = checker.locator();
|
let locator = checker.locator();
|
||||||
let iter = for_stmt.iter.as_ref();
|
let iter = for_stmt.iter.as_ref();
|
||||||
|
|
||||||
let original_parenthesized_range = parenthesized_range(
|
let original_parenthesized_range =
|
||||||
iter.into(),
|
parenthesized_range(iter.into(), for_stmt.into(), checker.tokens());
|
||||||
for_stmt.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.source(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(range) = original_parenthesized_range {
|
if let Some(range) = original_parenthesized_range {
|
||||||
return Cow::Borrowed(locator.slice(range));
|
return Cow::Borrowed(locator.slice(range));
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{
|
use ruff_python_ast::{
|
||||||
Expr, ExprAttribute, ExprBinOp, ExprCall, ExprStringLiteral, ExprSubscript, ExprUnaryOp,
|
Expr, ExprAttribute, ExprBinOp, ExprCall, ExprStringLiteral, ExprSubscript, ExprUnaryOp,
|
||||||
Number, Operator, PythonVersion, UnaryOp,
|
Number, Operator, PythonVersion, UnaryOp,
|
||||||
|
|
@ -112,8 +112,7 @@ pub(crate) fn fromisoformat_replace_z(checker: &Checker, call: &ExprCall) {
|
||||||
let value_full_range = parenthesized_range(
|
let value_full_range = parenthesized_range(
|
||||||
replace_time_zone.date.into(),
|
replace_time_zone.date.into(),
|
||||||
replace_time_zone.parent.into(),
|
replace_time_zone.parent.into(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.source(),
|
|
||||||
)
|
)
|
||||||
.unwrap_or(replace_time_zone.date.range());
|
.unwrap_or(replace_time_zone.date.range());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,7 @@ use ruff_python_ast as ast;
|
||||||
use ruff_python_ast::Expr;
|
use ruff_python_ast::Expr;
|
||||||
use ruff_python_ast::comparable::ComparableExpr;
|
use ruff_python_ast::comparable::ComparableExpr;
|
||||||
use ruff_python_ast::helpers::contains_effect;
|
use ruff_python_ast::helpers::contains_effect;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::{Tokens, parenthesized_range};
|
||||||
use ruff_python_trivia::CommentRanges;
|
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::Locator;
|
use crate::Locator;
|
||||||
|
|
@ -76,8 +75,8 @@ pub(crate) fn if_exp_instead_of_or_operator(checker: &Checker, if_expr: &ast::Ex
|
||||||
Edit::range_replacement(
|
Edit::range_replacement(
|
||||||
format!(
|
format!(
|
||||||
"{} or {}",
|
"{} or {}",
|
||||||
parenthesize_test(test, if_expr, checker.comment_ranges(), checker.locator()),
|
parenthesize_test(test, if_expr, checker.tokens(), checker.locator()),
|
||||||
parenthesize_test(orelse, if_expr, checker.comment_ranges(), checker.locator()),
|
parenthesize_test(orelse, if_expr, checker.tokens(), checker.locator()),
|
||||||
),
|
),
|
||||||
if_expr.range(),
|
if_expr.range(),
|
||||||
),
|
),
|
||||||
|
|
@ -99,15 +98,10 @@ pub(crate) fn if_exp_instead_of_or_operator(checker: &Checker, if_expr: &ast::Ex
|
||||||
fn parenthesize_test<'a>(
|
fn parenthesize_test<'a>(
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
if_expr: &ast::ExprIf,
|
if_expr: &ast::ExprIf,
|
||||||
comment_ranges: &CommentRanges,
|
tokens: &Tokens,
|
||||||
locator: &Locator<'a>,
|
locator: &Locator<'a>,
|
||||||
) -> Cow<'a, str> {
|
) -> Cow<'a, str> {
|
||||||
if let Some(range) = parenthesized_range(
|
if let Some(range) = parenthesized_range(expr.into(), if_expr.into(), tokens) {
|
||||||
expr.into(),
|
|
||||||
if_expr.into(),
|
|
||||||
comment_ranges,
|
|
||||||
locator.contents(),
|
|
||||||
) {
|
|
||||||
Cow::Borrowed(locator.slice(range))
|
Cow::Borrowed(locator.slice(range))
|
||||||
} else if matches!(expr, Expr::If(_) | Expr::Lambda(_) | Expr::Named(_)) {
|
} else if matches!(expr, Expr::If(_) | Expr::Lambda(_) | Expr::Named(_)) {
|
||||||
Cow::Owned(format!("({})", locator.slice(expr.range())))
|
Cow::Owned(format!("({})", locator.slice(expr.range())))
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use ruff_diagnostics::Applicability;
|
use ruff_diagnostics::Applicability;
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{Comprehension, Expr, StmtFor};
|
use ruff_python_ast::{Comprehension, Expr, StmtFor};
|
||||||
use ruff_python_semantic::analyze::typing;
|
use ruff_python_semantic::analyze::typing;
|
||||||
use ruff_python_semantic::analyze::typing::is_io_base_expr;
|
use ruff_python_semantic::analyze::typing::is_io_base_expr;
|
||||||
|
|
@ -104,8 +104,7 @@ fn readlines_in_iter(checker: &Checker, iter_expr: &Expr) {
|
||||||
let deletion_range = if let Some(parenthesized_range) = parenthesized_range(
|
let deletion_range = if let Some(parenthesized_range) = parenthesized_range(
|
||||||
expr_attr.value.as_ref().into(),
|
expr_attr.value.as_ref().into(),
|
||||||
expr_attr.into(),
|
expr_attr.into(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.source(),
|
|
||||||
) {
|
) {
|
||||||
expr_call.range().add_start(parenthesized_range.len())
|
expr_call.range().add_start(parenthesized_range.len())
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use ruff_diagnostics::Applicability;
|
use ruff_diagnostics::Applicability;
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{self as ast, Expr, Number};
|
use ruff_python_ast::{self as ast, Expr, Number};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
|
|
@ -152,13 +152,8 @@ fn generate_fix(checker: &Checker, call: &ast::ExprCall, base: Base, arg: &Expr)
|
||||||
checker.semantic(),
|
checker.semantic(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let arg_range = parenthesized_range(
|
let arg_range =
|
||||||
arg.into(),
|
parenthesized_range(arg.into(), call.into(), checker.tokens()).unwrap_or(arg.range());
|
||||||
call.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.source(),
|
|
||||||
)
|
|
||||||
.unwrap_or(arg.range());
|
|
||||||
let arg_str = checker.locator().slice(arg_range);
|
let arg_str = checker.locator().slice(arg_range);
|
||||||
|
|
||||||
Ok(Fix::applicable_edits(
|
Ok(Fix::applicable_edits(
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ pub(crate) fn single_item_membership_test(
|
||||||
&[membership_test.replacement_op()],
|
&[membership_test.replacement_op()],
|
||||||
std::slice::from_ref(item),
|
std::slice::from_ref(item),
|
||||||
expr.into(),
|
expr.into(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.source(),
|
checker.source(),
|
||||||
),
|
),
|
||||||
expr.range(),
|
expr.range(),
|
||||||
|
|
|
||||||
|
|
@ -305,6 +305,25 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn range_suppressions() -> Result<()> {
|
||||||
|
assert_diagnostics_diff!(
|
||||||
|
Path::new("ruff/suppressions.py"),
|
||||||
|
&settings::LinterSettings::for_rules(vec![
|
||||||
|
Rule::UnusedVariable,
|
||||||
|
Rule::AmbiguousVariableName,
|
||||||
|
Rule::UnusedNOQA,
|
||||||
|
]),
|
||||||
|
&settings::LinterSettings::for_rules(vec![
|
||||||
|
Rule::UnusedVariable,
|
||||||
|
Rule::AmbiguousVariableName,
|
||||||
|
Rule::UnusedNOQA,
|
||||||
|
])
|
||||||
|
.with_preview_mode(),
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ruf100_0() -> Result<()> {
|
fn ruf100_0() -> Result<()> {
|
||||||
let diagnostics = test_path(
|
let diagnostics = test_path(
|
||||||
|
|
|
||||||
|
|
@ -163,7 +163,7 @@ fn convert_type_vars(
|
||||||
class_arguments,
|
class_arguments,
|
||||||
Parentheses::Remove,
|
Parentheses::Remove,
|
||||||
source,
|
source,
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)?;
|
)?;
|
||||||
let replace_type_params =
|
let replace_type_params =
|
||||||
Edit::range_replacement(new_type_params.to_string(), type_params.range);
|
Edit::range_replacement(new_type_params.to_string(), type_params.range);
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ use anyhow::Result;
|
||||||
use ast::Keyword;
|
use ast::Keyword;
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::helpers::is_constant;
|
use ruff_python_ast::helpers::is_constant;
|
||||||
|
use ruff_python_ast::token::Tokens;
|
||||||
use ruff_python_ast::{self as ast, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
use ruff_python_trivia::CommentRanges;
|
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::Locator;
|
use crate::Locator;
|
||||||
|
|
@ -108,9 +108,8 @@ pub(crate) fn default_factory_kwarg(checker: &Checker, call: &ast::ExprCall) {
|
||||||
},
|
},
|
||||||
call.range(),
|
call.range(),
|
||||||
);
|
);
|
||||||
diagnostic.try_set_fix(|| {
|
diagnostic
|
||||||
convert_to_positional(call, keyword, checker.locator(), checker.comment_ranges())
|
.try_set_fix(|| convert_to_positional(call, keyword, checker.locator(), checker.tokens()));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if a value is definitively not callable (e.g., `1` or `[]`).
|
/// Returns `true` if a value is definitively not callable (e.g., `1` or `[]`).
|
||||||
|
|
@ -136,7 +135,7 @@ fn convert_to_positional(
|
||||||
call: &ast::ExprCall,
|
call: &ast::ExprCall,
|
||||||
default_factory: &Keyword,
|
default_factory: &Keyword,
|
||||||
locator: &Locator,
|
locator: &Locator,
|
||||||
comment_ranges: &CommentRanges,
|
tokens: &Tokens,
|
||||||
) -> Result<Fix> {
|
) -> Result<Fix> {
|
||||||
if call.arguments.len() == 1 {
|
if call.arguments.len() == 1 {
|
||||||
// Ex) `defaultdict(default_factory=list)`
|
// Ex) `defaultdict(default_factory=list)`
|
||||||
|
|
@ -153,7 +152,7 @@ fn convert_to_positional(
|
||||||
&call.arguments,
|
&call.arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
locator.contents(),
|
locator.contents(),
|
||||||
comment_ranges,
|
tokens,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Second, insert the value as the first positional argument.
|
// Second, insert the value as the first positional argument.
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ pub(crate) fn falsy_dict_get_fallback(checker: &Checker, expr: &Expr) {
|
||||||
&call.arguments,
|
&call.arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
checker.locator().contents(),
|
checker.locator().contents(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)
|
)
|
||||||
.map(|edit| Fix::applicable_edit(edit, applicability))
|
.map(|edit| Fix::applicable_edit(edit, applicability))
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
@ -77,14 +77,7 @@ pub(crate) fn parenthesize_chained_logical_operators(checker: &Checker, expr: &a
|
||||||
) => {
|
) => {
|
||||||
let locator = checker.locator();
|
let locator = checker.locator();
|
||||||
let source_range = bool_op.range();
|
let source_range = bool_op.range();
|
||||||
if parenthesized_range(
|
if parenthesized_range(bool_op.into(), expr.into(), checker.tokens()).is_none() {
|
||||||
bool_op.into(),
|
|
||||||
expr.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
locator.contents(),
|
|
||||||
)
|
|
||||||
.is_none()
|
|
||||||
{
|
|
||||||
let new_source = format!("({})", locator.slice(source_range));
|
let new_source = format!("({})", locator.slice(source_range));
|
||||||
let edit = Edit::range_replacement(new_source, source_range);
|
let edit = Edit::range_replacement(new_source, source_range);
|
||||||
checker
|
checker
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use anyhow::Context;
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_semantic::{Scope, ScopeKind};
|
use ruff_python_semantic::{Scope, ScopeKind};
|
||||||
use ruff_python_trivia::{indentation_at_offset, textwrap};
|
use ruff_python_trivia::{indentation_at_offset, textwrap};
|
||||||
use ruff_source_file::LineRanges;
|
use ruff_source_file::LineRanges;
|
||||||
|
|
@ -159,8 +159,7 @@ fn use_initvar(
|
||||||
let default_loc = parenthesized_range(
|
let default_loc = parenthesized_range(
|
||||||
default.into(),
|
default.into(),
|
||||||
parameter_with_default.into(),
|
parameter_with_default.into(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.source(),
|
|
||||||
)
|
)
|
||||||
.unwrap_or(default.range());
|
.unwrap_or(default.range());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{self as ast, Arguments, Expr};
|
use ruff_python_ast::{self as ast, Arguments, Expr};
|
||||||
use ruff_python_semantic::SemanticModel;
|
use ruff_python_semantic::SemanticModel;
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
@ -116,13 +116,8 @@ fn convert_to_reduce(iterable: &Expr, call: &ast::ExprCall, checker: &Checker) -
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let iterable = checker.locator().slice(
|
let iterable = checker.locator().slice(
|
||||||
parenthesized_range(
|
parenthesized_range(iterable.into(), (&call.arguments).into(), checker.tokens())
|
||||||
iterable.into(),
|
.unwrap_or(iterable.range()),
|
||||||
(&call.arguments).into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(iterable.range()),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(Fix::unsafe_edits(
|
Ok(Fix::unsafe_edits(
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::PythonVersion;
|
use ruff_python_ast::PythonVersion;
|
||||||
use ruff_python_ast::token::TokenKind;
|
use ruff_python_ast::token::TokenKind;
|
||||||
use ruff_python_ast::{Expr, ExprCall, parenthesize::parenthesized_range};
|
use ruff_python_ast::{Expr, ExprCall, token::parenthesized_range};
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
@ -124,13 +124,8 @@ fn replace_with_map(starmap: &ExprCall, zip: &ExprCall, checker: &Checker) -> Op
|
||||||
|
|
||||||
let mut remove_zip = vec![];
|
let mut remove_zip = vec![];
|
||||||
|
|
||||||
let full_zip_range = parenthesized_range(
|
let full_zip_range =
|
||||||
zip.into(),
|
parenthesized_range(zip.into(), starmap.into(), checker.tokens()).unwrap_or(zip.range());
|
||||||
starmap.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.source(),
|
|
||||||
)
|
|
||||||
.unwrap_or(zip.range());
|
|
||||||
|
|
||||||
// Delete any parentheses around the `zip` call to prevent that the argument turns into a tuple.
|
// Delete any parentheses around the `zip` call to prevent that the argument turns into a tuple.
|
||||||
remove_zip.push(Edit::range_deletion(TextRange::new(
|
remove_zip.push(Edit::range_deletion(TextRange::new(
|
||||||
|
|
@ -138,13 +133,8 @@ fn replace_with_map(starmap: &ExprCall, zip: &ExprCall, checker: &Checker) -> Op
|
||||||
zip.start(),
|
zip.start(),
|
||||||
)));
|
)));
|
||||||
|
|
||||||
let full_zip_func_range = parenthesized_range(
|
let full_zip_func_range = parenthesized_range((&zip.func).into(), zip.into(), checker.tokens())
|
||||||
(&zip.func).into(),
|
.unwrap_or(zip.func.range());
|
||||||
zip.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.source(),
|
|
||||||
)
|
|
||||||
.unwrap_or(zip.func.range());
|
|
||||||
|
|
||||||
// Delete the `zip` callee
|
// Delete the `zip` callee
|
||||||
remove_zip.push(Edit::range_deletion(full_zip_func_range));
|
remove_zip.push(Edit::range_deletion(full_zip_func_range));
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::{Tokens, parenthesized_range};
|
||||||
use ruff_python_ast::{Arguments, Expr, ExprCall};
|
use ruff_python_ast::{Arguments, Expr, ExprCall};
|
||||||
use ruff_python_semantic::SemanticModel;
|
use ruff_python_semantic::SemanticModel;
|
||||||
use ruff_python_semantic::analyze::type_inference::{NumberLike, PythonType, ResolvedPythonType};
|
use ruff_python_semantic::analyze::type_inference::{NumberLike, PythonType, ResolvedPythonType};
|
||||||
|
|
@ -86,6 +86,7 @@ pub(crate) fn unnecessary_cast_to_int(checker: &Checker, call: &ExprCall) {
|
||||||
applicability,
|
applicability,
|
||||||
checker.semantic(),
|
checker.semantic(),
|
||||||
checker.locator(),
|
checker.locator(),
|
||||||
|
checker.tokens(),
|
||||||
checker.comment_ranges(),
|
checker.comment_ranges(),
|
||||||
checker.source(),
|
checker.source(),
|
||||||
);
|
);
|
||||||
|
|
@ -95,27 +96,26 @@ pub(crate) fn unnecessary_cast_to_int(checker: &Checker, call: &ExprCall) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a fix that replaces `int(expression)` with `expression`.
|
/// Creates a fix that replaces `int(expression)` with `expression`.
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn unwrap_int_expression(
|
fn unwrap_int_expression(
|
||||||
call: &ExprCall,
|
call: &ExprCall,
|
||||||
argument: &Expr,
|
argument: &Expr,
|
||||||
applicability: Applicability,
|
applicability: Applicability,
|
||||||
semantic: &SemanticModel,
|
semantic: &SemanticModel,
|
||||||
locator: &Locator,
|
locator: &Locator,
|
||||||
|
tokens: &Tokens,
|
||||||
comment_ranges: &CommentRanges,
|
comment_ranges: &CommentRanges,
|
||||||
source: &str,
|
source: &str,
|
||||||
) -> Fix {
|
) -> Fix {
|
||||||
let content = if let Some(range) = parenthesized_range(
|
let content = if let Some(range) =
|
||||||
argument.into(),
|
parenthesized_range(argument.into(), (&call.arguments).into(), tokens)
|
||||||
(&call.arguments).into(),
|
{
|
||||||
comment_ranges,
|
|
||||||
source,
|
|
||||||
) {
|
|
||||||
locator.slice(range).to_string()
|
locator.slice(range).to_string()
|
||||||
} else {
|
} else {
|
||||||
let parenthesize = semantic.current_expression_parent().is_some()
|
let parenthesize = semantic.current_expression_parent().is_some()
|
||||||
|| argument.is_named_expr()
|
|| argument.is_named_expr()
|
||||||
|| locator.count_lines(argument.range()) > 0;
|
|| locator.count_lines(argument.range()) > 0;
|
||||||
if parenthesize && !has_own_parentheses(argument, comment_ranges, source) {
|
if parenthesize && !has_own_parentheses(argument, tokens, source) {
|
||||||
format!("({})", locator.slice(argument.range()))
|
format!("({})", locator.slice(argument.range()))
|
||||||
} else {
|
} else {
|
||||||
locator.slice(argument.range()).to_string()
|
locator.slice(argument.range()).to_string()
|
||||||
|
|
@ -255,7 +255,7 @@ fn round_applicability(arguments: &Arguments, semantic: &SemanticModel) -> Optio
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the given [`Expr`] has its own parentheses (e.g., `()`, `[]`, `{}`).
|
/// Returns `true` if the given [`Expr`] has its own parentheses (e.g., `()`, `[]`, `{}`).
|
||||||
fn has_own_parentheses(expr: &Expr, comment_ranges: &CommentRanges, source: &str) -> bool {
|
fn has_own_parentheses(expr: &Expr, tokens: &Tokens, source: &str) -> bool {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::ListComp(_)
|
Expr::ListComp(_)
|
||||||
| Expr::SetComp(_)
|
| Expr::SetComp(_)
|
||||||
|
|
@ -276,14 +276,10 @@ fn has_own_parentheses(expr: &Expr, comment_ranges: &CommentRanges, source: &str
|
||||||
// f
|
// f
|
||||||
// (10)
|
// (10)
|
||||||
// ```
|
// ```
|
||||||
let func_end = parenthesized_range(
|
let func_end =
|
||||||
call_expr.func.as_ref().into(),
|
parenthesized_range(call_expr.func.as_ref().into(), call_expr.into(), tokens)
|
||||||
call_expr.into(),
|
.unwrap_or(call_expr.func.range())
|
||||||
comment_ranges,
|
.end();
|
||||||
source,
|
|
||||||
)
|
|
||||||
.unwrap_or(call_expr.func.range())
|
|
||||||
.end();
|
|
||||||
lines_after_ignoring_trivia(func_end, source) == 0
|
lines_after_ignoring_trivia(func_end, source) == 0
|
||||||
}
|
}
|
||||||
Expr::Subscript(subscript_expr) => {
|
Expr::Subscript(subscript_expr) => {
|
||||||
|
|
@ -291,8 +287,7 @@ fn has_own_parentheses(expr: &Expr, comment_ranges: &CommentRanges, source: &str
|
||||||
let subscript_end = parenthesized_range(
|
let subscript_end = parenthesized_range(
|
||||||
subscript_expr.value.as_ref().into(),
|
subscript_expr.value.as_ref().into(),
|
||||||
subscript_expr.into(),
|
subscript_expr.into(),
|
||||||
comment_ranges,
|
tokens,
|
||||||
source,
|
|
||||||
)
|
)
|
||||||
.unwrap_or(subscript_expr.value.range())
|
.unwrap_or(subscript_expr.value.range())
|
||||||
.end();
|
.end();
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use ruff_python_ast::{self as ast, BoolOp, CmpOp, Expr};
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::helpers::contains_effect;
|
use ruff_python_ast::helpers::contains_effect;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
@ -108,22 +108,12 @@ pub(crate) fn unnecessary_key_check(checker: &Checker, expr: &Expr) {
|
||||||
format!(
|
format!(
|
||||||
"{}.get({})",
|
"{}.get({})",
|
||||||
checker.locator().slice(
|
checker.locator().slice(
|
||||||
parenthesized_range(
|
parenthesized_range(obj_right.into(), right.into(), checker.tokens(),)
|
||||||
obj_right.into(),
|
.unwrap_or(obj_right.range())
|
||||||
right.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(obj_right.range())
|
|
||||||
),
|
),
|
||||||
checker.locator().slice(
|
checker.locator().slice(
|
||||||
parenthesized_range(
|
parenthesized_range(key_right.into(), right.into(), checker.tokens(),)
|
||||||
key_right.into(),
|
.unwrap_or(key_right.range())
|
||||||
right.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(key_right.range())
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
expr.range(),
|
expr.range(),
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use ruff_diagnostics::{Applicability, Edit};
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
|
|
||||||
use ruff_python_ast::helpers::is_empty_f_string;
|
use ruff_python_ast::helpers::is_empty_f_string;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{self as ast, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
|
|
@ -140,31 +140,19 @@ fn fix_unnecessary_literal_in_deque(
|
||||||
// call. otherwise, we only delete the `iterable` argument and leave the others untouched.
|
// call. otherwise, we only delete the `iterable` argument and leave the others untouched.
|
||||||
let edit = if let Some(maxlen) = maxlen {
|
let edit = if let Some(maxlen) = maxlen {
|
||||||
let deque_name = checker.locator().slice(
|
let deque_name = checker.locator().slice(
|
||||||
parenthesized_range(
|
parenthesized_range(deque.func.as_ref().into(), deque.into(), checker.tokens())
|
||||||
deque.func.as_ref().into(),
|
.unwrap_or(deque.func.range()),
|
||||||
deque.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.source(),
|
|
||||||
)
|
|
||||||
.unwrap_or(deque.func.range()),
|
|
||||||
);
|
);
|
||||||
let len_str = checker.locator().slice(maxlen);
|
let len_str = checker.locator().slice(maxlen);
|
||||||
let deque_str = format!("{deque_name}(maxlen={len_str})");
|
let deque_str = format!("{deque_name}(maxlen={len_str})");
|
||||||
Edit::range_replacement(deque_str, deque.range)
|
Edit::range_replacement(deque_str, deque.range)
|
||||||
} else {
|
} else {
|
||||||
let range = parenthesized_range(
|
|
||||||
iterable.value().into(),
|
|
||||||
(&deque.arguments).into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.source(),
|
|
||||||
)
|
|
||||||
.unwrap_or(iterable.range());
|
|
||||||
remove_argument(
|
remove_argument(
|
||||||
&range,
|
&iterable,
|
||||||
&deque.arguments,
|
&deque.arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
checker.source(),
|
checker.source(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)?
|
)?
|
||||||
};
|
};
|
||||||
let has_comments = checker.comment_ranges().intersects(edit.range());
|
let has_comments = checker.comment_ranges().intersects(edit.range());
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
|
|
||||||
use crate::AlwaysFixableViolation;
|
use crate::AlwaysFixableViolation;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq, Default)]
|
||||||
pub(crate) struct UnusedCodes {
|
pub(crate) struct UnusedCodes {
|
||||||
pub disabled: Vec<String>,
|
pub disabled: Vec<String>,
|
||||||
pub duplicated: Vec<String>,
|
pub duplicated: Vec<String>,
|
||||||
|
|
@ -12,6 +12,21 @@ pub(crate) struct UnusedCodes {
|
||||||
pub unmatched: Vec<String>,
|
pub unmatched: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub(crate) enum UnusedNOQAKind {
|
||||||
|
Noqa,
|
||||||
|
Suppression,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnusedNOQAKind {
|
||||||
|
fn as_str(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
UnusedNOQAKind::Noqa => "`noqa` directive",
|
||||||
|
UnusedNOQAKind::Suppression => "suppression",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks for `noqa` directives that are no longer applicable.
|
/// Checks for `noqa` directives that are no longer applicable.
|
||||||
///
|
///
|
||||||
|
|
@ -46,6 +61,7 @@ pub(crate) struct UnusedCodes {
|
||||||
#[violation_metadata(stable_since = "v0.0.155")]
|
#[violation_metadata(stable_since = "v0.0.155")]
|
||||||
pub(crate) struct UnusedNOQA {
|
pub(crate) struct UnusedNOQA {
|
||||||
pub codes: Option<UnusedCodes>,
|
pub codes: Option<UnusedCodes>,
|
||||||
|
pub kind: UnusedNOQAKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AlwaysFixableViolation for UnusedNOQA {
|
impl AlwaysFixableViolation for UnusedNOQA {
|
||||||
|
|
@ -95,16 +111,20 @@ impl AlwaysFixableViolation for UnusedNOQA {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if codes_by_reason.is_empty() {
|
if codes_by_reason.is_empty() {
|
||||||
"Unused `noqa` directive".to_string()
|
format!("Unused {}", self.kind.as_str())
|
||||||
} else {
|
} else {
|
||||||
format!("Unused `noqa` directive ({})", codes_by_reason.join("; "))
|
format!(
|
||||||
|
"Unused {} ({})",
|
||||||
|
self.kind.as_str(),
|
||||||
|
codes_by_reason.join("; ")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => "Unused blanket `noqa` directive".to_string(),
|
None => format!("Unused blanket {}", self.kind.as_str()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fix_title(&self) -> String {
|
fn fix_title(&self) -> String {
|
||||||
"Remove unused `noqa` directive".to_string()
|
format!("Remove unused {}", self.kind.as_str())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,451 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_linter/src/rules/ruff/mod.rs
|
||||||
|
---
|
||||||
|
--- Linter settings ---
|
||||||
|
-linter.preview = disabled
|
||||||
|
+linter.preview = enabled
|
||||||
|
|
||||||
|
--- Summary ---
|
||||||
|
Removed: 14
|
||||||
|
Added: 11
|
||||||
|
|
||||||
|
--- Removed ---
|
||||||
|
E741 Ambiguous variable name: `I`
|
||||||
|
--> suppressions.py:4:5
|
||||||
|
|
|
||||||
|
2 | # These should both be ignored by the range suppression.
|
||||||
|
3 | # ruff: disable[E741, F841]
|
||||||
|
4 | I = 1
|
||||||
|
| ^
|
||||||
|
5 | # ruff: enable[E741, F841]
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
F841 [*] Local variable `I` is assigned to but never used
|
||||||
|
--> suppressions.py:4:5
|
||||||
|
|
|
||||||
|
2 | # These should both be ignored by the range suppression.
|
||||||
|
3 | # ruff: disable[E741, F841]
|
||||||
|
4 | I = 1
|
||||||
|
| ^
|
||||||
|
5 | # ruff: enable[E741, F841]
|
||||||
|
|
|
||||||
|
help: Remove assignment to unused variable `I`
|
||||||
|
1 | def f():
|
||||||
|
2 | # These should both be ignored by the range suppression.
|
||||||
|
3 | # ruff: disable[E741, F841]
|
||||||
|
- I = 1
|
||||||
|
4 + pass
|
||||||
|
5 | # ruff: enable[E741, F841]
|
||||||
|
6 |
|
||||||
|
7 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
E741 Ambiguous variable name: `I`
|
||||||
|
--> suppressions.py:12:5
|
||||||
|
|
|
||||||
|
10 | # Should also generate an "unmatched suppression" warning.
|
||||||
|
11 | # ruff:disable[E741,F841]
|
||||||
|
12 | I = 1
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
F841 [*] Local variable `I` is assigned to but never used
|
||||||
|
--> suppressions.py:12:5
|
||||||
|
|
|
||||||
|
10 | # Should also generate an "unmatched suppression" warning.
|
||||||
|
11 | # ruff:disable[E741,F841]
|
||||||
|
12 | I = 1
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
help: Remove assignment to unused variable `I`
|
||||||
|
9 | # These should both be ignored by the implicit range suppression.
|
||||||
|
10 | # Should also generate an "unmatched suppression" warning.
|
||||||
|
11 | # ruff:disable[E741,F841]
|
||||||
|
- I = 1
|
||||||
|
12 + pass
|
||||||
|
13 |
|
||||||
|
14 |
|
||||||
|
15 | def f():
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
E741 Ambiguous variable name: `I`
|
||||||
|
--> suppressions.py:26:5
|
||||||
|
|
|
||||||
|
24 | # the other logged to the user.
|
||||||
|
25 | # ruff: disable[E741]
|
||||||
|
26 | I = 1
|
||||||
|
| ^
|
||||||
|
27 | # ruff: enable[E741]
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
E741 Ambiguous variable name: `l`
|
||||||
|
--> suppressions.py:35:5
|
||||||
|
|
|
||||||
|
33 | # middle line should be completely silenced.
|
||||||
|
34 | # ruff: disable[E741]
|
||||||
|
35 | l = 0
|
||||||
|
| ^
|
||||||
|
36 | # ruff: disable[F841]
|
||||||
|
37 | O = 1
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
E741 Ambiguous variable name: `O`
|
||||||
|
--> suppressions.py:37:5
|
||||||
|
|
|
||||||
|
35 | l = 0
|
||||||
|
36 | # ruff: disable[F841]
|
||||||
|
37 | O = 1
|
||||||
|
| ^
|
||||||
|
38 | # ruff: enable[E741]
|
||||||
|
39 | I = 2
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
F841 [*] Local variable `O` is assigned to but never used
|
||||||
|
--> suppressions.py:37:5
|
||||||
|
|
|
||||||
|
35 | l = 0
|
||||||
|
36 | # ruff: disable[F841]
|
||||||
|
37 | O = 1
|
||||||
|
| ^
|
||||||
|
38 | # ruff: enable[E741]
|
||||||
|
39 | I = 2
|
||||||
|
|
|
||||||
|
help: Remove assignment to unused variable `O`
|
||||||
|
34 | # ruff: disable[E741]
|
||||||
|
35 | l = 0
|
||||||
|
36 | # ruff: disable[F841]
|
||||||
|
- O = 1
|
||||||
|
37 | # ruff: enable[E741]
|
||||||
|
38 | I = 2
|
||||||
|
39 | # ruff: enable[F841]
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
F841 [*] Local variable `I` is assigned to but never used
|
||||||
|
--> suppressions.py:39:5
|
||||||
|
|
|
||||||
|
37 | O = 1
|
||||||
|
38 | # ruff: enable[E741]
|
||||||
|
39 | I = 2
|
||||||
|
| ^
|
||||||
|
40 | # ruff: enable[F841]
|
||||||
|
|
|
||||||
|
help: Remove assignment to unused variable `I`
|
||||||
|
36 | # ruff: disable[F841]
|
||||||
|
37 | O = 1
|
||||||
|
38 | # ruff: enable[E741]
|
||||||
|
- I = 2
|
||||||
|
39 | # ruff: enable[F841]
|
||||||
|
40 |
|
||||||
|
41 |
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
F841 [*] Local variable `foo` is assigned to but never used
|
||||||
|
--> suppressions.py:62:5
|
||||||
|
|
|
||||||
|
60 | # TODO: Duplicate codes should be counted as duplicate, not unused
|
||||||
|
61 | # ruff: disable[F841, F841]
|
||||||
|
62 | foo = 0
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
help: Remove assignment to unused variable `foo`
|
||||||
|
59 | def f():
|
||||||
|
60 | # TODO: Duplicate codes should be counted as duplicate, not unused
|
||||||
|
61 | # ruff: disable[F841, F841]
|
||||||
|
- foo = 0
|
||||||
|
62 + pass
|
||||||
|
63 |
|
||||||
|
64 |
|
||||||
|
65 | def f():
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
F841 [*] Local variable `foo` is assigned to but never used
|
||||||
|
--> suppressions.py:70:5
|
||||||
|
|
|
||||||
|
68 | # ruff: disable[F841]
|
||||||
|
69 | # ruff: disable[F841]
|
||||||
|
70 | foo = 0
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
help: Remove assignment to unused variable `foo`
|
||||||
|
67 | # and the other should trigger an unused suppression diagnostic
|
||||||
|
68 | # ruff: disable[F841]
|
||||||
|
69 | # ruff: disable[F841]
|
||||||
|
- foo = 0
|
||||||
|
70 + pass
|
||||||
|
71 |
|
||||||
|
72 |
|
||||||
|
73 | def f():
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
F841 [*] Local variable `foo` is assigned to but never used
|
||||||
|
--> suppressions.py:76:5
|
||||||
|
|
|
||||||
|
74 | # Multiple codes but only one is used
|
||||||
|
75 | # ruff: disable[E741, F401, F841]
|
||||||
|
76 | foo = 0
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
help: Remove assignment to unused variable `foo`
|
||||||
|
73 | def f():
|
||||||
|
74 | # Multiple codes but only one is used
|
||||||
|
75 | # ruff: disable[E741, F401, F841]
|
||||||
|
- foo = 0
|
||||||
|
76 + pass
|
||||||
|
77 |
|
||||||
|
78 |
|
||||||
|
79 | def f():
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
E741 Ambiguous variable name: `I`
|
||||||
|
--> suppressions.py:82:5
|
||||||
|
|
|
||||||
|
80 | # Multiple codes but only two are used
|
||||||
|
81 | # ruff: disable[E741, F401, F841]
|
||||||
|
82 | I = 0
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
F841 [*] Local variable `I` is assigned to but never used
|
||||||
|
--> suppressions.py:82:5
|
||||||
|
|
|
||||||
|
80 | # Multiple codes but only two are used
|
||||||
|
81 | # ruff: disable[E741, F401, F841]
|
||||||
|
82 | I = 0
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
help: Remove assignment to unused variable `I`
|
||||||
|
79 | def f():
|
||||||
|
80 | # Multiple codes but only two are used
|
||||||
|
81 | # ruff: disable[E741, F401, F841]
|
||||||
|
- I = 0
|
||||||
|
82 + pass
|
||||||
|
83 |
|
||||||
|
84 |
|
||||||
|
85 | def f():
|
||||||
|
note: This is an unsafe fix and may change runtime behavior
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- Added ---
|
||||||
|
RUF100 [*] Unused suppression (non-enabled: `E501`)
|
||||||
|
--> suppressions.py:46:5
|
||||||
|
|
|
||||||
|
44 | # Neither of these are ignored and warnings are
|
||||||
|
45 | # logged to user
|
||||||
|
46 | # ruff: disable[E501]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
47 | I = 1
|
||||||
|
48 | # ruff: enable[E501]
|
||||||
|
|
|
||||||
|
help: Remove unused suppression
|
||||||
|
43 | def f():
|
||||||
|
44 | # Neither of these are ignored and warnings are
|
||||||
|
45 | # logged to user
|
||||||
|
- # ruff: disable[E501]
|
||||||
|
46 | I = 1
|
||||||
|
47 | # ruff: enable[E501]
|
||||||
|
48 |
|
||||||
|
|
||||||
|
|
||||||
|
RUF100 [*] Unused suppression (non-enabled: `E501`)
|
||||||
|
--> suppressions.py:48:5
|
||||||
|
|
|
||||||
|
46 | # ruff: disable[E501]
|
||||||
|
47 | I = 1
|
||||||
|
48 | # ruff: enable[E501]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: Remove unused suppression
|
||||||
|
45 | # logged to user
|
||||||
|
46 | # ruff: disable[E501]
|
||||||
|
47 | I = 1
|
||||||
|
- # ruff: enable[E501]
|
||||||
|
48 |
|
||||||
|
49 |
|
||||||
|
50 | def f():
|
||||||
|
|
||||||
|
|
||||||
|
RUF100 [*] Unused `noqa` directive (unused: `E741`, `F841`)
|
||||||
|
--> suppressions.py:55:12
|
||||||
|
|
|
||||||
|
53 | # and an unusued noqa diagnostic should be logged.
|
||||||
|
54 | # ruff:disable[E741,F841]
|
||||||
|
55 | I = 1 # noqa: E741,F841
|
||||||
|
| ^^^^^^^^^^^^^^^^^
|
||||||
|
56 | # ruff:enable[E741,F841]
|
||||||
|
|
|
||||||
|
help: Remove unused `noqa` directive
|
||||||
|
52 | # These should both be ignored by the range suppression,
|
||||||
|
53 | # and an unusued noqa diagnostic should be logged.
|
||||||
|
54 | # ruff:disable[E741,F841]
|
||||||
|
- I = 1 # noqa: E741,F841
|
||||||
|
55 + I = 1
|
||||||
|
56 | # ruff:enable[E741,F841]
|
||||||
|
57 |
|
||||||
|
58 |
|
||||||
|
|
||||||
|
|
||||||
|
RUF100 [*] Unused suppression (unused: `F841`)
|
||||||
|
--> suppressions.py:61:21
|
||||||
|
|
|
||||||
|
59 | def f():
|
||||||
|
60 | # TODO: Duplicate codes should be counted as duplicate, not unused
|
||||||
|
61 | # ruff: disable[F841, F841]
|
||||||
|
| ^^^^
|
||||||
|
62 | foo = 0
|
||||||
|
|
|
||||||
|
help: Remove unused suppression
|
||||||
|
58 |
|
||||||
|
59 | def f():
|
||||||
|
60 | # TODO: Duplicate codes should be counted as duplicate, not unused
|
||||||
|
- # ruff: disable[F841, F841]
|
||||||
|
61 + # ruff: disable[F841]
|
||||||
|
62 | foo = 0
|
||||||
|
63 |
|
||||||
|
64 |
|
||||||
|
|
||||||
|
|
||||||
|
RUF100 [*] Unused suppression (unused: `F841`)
|
||||||
|
--> suppressions.py:69:5
|
||||||
|
|
|
||||||
|
67 | # and the other should trigger an unused suppression diagnostic
|
||||||
|
68 | # ruff: disable[F841]
|
||||||
|
69 | # ruff: disable[F841]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
70 | foo = 0
|
||||||
|
|
|
||||||
|
help: Remove unused suppression
|
||||||
|
66 | # Overlapping range suppressions, one should be marked as used,
|
||||||
|
67 | # and the other should trigger an unused suppression diagnostic
|
||||||
|
68 | # ruff: disable[F841]
|
||||||
|
- # ruff: disable[F841]
|
||||||
|
69 | foo = 0
|
||||||
|
70 |
|
||||||
|
71 |
|
||||||
|
|
||||||
|
|
||||||
|
RUF100 [*] Unused suppression (unused: `E741`)
|
||||||
|
--> suppressions.py:75:21
|
||||||
|
|
|
||||||
|
73 | def f():
|
||||||
|
74 | # Multiple codes but only one is used
|
||||||
|
75 | # ruff: disable[E741, F401, F841]
|
||||||
|
| ^^^^
|
||||||
|
76 | foo = 0
|
||||||
|
|
|
||||||
|
help: Remove unused suppression
|
||||||
|
72 |
|
||||||
|
73 | def f():
|
||||||
|
74 | # Multiple codes but only one is used
|
||||||
|
- # ruff: disable[E741, F401, F841]
|
||||||
|
75 + # ruff: disable[F401, F841]
|
||||||
|
76 | foo = 0
|
||||||
|
77 |
|
||||||
|
78 |
|
||||||
|
|
||||||
|
|
||||||
|
RUF100 [*] Unused suppression (non-enabled: `F401`)
|
||||||
|
--> suppressions.py:75:27
|
||||||
|
|
|
||||||
|
73 | def f():
|
||||||
|
74 | # Multiple codes but only one is used
|
||||||
|
75 | # ruff: disable[E741, F401, F841]
|
||||||
|
| ^^^^
|
||||||
|
76 | foo = 0
|
||||||
|
|
|
||||||
|
help: Remove unused suppression
|
||||||
|
72 |
|
||||||
|
73 | def f():
|
||||||
|
74 | # Multiple codes but only one is used
|
||||||
|
- # ruff: disable[E741, F401, F841]
|
||||||
|
75 + # ruff: disable[E741, F841]
|
||||||
|
76 | foo = 0
|
||||||
|
77 |
|
||||||
|
78 |
|
||||||
|
|
||||||
|
|
||||||
|
RUF100 [*] Unused suppression (non-enabled: `F401`)
|
||||||
|
--> suppressions.py:81:27
|
||||||
|
|
|
||||||
|
79 | def f():
|
||||||
|
80 | # Multiple codes but only two are used
|
||||||
|
81 | # ruff: disable[E741, F401, F841]
|
||||||
|
| ^^^^
|
||||||
|
82 | I = 0
|
||||||
|
|
|
||||||
|
help: Remove unused suppression
|
||||||
|
78 |
|
||||||
|
79 | def f():
|
||||||
|
80 | # Multiple codes but only two are used
|
||||||
|
- # ruff: disable[E741, F401, F841]
|
||||||
|
81 + # ruff: disable[E741, F841]
|
||||||
|
82 | I = 0
|
||||||
|
83 |
|
||||||
|
84 |
|
||||||
|
|
||||||
|
|
||||||
|
RUF100 [*] Unused suppression (unused: `E741`)
|
||||||
|
--> suppressions.py:87:21
|
||||||
|
|
|
||||||
|
85 | def f():
|
||||||
|
86 | # Multiple codes but none are used
|
||||||
|
87 | # ruff: disable[E741, F401, F841]
|
||||||
|
| ^^^^
|
||||||
|
88 | print("hello")
|
||||||
|
|
|
||||||
|
help: Remove unused suppression
|
||||||
|
84 |
|
||||||
|
85 | def f():
|
||||||
|
86 | # Multiple codes but none are used
|
||||||
|
- # ruff: disable[E741, F401, F841]
|
||||||
|
87 + # ruff: disable[F401, F841]
|
||||||
|
88 | print("hello")
|
||||||
|
|
||||||
|
|
||||||
|
RUF100 [*] Unused suppression (non-enabled: `F401`)
|
||||||
|
--> suppressions.py:87:27
|
||||||
|
|
|
||||||
|
85 | def f():
|
||||||
|
86 | # Multiple codes but none are used
|
||||||
|
87 | # ruff: disable[E741, F401, F841]
|
||||||
|
| ^^^^
|
||||||
|
88 | print("hello")
|
||||||
|
|
|
||||||
|
help: Remove unused suppression
|
||||||
|
84 |
|
||||||
|
85 | def f():
|
||||||
|
86 | # Multiple codes but none are used
|
||||||
|
- # ruff: disable[E741, F401, F841]
|
||||||
|
87 + # ruff: disable[E741, F841]
|
||||||
|
88 | print("hello")
|
||||||
|
|
||||||
|
|
||||||
|
RUF100 [*] Unused suppression (unused: `F841`)
|
||||||
|
--> suppressions.py:87:33
|
||||||
|
|
|
||||||
|
85 | def f():
|
||||||
|
86 | # Multiple codes but none are used
|
||||||
|
87 | # ruff: disable[E741, F401, F841]
|
||||||
|
| ^^^^
|
||||||
|
88 | print("hello")
|
||||||
|
|
|
||||||
|
help: Remove unused suppression
|
||||||
|
84 |
|
||||||
|
85 | def f():
|
||||||
|
86 | # Multiple codes but none are used
|
||||||
|
- # ruff: disable[E741, F401, F841]
|
||||||
|
87 + # ruff: disable[E741, F401]
|
||||||
|
88 | print("hello")
|
||||||
|
|
@ -465,6 +465,12 @@ impl LinterSettings {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_preview_mode(mut self) -> Self {
|
||||||
|
self.preview = PreviewMode::Enabled;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Resolve the [`TargetVersion`] to use for linting.
|
/// Resolve the [`TargetVersion`] to use for linting.
|
||||||
///
|
///
|
||||||
/// This method respects the per-file version overrides in
|
/// This method respects the per-file version overrides in
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
use compact_str::CompactString;
|
use compact_str::CompactString;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
use ruff_db::diagnostic::Diagnostic;
|
||||||
|
use ruff_diagnostics::{Edit, Fix};
|
||||||
use ruff_python_ast::token::{TokenKind, Tokens};
|
use ruff_python_ast::token::{TokenKind, Tokens};
|
||||||
use ruff_python_ast::whitespace::indentation;
|
use ruff_python_ast::whitespace::indentation;
|
||||||
|
use std::cell::Cell;
|
||||||
use std::{error::Error, fmt::Formatter};
|
use std::{error::Error, fmt::Formatter};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
|
@ -9,7 +12,14 @@ use ruff_python_trivia::Cursor;
|
||||||
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize, TextSlice};
|
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize, TextSlice};
|
||||||
use smallvec::{SmallVec, smallvec};
|
use smallvec::{SmallVec, smallvec};
|
||||||
|
|
||||||
#[allow(unused)]
|
use crate::Locator;
|
||||||
|
use crate::checkers::ast::LintContext;
|
||||||
|
use crate::codes::Rule;
|
||||||
|
use crate::fix::edits::delete_comment;
|
||||||
|
use crate::preview::is_range_suppressions_enabled;
|
||||||
|
use crate::rules::ruff::rules::{UnusedCodes, UnusedNOQA, UnusedNOQAKind};
|
||||||
|
use crate::settings::LinterSettings;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
enum SuppressionAction {
|
enum SuppressionAction {
|
||||||
Disable,
|
Disable,
|
||||||
|
|
@ -31,7 +41,6 @@ pub(crate) struct SuppressionComment {
|
||||||
reason: TextRange,
|
reason: TextRange,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
impl SuppressionComment {
|
impl SuppressionComment {
|
||||||
/// Return the suppressed codes as strings
|
/// Return the suppressed codes as strings
|
||||||
fn codes_as_str<'src>(&self, source: &'src str) -> impl Iterator<Item = &'src str> {
|
fn codes_as_str<'src>(&self, source: &'src str) -> impl Iterator<Item = &'src str> {
|
||||||
|
|
@ -48,7 +57,6 @@ pub(crate) struct PendingSuppressionComment<'a> {
|
||||||
comment: SuppressionComment,
|
comment: SuppressionComment,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
impl PendingSuppressionComment<'_> {
|
impl PendingSuppressionComment<'_> {
|
||||||
/// Whether the comment "matches" another comment, based on indentation and suppressed codes
|
/// Whether the comment "matches" another comment, based on indentation and suppressed codes
|
||||||
/// Expects a "forward search" for matches, ie, will only match if the current comment is a
|
/// Expects a "forward search" for matches, ie, will only match if the current comment is a
|
||||||
|
|
@ -64,8 +72,7 @@ impl PendingSuppressionComment<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[derive(Debug)]
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub(crate) struct Suppression {
|
pub(crate) struct Suppression {
|
||||||
/// The lint code being suppressed
|
/// The lint code being suppressed
|
||||||
code: CompactString,
|
code: CompactString,
|
||||||
|
|
@ -75,9 +82,11 @@ pub(crate) struct Suppression {
|
||||||
|
|
||||||
/// Any comments associated with the suppression
|
/// Any comments associated with the suppression
|
||||||
comments: SmallVec<[SuppressionComment; 2]>,
|
comments: SmallVec<[SuppressionComment; 2]>,
|
||||||
|
|
||||||
|
/// Whether this suppression actually suppressed a diagnostic
|
||||||
|
used: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub(crate) enum InvalidSuppressionKind {
|
pub(crate) enum InvalidSuppressionKind {
|
||||||
/// Trailing suppression not supported
|
/// Trailing suppression not supported
|
||||||
|
|
@ -98,8 +107,8 @@ pub(crate) struct InvalidSuppression {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Default)]
|
||||||
pub(crate) struct Suppressions {
|
pub struct Suppressions {
|
||||||
/// Valid suppression ranges with associated comments
|
/// Valid suppression ranges with associated comments
|
||||||
valid: Vec<Suppression>,
|
valid: Vec<Suppression>,
|
||||||
|
|
||||||
|
|
@ -110,11 +119,121 @@ pub(crate) struct Suppressions {
|
||||||
errors: Vec<ParseError>,
|
errors: Vec<ParseError>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
impl Suppressions {
|
impl Suppressions {
|
||||||
pub(crate) fn from_tokens(source: &str, tokens: &Tokens) -> Suppressions {
|
pub fn from_tokens(settings: &LinterSettings, source: &str, tokens: &Tokens) -> Suppressions {
|
||||||
let builder = SuppressionsBuilder::new(source);
|
if is_range_suppressions_enabled(settings) {
|
||||||
builder.load_from_tokens(tokens)
|
let builder = SuppressionsBuilder::new(source);
|
||||||
|
builder.load_from_tokens(tokens)
|
||||||
|
} else {
|
||||||
|
Suppressions::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_empty(&self) -> bool {
|
||||||
|
self.valid.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if a diagnostic is suppressed by any known range suppressions
|
||||||
|
pub(crate) fn check_diagnostic(&self, diagnostic: &Diagnostic) -> bool {
|
||||||
|
if self.valid.is_empty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(code) = diagnostic.secondary_code() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let Some(span) = diagnostic.primary_span() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let Some(range) = span.range() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
for suppression in &self.valid {
|
||||||
|
if *code == suppression.code.as_str() && suppression.range.contains_range(range) {
|
||||||
|
suppression.used.set(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn check_suppressions(&self, context: &LintContext, locator: &Locator) {
|
||||||
|
if !context.any_rule_enabled(&[Rule::UnusedNOQA, Rule::InvalidRuleCode]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let unused = self
|
||||||
|
.valid
|
||||||
|
.iter()
|
||||||
|
.filter(|suppression| !suppression.used.get());
|
||||||
|
|
||||||
|
for suppression in unused {
|
||||||
|
let Ok(rule) = Rule::from_code(&suppression.code) else {
|
||||||
|
continue; // TODO: invalid code
|
||||||
|
};
|
||||||
|
for comment in &suppression.comments {
|
||||||
|
let mut range = comment.range;
|
||||||
|
let edit = if comment.codes.len() == 1 {
|
||||||
|
delete_comment(comment.range, locator)
|
||||||
|
} else {
|
||||||
|
let code_index = comment
|
||||||
|
.codes
|
||||||
|
.iter()
|
||||||
|
.position(|range| locator.slice(range) == suppression.code)
|
||||||
|
.unwrap();
|
||||||
|
range = comment.codes[code_index];
|
||||||
|
let code_range = if code_index < (comment.codes.len() - 1) {
|
||||||
|
TextRange::new(
|
||||||
|
comment.codes[code_index].start(),
|
||||||
|
comment.codes[code_index + 1].start(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
TextRange::new(
|
||||||
|
comment.codes[code_index - 1].end(),
|
||||||
|
comment.codes[code_index].end(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
Edit::range_deletion(code_range)
|
||||||
|
};
|
||||||
|
|
||||||
|
let codes = if context.is_rule_enabled(rule) {
|
||||||
|
UnusedCodes {
|
||||||
|
unmatched: vec![suppression.code.to_string()],
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
UnusedCodes {
|
||||||
|
disabled: vec![suppression.code.to_string()],
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut diagnostic = context.report_diagnostic(
|
||||||
|
UnusedNOQA {
|
||||||
|
codes: Some(codes),
|
||||||
|
kind: UnusedNOQAKind::Suppression,
|
||||||
|
},
|
||||||
|
range,
|
||||||
|
);
|
||||||
|
diagnostic.set_fix(Fix::safe_edit(edit));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for error in self
|
||||||
|
.errors
|
||||||
|
.iter()
|
||||||
|
.filter(|error| error.kind == ParseErrorKind::MissingCodes)
|
||||||
|
{
|
||||||
|
let mut diagnostic = context.report_diagnostic(
|
||||||
|
UnusedNOQA {
|
||||||
|
codes: Some(UnusedCodes::default()),
|
||||||
|
kind: UnusedNOQAKind::Suppression,
|
||||||
|
},
|
||||||
|
error.range,
|
||||||
|
);
|
||||||
|
diagnostic.set_fix(Fix::safe_edit(delete_comment(error.range, locator)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -240,6 +359,7 @@ impl<'a> SuppressionsBuilder<'a> {
|
||||||
code: code.into(),
|
code: code.into(),
|
||||||
range: combined_range,
|
range: combined_range,
|
||||||
comments: smallvec![comment.comment.clone(), other.comment.clone()],
|
comments: smallvec![comment.comment.clone(), other.comment.clone()],
|
||||||
|
used: false.into(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -256,6 +376,7 @@ impl<'a> SuppressionsBuilder<'a> {
|
||||||
code: code.into(),
|
code: code.into(),
|
||||||
range: implicit_range,
|
range: implicit_range,
|
||||||
comments: smallvec![comment.comment.clone()],
|
comments: smallvec![comment.comment.clone()],
|
||||||
|
used: false.into(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
self.pending.remove(comment_index);
|
self.pending.remove(comment_index);
|
||||||
|
|
@ -369,8 +490,10 @@ impl<'src> SuppressionParser<'src> {
|
||||||
} else if self.cursor.as_str().starts_with("enable") {
|
} else if self.cursor.as_str().starts_with("enable") {
|
||||||
self.cursor.skip_bytes("enable".len());
|
self.cursor.skip_bytes("enable".len());
|
||||||
Ok(SuppressionAction::Enable)
|
Ok(SuppressionAction::Enable)
|
||||||
} else if self.cursor.as_str().starts_with("noqa") {
|
} else if self.cursor.as_str().starts_with("noqa")
|
||||||
// file-level "noqa" variant, ignore for now
|
|| self.cursor.as_str().starts_with("isort")
|
||||||
|
{
|
||||||
|
// alternate suppression variants, ignore for now
|
||||||
self.error(ParseErrorKind::NotASuppression)
|
self.error(ParseErrorKind::NotASuppression)
|
||||||
} else {
|
} else {
|
||||||
self.error(ParseErrorKind::UnknownAction)
|
self.error(ParseErrorKind::UnknownAction)
|
||||||
|
|
@ -457,9 +580,12 @@ mod tests {
|
||||||
use ruff_text_size::{TextRange, TextSize};
|
use ruff_text_size::{TextRange, TextSize};
|
||||||
use similar::DiffableStr;
|
use similar::DiffableStr;
|
||||||
|
|
||||||
use crate::suppression::{
|
use crate::{
|
||||||
InvalidSuppression, ParseError, Suppression, SuppressionAction, SuppressionComment,
|
settings::LinterSettings,
|
||||||
SuppressionParser, Suppressions,
|
suppression::{
|
||||||
|
InvalidSuppression, ParseError, Suppression, SuppressionAction, SuppressionComment,
|
||||||
|
SuppressionParser, Suppressions,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -1376,7 +1502,11 @@ def bar():
|
||||||
/// Parse all suppressions and errors in a module for testing
|
/// Parse all suppressions and errors in a module for testing
|
||||||
fn debug(source: &'_ str) -> DebugSuppressions<'_> {
|
fn debug(source: &'_ str) -> DebugSuppressions<'_> {
|
||||||
let parsed = parse(source, ParseOptions::from(Mode::Module)).unwrap();
|
let parsed = parse(source, ParseOptions::from(Mode::Module)).unwrap();
|
||||||
let suppressions = Suppressions::from_tokens(source, parsed.tokens());
|
let suppressions = Suppressions::from_tokens(
|
||||||
|
&LinterSettings::default().with_preview_mode(),
|
||||||
|
source,
|
||||||
|
parsed.tokens(),
|
||||||
|
);
|
||||||
DebugSuppressions {
|
DebugSuppressions {
|
||||||
source,
|
source,
|
||||||
suppressions,
|
suppressions,
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ use crate::packaging::detect_package_root;
|
||||||
use crate::settings::types::UnsafeFixes;
|
use crate::settings::types::UnsafeFixes;
|
||||||
use crate::settings::{LinterSettings, flags};
|
use crate::settings::{LinterSettings, flags};
|
||||||
use crate::source_kind::SourceKind;
|
use crate::source_kind::SourceKind;
|
||||||
|
use crate::suppression::Suppressions;
|
||||||
use crate::{Applicability, FixAvailability};
|
use crate::{Applicability, FixAvailability};
|
||||||
use crate::{Locator, directives};
|
use crate::{Locator, directives};
|
||||||
|
|
||||||
|
|
@ -234,6 +235,7 @@ pub(crate) fn test_contents<'a>(
|
||||||
&locator,
|
&locator,
|
||||||
&indexer,
|
&indexer,
|
||||||
);
|
);
|
||||||
|
let suppressions = Suppressions::from_tokens(settings, locator.contents(), parsed.tokens());
|
||||||
let messages = check_path(
|
let messages = check_path(
|
||||||
path,
|
path,
|
||||||
path.parent()
|
path.parent()
|
||||||
|
|
@ -249,6 +251,7 @@ pub(crate) fn test_contents<'a>(
|
||||||
source_type,
|
source_type,
|
||||||
&parsed,
|
&parsed,
|
||||||
target_version,
|
target_version,
|
||||||
|
&suppressions,
|
||||||
);
|
);
|
||||||
|
|
||||||
let source_has_errors = parsed.has_invalid_syntax();
|
let source_has_errors = parsed.has_invalid_syntax();
|
||||||
|
|
@ -299,6 +302,8 @@ pub(crate) fn test_contents<'a>(
|
||||||
&indexer,
|
&indexer,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let suppressions =
|
||||||
|
Suppressions::from_tokens(settings, locator.contents(), parsed.tokens());
|
||||||
let fixed_messages = check_path(
|
let fixed_messages = check_path(
|
||||||
path,
|
path,
|
||||||
None,
|
None,
|
||||||
|
|
@ -312,6 +317,7 @@ pub(crate) fn test_contents<'a>(
|
||||||
source_type,
|
source_type,
|
||||||
&parsed,
|
&parsed,
|
||||||
target_version,
|
target_version,
|
||||||
|
&suppressions,
|
||||||
);
|
);
|
||||||
|
|
||||||
if parsed.has_invalid_syntax() && !source_has_errors {
|
if parsed.has_invalid_syntax() && !source_has_errors {
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue