Compare commits

..

1 Commits

Author SHA1 Message Date
Zanie
17e77fe515 Change the Cargo.toml file 2023-12-06 22:13:44 -06:00
195 changed files with 2612 additions and 15156 deletions

View File

@@ -215,7 +215,7 @@ jobs:
}}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
@@ -338,7 +338,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
@@ -362,7 +362,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: "Install Rust toolchain"
@@ -392,7 +392,7 @@ jobs:
MKDOCS_INSIDERS_SSH_KEY_EXISTS: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY != '' }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/setup-python@v4
- name: "Add SSH key"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
uses: webfactory/ssh-agent@v0.8.0
@@ -455,7 +455,7 @@ jobs:
with:
repository: "astral-sh/ruff-lsp"
- uses: actions/setup-python@v5
- uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}

View File

@@ -20,7 +20,7 @@ jobs:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.ref }}
- uses: actions/setup-python@v5
- uses: actions/setup-python@v4
- name: "Add SSH key"
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
uses: webfactory/ssh-agent@v0.8.0

View File

@@ -20,7 +20,7 @@ jobs:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
@@ -43,7 +43,7 @@ jobs:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
@@ -69,7 +69,7 @@ jobs:
target: [x64, x86]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: ${{ matrix.target }}
@@ -97,7 +97,7 @@ jobs:
target: [x86_64, i686]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
@@ -124,7 +124,7 @@ jobs:
target: [aarch64, armv7, s390x, ppc64le, ppc64]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: "Build wheels"
@@ -161,7 +161,7 @@ jobs:
- i686-unknown-linux-musl
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
@@ -197,7 +197,7 @@ jobs:
arch: armv7
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: "Build wheels"
@@ -237,7 +237,7 @@ jobs:
- uses: actions/download-artifact@v3
with:
name: wheels
- uses: actions/setup-python@v5
- uses: actions/setup-python@v4
- name: "Publish to PyPi"
env:
TWINE_USERNAME: __token__

View File

@@ -36,7 +36,7 @@ jobs:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.sha }}
- uses: actions/setup-python@v5
- uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: "Prep README.md"
@@ -63,7 +63,7 @@ jobs:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.sha }}
- uses: actions/setup-python@v5
- uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
@@ -103,7 +103,7 @@ jobs:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.sha }}
- uses: actions/setup-python@v5
- uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
@@ -151,7 +151,7 @@ jobs:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.sha }}
- uses: actions/setup-python@v5
- uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: ${{ matrix.platform.arch }}
@@ -199,7 +199,7 @@ jobs:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.sha }}
- uses: actions/setup-python@v5
- uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
@@ -258,7 +258,7 @@ jobs:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.sha }}
- uses: actions/setup-python@v5
- uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: "Prep README.md"
@@ -313,7 +313,7 @@ jobs:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.sha }}
- uses: actions/setup-python@v5
- uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: x64
@@ -332,10 +332,10 @@ jobs:
image: alpine:latest
options: -v ${{ github.workspace }}:/io -w /io
run: |
apk add python3
python -m venv .venv
.venv/bin/pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
.venv/bin/ruff check --help
apk add py3-pip
pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links /io/dist/ --force-reinstall
ruff --help
python -m ruff --help
- name: "Upload wheels"
uses: actions/upload-artifact@v3
with:
@@ -369,7 +369,7 @@ jobs:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.sha }}
- uses: actions/setup-python@v5
- uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: "Prep README.md"
@@ -388,11 +388,10 @@ jobs:
distro: alpine_latest
githubToken: ${{ github.token }}
install: |
apk add python3
apk add py3-pip
run: |
python -m venv .venv
.venv/bin/pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
.venv/bin/ruff check --help
pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
ruff check --help
- name: "Upload wheels"
uses: actions/upload-artifact@v3
with:

153
Cargo.lock generated
View File

@@ -16,15 +16,14 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "ahash"
version = "0.8.6"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a"
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
dependencies = [
"cfg-if",
"getrandom",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
@@ -382,7 +381,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.39",
]
[[package]]
@@ -607,7 +606,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim",
"syn 2.0.40",
"syn 2.0.39",
]
[[package]]
@@ -618,7 +617,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
dependencies = [
"darling_core",
"quote",
"syn 2.0.40",
"syn 2.0.39",
]
[[package]]
@@ -791,14 +790,14 @@ dependencies = [
[[package]]
name = "filetime"
version = "0.2.23"
version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0"
dependencies = [
"cfg-if",
"libc",
"redox_syscall 0.4.1",
"windows-sys 0.52.0",
"redox_syscall 0.3.5",
"windows-sys 0.48.0",
]
[[package]]
@@ -1123,15 +1122,15 @@ dependencies = [
[[package]]
name = "is-macro"
version = "0.3.1"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc74b7abae208af9314a406bd7dcc65091230b6e749c09e07a645885fecf34f9"
checksum = "f4467ed1321b310c2625c5aa6c1b1ffc5de4d9e42668cf697a08fb033ee8265e"
dependencies = [
"Inflector",
"pmutil 0.6.1",
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.39",
]
[[package]]
@@ -1709,7 +1708,7 @@ checksum = "52a40bc70c2c58040d2d8b167ba9a5ff59fc9dab7ad44771cfde3dcfde7a09c6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.39",
]
[[package]]
@@ -2270,7 +2269,7 @@ dependencies = [
"proc-macro2",
"quote",
"ruff_python_trivia",
"syn 2.0.40",
"syn 2.0.39",
]
[[package]]
@@ -2677,18 +2676,18 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
[[package]]
name = "serde"
version = "1.0.193"
version = "1.0.190"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde-wasm-bindgen"
version = "0.6.3"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9b713f70513ae1f8d92665bbbbda5c295c2cf1da5542881ae5eefe20c9af132"
checksum = "17ba92964781421b6cef36bf0d7da26d201e96d84e1b10e7ae6ed416e516906d"
dependencies = [
"js-sys",
"serde",
@@ -2697,13 +2696,13 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.193"
version = "1.0.190"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.39",
]
[[package]]
@@ -2765,7 +2764,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.39",
]
[[package]]
@@ -2869,7 +2868,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.40",
"syn 2.0.39",
]
[[package]]
@@ -2885,9 +2884,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.40"
version = "2.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13fa70a4ee923979ffb522cacce59d34421ebdea5625e1073c4326ef9d2dd42e"
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
dependencies = [
"proc-macro2",
"quote",
@@ -2974,7 +2973,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.39",
]
[[package]]
@@ -2986,7 +2985,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.39",
"test-case-core",
]
@@ -3007,7 +3006,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.39",
]
[[package]]
@@ -3169,7 +3168,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.39",
]
[[package]]
@@ -3387,7 +3386,7 @@ checksum = "f49e7f3f3db8040a100710a11932239fd30697115e2ba4107080d8252939845e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.39",
]
[[package]]
@@ -3481,7 +3480,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.39",
"wasm-bindgen-shared",
]
@@ -3515,7 +3514,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.39",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -3548,7 +3547,7 @@ checksum = "493fcbab756bb764fa37e6bee8cec2dd709eb4273d06d0c282a5e74275ded735"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.39",
]
[[package]]
@@ -3645,15 +3644,6 @@ dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.0",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
@@ -3684,21 +3674,6 @@ dependencies = [
"windows_x86_64_msvc 0.48.5",
]
[[package]]
name = "windows-targets"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
dependencies = [
"windows_aarch64_gnullvm 0.52.0",
"windows_aarch64_msvc 0.52.0",
"windows_i686_gnu 0.52.0",
"windows_i686_msvc 0.52.0",
"windows_x86_64_gnu 0.52.0",
"windows_x86_64_gnullvm 0.52.0",
"windows_x86_64_msvc 0.52.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
@@ -3711,12 +3686,6 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
@@ -3729,12 +3698,6 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
@@ -3747,12 +3710,6 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
@@ -3765,12 +3722,6 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
@@ -3783,12 +3734,6 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
@@ -3801,12 +3746,6 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
@@ -3819,12 +3758,6 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "winnow"
version = "0.5.15"
@@ -3863,23 +3796,3 @@ checksum = "fe5c30ade05e61656247b2e334a031dfd0cc466fadef865bdcdea8d537951bf1"
dependencies = [
"winapi",
]
[[package]]
name = "zerocopy"
version = "0.7.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "306dca4455518f1f31635ec308b6b3e4eb1b11758cefafc782827d0aa7acb5c7"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be912bf68235a88fbefd1b73415cb218405958d1655b2ece9035a19920bdf6ba"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.40",
]

View File

@@ -9,7 +9,7 @@ homepage = "https://docs.astral.sh/ruff"
documentation = "https://docs.astral.sh/ruff"
repository = "https://github.com/astral-sh/ruff"
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
license = "MIT"
license = "MIT2"
[workspace.dependencies]
anyhow = { version = "1.0.69" }
@@ -17,12 +17,12 @@ bitflags = { version = "2.4.1" }
chrono = { version = "0.4.31", default-features = false, features = ["clock"] }
clap = { version = "4.4.7", features = ["derive"] }
colored = { version = "2.0.0" }
filetime = { version = "0.2.23" }
filetime = { version = "0.2.20" }
glob = { version = "0.3.1" }
globset = { version = "0.4.14" }
ignore = { version = "0.4.20" }
insta = { version = "1.34.0", feature = ["filters", "glob"] }
is-macro = { version = "0.3.1" }
is-macro = { version = "0.3.0" }
itertools = { version = "0.11.0" }
libcst = { version = "1.1.0", default-features = false }
log = { version = "0.4.17" }
@@ -34,7 +34,7 @@ quote = { version = "1.0.23" }
regex = { version = "1.10.2" }
rustc-hash = { version = "1.1.0" }
schemars = { version = "0.8.16" }
serde = { version = "1.0.193", features = ["derive"] }
serde = { version = "1.0.190", features = ["derive"] }
serde_json = { version = "1.0.108" }
shellexpand = { version = "3.0.0" }
similar = { version = "2.3.0", features = ["inline"] }
@@ -42,7 +42,7 @@ smallvec = { version = "1.11.2" }
static_assertions = "1.1.0"
strum = { version = "0.25.0", features = ["strum_macros"] }
strum_macros = { version = "0.25.3" }
syn = { version = "2.0.40" }
syn = { version = "2.0.39" }
test-case = { version = "3.2.1" }
thiserror = { version = "1.0.50" }
toml = { version = "0.7.8" }

View File

@@ -125,7 +125,15 @@ impl Printer {
if let Some(fixables) = fixables {
let fix_prefix = format!("[{}]", "*".cyan());
if self.unsafe_fixes.is_hint() {
if self.unsafe_fixes.is_enabled() {
if fixables.applicable > 0 {
writeln!(
writer,
"{fix_prefix} {} fixable with the --fix option.",
fixables.applicable
)?;
}
} else {
if fixables.applicable > 0 && fixables.unapplicable_unsafe > 0 {
let es = if fixables.unapplicable_unsafe == 1 {
""
@@ -155,14 +163,6 @@ impl Printer {
fixables.unapplicable_unsafe
)?;
}
} else {
if fixables.applicable > 0 {
writeln!(
writer,
"{fix_prefix} {} fixable with the --fix option.",
fixables.applicable
)?;
}
}
}
} else {

View File

@@ -1158,44 +1158,6 @@ fn check_hints_hidden_unsafe_fixes_with_no_safe_fixes() {
"###);
}
#[test]
fn check_no_hint_for_hidden_unsafe_fixes_when_disabled() {
let mut cmd = RuffCheck::default()
.args(["--select", "F601,UP034", "--no-unsafe-fixes"])
.build();
assert_cmd_snapshot!(cmd
.pass_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n"),
@r###"
success: false
exit_code: 1
----- stdout -----
-:1:14: F601 Dictionary key literal `'a'` repeated
-:2:7: UP034 [*] Avoid extraneous parentheses
Found 2 errors.
[*] 1 fixable with the --fix option.
----- stderr -----
"###);
}
#[test]
fn check_no_hint_for_hidden_unsafe_fixes_with_no_safe_fixes_when_disabled() {
let mut cmd = RuffCheck::default()
.args(["--select", "F601", "--no-unsafe-fixes"])
.build();
assert_cmd_snapshot!(cmd
.pass_stdin("x = {'a': 1, 'a': 1}\n"),
@r###"
success: false
exit_code: 1
----- stdout -----
-:1:14: F601 Dictionary key literal `'a'` repeated
Found 1 error.
----- stderr -----
"###);
}
#[test]
fn check_shows_unsafe_fixes_with_opt_in() {
let mut cmd = RuffCheck::default()

View File

@@ -8,7 +8,6 @@ def func(address):
# Error
"0.0.0.0"
'0.0.0.0'
f"0.0.0.0"
# Error

View File

@@ -5,9 +5,6 @@ with open("/abc/tmp", "w") as f:
with open("/tmp/abc", "w") as f:
f.write("def")
with open(f"/tmp/abc", "w") as f:
f.write("def")
with open("/var/tmp/123", "w") as f:
f.write("def")

View File

@@ -27,7 +27,7 @@ def f_ok():
raise RuntimeError(msg)
def f_msg_defined():
def f_unfixable():
msg = "hello"
raise RuntimeError("This is an example exception")

View File

@@ -32,7 +32,6 @@ def f8(x: bytes = b"50 character byte stringgggggggggggggggggggggggggg\xff") ->
foo: str = "50 character stringggggggggggggggggggggggggggggggg"
bar: str = "51 character stringgggggggggggggggggggggggggggggggg"
baz: str = f"51 character stringgggggggggggggggggggggggggggggggg"
baz: bytes = b"50 character byte stringgggggggggggggggggggggggggg"

View File

@@ -29,10 +29,6 @@ baz: bytes = b"50 character byte stringgggggggggggggggggggggggggg" # OK
qux: bytes = b"51 character byte stringggggggggggggggggggggggggggg\xff" # Error: PYI053
ffoo: str = f"50 character stringggggggggggggggggggggggggggggggg" # OK
fbar: str = f"51 character stringgggggggggggggggggggggggggggggggg" # Error: PYI053
class Demo:
"""Docstrings are excluded from this rule. Some padding.""" # OK

View File

@@ -1,4 +0,0 @@
from a import x
import b
from c import y
import d

View File

@@ -55,6 +55,3 @@ def model_assign() -> None:
Bad = apps.get_model() # N806
Bad = apps.get_model(model_name="Stream") # N806
Address: Type = apps.get_model("zerver", variable) # OK
ValidationError = import_string(variable) # N806

View File

@@ -72,15 +72,3 @@ a = 42 # (Two spaces)
# EF Means test is giving error and Failing
#! Means test is segfaulting
# 8 Means test runs forever
#: Colon prefix is okay
###This is a variable ###
# We should strip the space, but preserve the hashes.
#: E266:1:3
## Foo
a = 1 ## Foo
a = 1 #:Foo

View File

@@ -60,6 +60,3 @@ def f():
if (a and
b):
pass
#: Okay
def f():
return 1

View File

@@ -19,32 +19,21 @@ if x > 0:
else:
import e
import sys
sys.path.insert(0, "some/path")
__some__magic = 1
import f
import matplotlib
matplotlib.use("Agg")
import g
__some__magic = 1
import h
def foo() -> None:
import i
import e
if __name__ == "__main__":
import j
import g
import k; import l
import h; import i
if __name__ == "__main__":
import m; \
import n
import j; \
import k

View File

@@ -3,11 +3,6 @@ import subprocess
# Errors.
subprocess.run("ls")
subprocess.run("ls", shell=True)
subprocess.run(
["ls"],
shell=False,
)
subprocess.run(["ls"], **kwargs)
# Non-errors.
subprocess.run("ls", check=True)

View File

@@ -5,11 +5,3 @@ A = 3.14 * r ** 2 # FURB152
C = 6.28 * r # FURB152
e = 2.71 # FURB152
r = 3.15 # OK
r = 3.141 # FURB152
r = 3.1415 # FURB152
e = 2.7 # OK

View File

@@ -16,8 +16,6 @@ special_log(1, 2)
special_log(1, 10)
special_log(1, math.e)
special_log(1, special_e)
math.log(1, 2.0)
math.log(1, 10.0)
# Ok.
math.log2(1)
@@ -47,6 +45,3 @@ def log(*args):
log(1, 2)
log(1, 10)
log(1, math.e)
math.log(1, 2.0001)
math.log(1, 10.0001)

View File

@@ -1,57 +0,0 @@
import hashlib
from hashlib import (
blake2b,
blake2s,
md5,
sha1,
sha3_224,
sha3_256,
sha3_384,
sha3_512,
sha224,
)
from hashlib import sha256
from hashlib import sha256 as hash_algo
from hashlib import sha384, sha512, shake_128, shake_256
# these will match
blake2b().digest().hex()
blake2s().digest().hex()
md5().digest().hex()
sha1().digest().hex()
sha224().digest().hex()
sha256().digest().hex()
sha384().digest().hex()
sha3_224().digest().hex()
sha3_256().digest().hex()
sha3_384().digest().hex()
sha3_512().digest().hex()
sha512().digest().hex()
shake_128().digest(10).hex()
shake_256().digest(10).hex()
hashlib.sha256().digest().hex()
sha256(b"text").digest().hex()
hash_algo().digest().hex()
# not yet supported
h = sha256()
h.digest().hex()
# these will not
sha256().digest()
sha256().digest().hex("_")
sha256().digest().hex(bytes_per_sep=4)
sha256().hexdigest()
class Hash:
def digest(self) -> bytes:
return b""
Hash().digest().hex()

View File

@@ -63,29 +63,11 @@ def f():
tasks = [asyncio.create_task(task) for task in tasks]
# Error
# OK (false negative)
def f():
task = asyncio.create_task(coordinator.ws_connect())
# Error
def f():
loop = asyncio.get_running_loop()
task: asyncio.Task = loop.create_task(coordinator.ws_connect())
# OK (potential false negative)
def f():
task = asyncio.create_task(coordinator.ws_connect())
background_tasks.add(task)
# OK
async def f():
task = asyncio.create_task(coordinator.ws_connect())
await task
# OK (potential false negative)
def f():
do_nothing_with_the_task(asyncio.create_task(coordinator.ws_connect()))
@@ -106,19 +88,3 @@ def f():
def f():
loop = asyncio.get_running_loop()
loop.do_thing(coordinator.ws_connect())
# OK
async def f():
task = unused = asyncio.create_task(coordinator.ws_connect())
await task
# OK (false negative)
async def f():
task = unused = asyncio.create_task(coordinator.ws_connect())
# OK
async def f():
task[i] = asyncio.create_task(coordinator.ws_connect())

View File

@@ -59,7 +59,3 @@ class F(BaseSettings):
without_annotation = []
class_variable: ClassVar[list[int]] = []
final_variable: Final[list[int]] = []
class E:
without_annotation = []

View File

@@ -3,12 +3,11 @@ use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
use crate::codes::Rule;
use crate::rules::{flake8_import_conventions, flake8_pyi, pyflakes, pylint, ruff};
use crate::rules::{flake8_import_conventions, flake8_pyi, pyflakes, pylint};
/// Run lint rules over the [`Binding`]s.
pub(crate) fn bindings(checker: &mut Checker) {
if !checker.any_enabled(&[
Rule::AsyncioDanglingTask,
Rule::InvalidAllFormat,
Rule::InvalidAllObject,
Rule::NonAsciiName,
@@ -72,12 +71,5 @@ pub(crate) fn bindings(checker: &mut Checker) {
checker.diagnostics.push(diagnostic);
}
}
if checker.enabled(Rule::AsyncioDanglingTask) {
if let Some(diagnostic) =
ruff::rules::asyncio_dangling_binding(binding, &checker.semantic)
{
checker.diagnostics.push(diagnostic);
}
}
}
}

View File

@@ -356,8 +356,6 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
Rule::FString,
// flynt
Rule::StaticJoinToFString,
// refurb
Rule::HashlibDigestHex,
]) {
if let Expr::Attribute(ast::ExprAttribute { value, attr, .. }) = func.as_ref() {
let attr = attr.as_str();
@@ -583,9 +581,6 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
if checker.enabled(Rule::HashlibInsecureHashFunction) {
flake8_bandit::rules::hashlib_insecure_hash_functions(checker, call);
}
if checker.enabled(Rule::HashlibDigestHex) {
refurb::rules::hashlib_digest_hex(checker, call);
}
if checker.enabled(Rule::RequestWithoutTimeout) {
flake8_bandit::rules::request_without_timeout(checker, call);
}
@@ -1275,12 +1270,32 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
refurb::rules::math_constant(checker, number_literal);
}
}
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
Expr::BytesLiteral(_) => {
if checker.source_type.is_stub() && checker.enabled(Rule::StringOrBytesTooLong) {
flake8_pyi::rules::string_or_bytes_too_long(checker, expr);
}
}
Expr::StringLiteral(string) => {
if checker.enabled(Rule::HardcodedBindAllInterfaces) {
if let Some(diagnostic) =
flake8_bandit::rules::hardcoded_bind_all_interfaces(string)
{
checker.diagnostics.push(diagnostic);
}
}
if checker.enabled(Rule::HardcodedTempFile) {
flake8_bandit::rules::hardcoded_tmp_directory(checker, string);
}
if checker.enabled(Rule::UnicodeKindPrefix) {
for string_part in value.parts() {
for string_part in string.value.parts() {
pyupgrade::rules::unicode_kind_prefix(checker, string_part);
}
}
if checker.source_type.is_stub() {
if checker.enabled(Rule::StringOrBytesTooLong) {
flake8_pyi::rules::string_or_bytes_too_long(checker, expr);
}
}
}
Expr::IfExp(
if_exp @ ast::ExprIfExp {

View File

@@ -10,7 +10,6 @@ pub(super) use module::module;
pub(super) use parameter::parameter;
pub(super) use parameters::parameters;
pub(super) use statement::statement;
pub(super) use string_like::string_like;
pub(super) use suite::suite;
pub(super) use unresolved_references::unresolved_references;
@@ -26,6 +25,5 @@ mod module;
mod parameter;
mod parameters;
mod statement;
mod string_like;
mod suite;
mod unresolved_references;

View File

@@ -1571,11 +1571,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
pylint::rules::named_expr_without_context(checker, value);
}
if checker.enabled(Rule::AsyncioDanglingTask) {
if let Some(diagnostic) =
ruff::rules::asyncio_dangling_task(value, checker.semantic())
{
checker.diagnostics.push(diagnostic);
}
ruff::rules::asyncio_dangling_task(checker, value);
}
if checker.enabled(Rule::RepeatedAppend) {
refurb::rules::repeated_append(checker, stmt);

View File

@@ -1,20 +0,0 @@
use ruff_python_ast::StringLike;
use crate::checkers::ast::Checker;
use crate::codes::Rule;
use crate::rules::{flake8_bandit, flake8_pyi};
/// Run lint rules over a [`StringLike`] syntax nodes.
pub(crate) fn string_like(string_like: StringLike, checker: &mut Checker) {
if checker.enabled(Rule::HardcodedBindAllInterfaces) {
flake8_bandit::rules::hardcoded_bind_all_interfaces(checker, string_like);
}
if checker.enabled(Rule::HardcodedTempFile) {
flake8_bandit::rules::hardcoded_tmp_directory(checker, string_like);
}
if checker.source_type.is_stub() {
if checker.enabled(Rule::StringOrBytesTooLong) {
flake8_pyi::rules::string_or_bytes_too_long(checker, string_like);
}
}
}

View File

@@ -44,12 +44,12 @@ use ruff_python_ast::helpers::{
};
use ruff_python_ast::identifier::Identifier;
use ruff_python_ast::str::trailing_quote;
use ruff_python_ast::visitor::{walk_except_handler, walk_f_string_element, walk_pattern, Visitor};
use ruff_python_ast::visitor::{walk_except_handler, walk_pattern, Visitor};
use ruff_python_ast::{helpers, str, visitor, PySourceType};
use ruff_python_codegen::{Generator, Quote, Stylist};
use ruff_python_index::Indexer;
use ruff_python_parser::typing::{parse_type_annotation, AnnotationKind};
use ruff_python_semantic::analyze::{imports, typing, visibility};
use ruff_python_semantic::analyze::{typing, visibility};
use ruff_python_semantic::{
BindingFlags, BindingId, BindingKind, Exceptions, Export, FromImport, Globals, Import, Module,
ModuleKind, NodeId, ScopeId, ScopeKind, SemanticModel, SemanticModelFlags, Snapshot,
@@ -303,12 +303,9 @@ where
}
_ => {
self.semantic.flags |= SemanticModelFlags::FUTURES_BOUNDARY;
if !(self.semantic.seen_import_boundary()
|| helpers::is_assignment_to_a_dunder(stmt)
|| helpers::in_nested_block(self.semantic.current_statements())
|| imports::is_matplotlib_activation(stmt, self.semantic())
|| self.settings.preview.is_enabled()
&& imports::is_sys_path_modification(stmt, self.semantic()))
if !self.semantic.seen_import_boundary()
&& !helpers::is_assignment_to_a_dunder(stmt)
&& !helpers::in_nested_block(self.semantic.current_statements())
{
self.semantic.flags |= SemanticModelFlags::IMPORT_BOUNDARY;
}
@@ -818,7 +815,8 @@ where
fn visit_expr(&mut self, expr: &'b Expr) {
// Step 0: Pre-processing
if !self.semantic.in_typing_literal()
if !self.semantic.in_f_string()
&& !self.semantic.in_typing_literal()
&& !self.semantic.in_deferred_type_definition()
&& self.semantic.in_type_definition()
&& self.semantic.future_annotations()
@@ -1240,7 +1238,10 @@ where
}
}
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
if self.semantic.in_type_definition() && !self.semantic.in_typing_literal() {
if self.semantic.in_type_definition()
&& !self.semantic.in_typing_literal()
&& !self.semantic.in_f_string()
{
self.deferred.string_type_definitions.push((
expr.range(),
value.to_str(),
@@ -1270,13 +1271,6 @@ where
// Step 4: Analysis
analyze::expression(expr, self);
match expr {
Expr::StringLiteral(string_literal) => {
analyze::string_like(string_literal.into(), self);
}
Expr::BytesLiteral(bytes_literal) => analyze::string_like(bytes_literal.into(), self),
_ => {}
}
self.semantic.flags = flags_snapshot;
self.semantic.pop_node();
@@ -1332,6 +1326,17 @@ where
self.semantic.flags = flags_snapshot;
}
fn visit_format_spec(&mut self, format_spec: &'b Expr) {
match format_spec {
Expr::FString(ast::ExprFString { value, .. }) => {
for expr in value.elements() {
self.visit_expr(expr);
}
}
_ => unreachable!("Unexpected expression for format_spec"),
}
}
fn visit_parameters(&mut self, parameters: &'b Parameters) {
// Step 1: Binding.
// Bind, but intentionally avoid walking default expressions, as we handle them
@@ -1441,16 +1446,6 @@ where
.push((bound, self.semantic.snapshot()));
}
}
fn visit_f_string_element(&mut self, f_string_element: &'b ast::FStringElement) {
// Step 2: Traversal
walk_f_string_element(self, f_string_element);
// Step 4: Analysis
if let Some(literal) = f_string_element.as_literal() {
analyze::string_like(literal.into(), self);
}
}
}
impl<'a> Checker<'a> {

View File

@@ -965,7 +965,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Refurb, "169") => (RuleGroup::Preview, rules::refurb::rules::TypeNoneComparison),
(Refurb, "171") => (RuleGroup::Preview, rules::refurb::rules::SingleItemMembershipTest),
(Refurb, "177") => (RuleGroup::Preview, rules::refurb::rules::ImplicitCwd),
(Refurb, "181") => (RuleGroup::Preview, rules::refurb::rules::HashlibDigestHex),
// flake8-logging
(Flake8Logging, "001") => (RuleGroup::Preview, rules::flake8_logging::rules::DirectLoggerInstantiation),

View File

@@ -1,9 +1,6 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{self as ast, StringLike};
use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
use ruff_python_ast::ExprStringLiteral;
/// ## What it does
/// Checks for hardcoded bindings to all network interfaces (`0.0.0.0`).
@@ -37,16 +34,10 @@ impl Violation for HardcodedBindAllInterfaces {
}
/// S104
pub(crate) fn hardcoded_bind_all_interfaces(checker: &mut Checker, string: StringLike) {
let is_bind_all_interface = match string {
StringLike::StringLiteral(ast::ExprStringLiteral { value, .. }) => value == "0.0.0.0",
StringLike::FStringLiteral(ast::FStringLiteralElement { value, .. }) => value == "0.0.0.0",
StringLike::BytesLiteral(_) => return,
};
if is_bind_all_interface {
checker
.diagnostics
.push(Diagnostic::new(HardcodedBindAllInterfaces, string.range()));
pub(crate) fn hardcoded_bind_all_interfaces(string: &ExprStringLiteral) -> Option<Diagnostic> {
if string.value.to_str() == "0.0.0.0" {
Some(Diagnostic::new(HardcodedBindAllInterfaces, string.range))
} else {
None
}
}

View File

@@ -1,5 +1,4 @@
use ruff_python_ast::{self as ast, Expr, StringLike};
use ruff_text_size::Ranged;
use ruff_python_ast::{self as ast, Expr};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
@@ -52,19 +51,13 @@ impl Violation for HardcodedTempFile {
}
/// S108
pub(crate) fn hardcoded_tmp_directory(checker: &mut Checker, string: StringLike) {
let value = match string {
StringLike::StringLiteral(ast::ExprStringLiteral { value, .. }) => value.to_str(),
StringLike::FStringLiteral(ast::FStringLiteralElement { value, .. }) => value,
StringLike::BytesLiteral(_) => return,
};
pub(crate) fn hardcoded_tmp_directory(checker: &mut Checker, string: &ast::ExprStringLiteral) {
if !checker
.settings
.flake8_bandit
.hardcoded_tmp_directory
.iter()
.any(|prefix| value.starts_with(prefix))
.any(|prefix| string.value.to_str().starts_with(prefix))
{
return;
}
@@ -83,8 +76,8 @@ pub(crate) fn hardcoded_tmp_directory(checker: &mut Checker, string: StringLike)
checker.diagnostics.push(Diagnostic::new(
HardcodedTempFile {
string: value.to_string(),
string: string.value.to_string(),
},
string.range(),
string.range,
));
}

View File

@@ -7,7 +7,6 @@ S104.py:9:1: S104 Possible binding to all interfaces
9 | "0.0.0.0"
| ^^^^^^^^^ S104
10 | '0.0.0.0'
11 | f"0.0.0.0"
|
S104.py:10:1: S104 Possible binding to all interfaces
@@ -16,30 +15,21 @@ S104.py:10:1: S104 Possible binding to all interfaces
9 | "0.0.0.0"
10 | '0.0.0.0'
| ^^^^^^^^^ S104
11 | f"0.0.0.0"
|
S104.py:11:3: S104 Possible binding to all interfaces
S104.py:14:6: S104 Possible binding to all interfaces
|
9 | "0.0.0.0"
10 | '0.0.0.0'
11 | f"0.0.0.0"
| ^^^^^^^ S104
|
S104.py:15:6: S104 Possible binding to all interfaces
|
14 | # Error
15 | func("0.0.0.0")
13 | # Error
14 | func("0.0.0.0")
| ^^^^^^^^^ S104
|
S104.py:19:9: S104 Possible binding to all interfaces
S104.py:18:9: S104 Possible binding to all interfaces
|
18 | def my_func():
19 | x = "0.0.0.0"
17 | def my_func():
18 | x = "0.0.0.0"
| ^^^^^^^^^ S104
20 | print(x)
19 | print(x)
|

View File

@@ -10,31 +10,22 @@ S108.py:5:11: S108 Probable insecure usage of temporary file or directory: "/tmp
6 | f.write("def")
|
S108.py:8:13: S108 Probable insecure usage of temporary file or directory: "/tmp/abc"
S108.py:8:11: S108 Probable insecure usage of temporary file or directory: "/var/tmp/123"
|
6 | f.write("def")
7 |
8 | with open(f"/tmp/abc", "w") as f:
| ^^^^^^^^ S108
8 | with open("/var/tmp/123", "w") as f:
| ^^^^^^^^^^^^^^ S108
9 | f.write("def")
|
S108.py:11:11: S108 Probable insecure usage of temporary file or directory: "/var/tmp/123"
S108.py:11:11: S108 Probable insecure usage of temporary file or directory: "/dev/shm/unit/test"
|
9 | f.write("def")
10 |
11 | with open("/var/tmp/123", "w") as f:
| ^^^^^^^^^^^^^^ S108
12 | f.write("def")
|
S108.py:14:11: S108 Probable insecure usage of temporary file or directory: "/dev/shm/unit/test"
|
12 | f.write("def")
13 |
14 | with open("/dev/shm/unit/test", "w") as f:
11 | with open("/dev/shm/unit/test", "w") as f:
| ^^^^^^^^^^^^^^^^^^^^ S108
15 | f.write("def")
12 | f.write("def")
|

View File

@@ -10,39 +10,30 @@ S108.py:5:11: S108 Probable insecure usage of temporary file or directory: "/tmp
6 | f.write("def")
|
S108.py:8:13: S108 Probable insecure usage of temporary file or directory: "/tmp/abc"
S108.py:8:11: S108 Probable insecure usage of temporary file or directory: "/var/tmp/123"
|
6 | f.write("def")
7 |
8 | with open(f"/tmp/abc", "w") as f:
| ^^^^^^^^ S108
8 | with open("/var/tmp/123", "w") as f:
| ^^^^^^^^^^^^^^ S108
9 | f.write("def")
|
S108.py:11:11: S108 Probable insecure usage of temporary file or directory: "/var/tmp/123"
S108.py:11:11: S108 Probable insecure usage of temporary file or directory: "/dev/shm/unit/test"
|
9 | f.write("def")
10 |
11 | with open("/var/tmp/123", "w") as f:
| ^^^^^^^^^^^^^^ S108
12 | f.write("def")
|
S108.py:14:11: S108 Probable insecure usage of temporary file or directory: "/dev/shm/unit/test"
|
12 | f.write("def")
13 |
14 | with open("/dev/shm/unit/test", "w") as f:
11 | with open("/dev/shm/unit/test", "w") as f:
| ^^^^^^^^^^^^^^^^^^^^ S108
15 | f.write("def")
12 | f.write("def")
|
S108.py:18:11: S108 Probable insecure usage of temporary file or directory: "/foo/bar"
S108.py:15:11: S108 Probable insecure usage of temporary file or directory: "/foo/bar"
|
17 | # not ok by config
18 | with open("/foo/bar", "w") as f:
14 | # not ok by config
15 | with open("/foo/bar", "w") as f:
| ^^^^^^^^^^ S108
19 | f.write("def")
16 | f.write("def")
|

View File

@@ -1083,7 +1083,7 @@ pub(crate) fn fix_unnecessary_map(
// If the expression is embedded in an f-string, surround it with spaces to avoid
// syntax errors.
if matches!(object_type, ObjectType::Set | ObjectType::Dict) {
if parent.is_some_and(Expr::is_f_string_expr) {
if parent.is_some_and(Expr::is_formatted_value_expr) {
content = format!(" {content} ");
}
}

View File

@@ -191,13 +191,15 @@ pub(crate) fn string_in_exception(checker: &mut Checker, stmt: &Stmt, exc: &Expr
if let Some(indentation) =
whitespace::indentation(checker.locator(), stmt)
{
diagnostic.set_fix(generate_fix(
stmt,
first,
indentation,
checker.stylist(),
checker.locator(),
));
if checker.semantic().is_available("msg") {
diagnostic.set_fix(generate_fix(
stmt,
first,
indentation,
checker.stylist(),
checker.locator(),
));
}
}
checker.diagnostics.push(diagnostic);
}
@@ -209,13 +211,15 @@ pub(crate) fn string_in_exception(checker: &mut Checker, stmt: &Stmt, exc: &Expr
let mut diagnostic = Diagnostic::new(FStringInException, first.range());
if let Some(indentation) = whitespace::indentation(checker.locator(), stmt)
{
diagnostic.set_fix(generate_fix(
stmt,
first,
indentation,
checker.stylist(),
checker.locator(),
));
if checker.semantic().is_available("msg") {
diagnostic.set_fix(generate_fix(
stmt,
first,
indentation,
checker.stylist(),
checker.locator(),
));
}
}
checker.diagnostics.push(diagnostic);
}
@@ -232,13 +236,15 @@ pub(crate) fn string_in_exception(checker: &mut Checker, stmt: &Stmt, exc: &Expr
if let Some(indentation) =
whitespace::indentation(checker.locator(), stmt)
{
diagnostic.set_fix(generate_fix(
stmt,
first,
indentation,
checker.stylist(),
checker.locator(),
));
if checker.semantic().is_available("msg") {
diagnostic.set_fix(generate_fix(
stmt,
first,
indentation,
checker.stylist(),
checker.locator(),
));
}
}
checker.diagnostics.push(diagnostic);
}

View File

@@ -59,26 +59,15 @@ EM.py:22:24: EM103 [*] Exception must not use a `.format()` string directly, ass
24 25 |
25 26 | def f_ok():
EM.py:32:24: EM101 [*] Exception must not use a string literal, assign to variable first
EM.py:32:24: EM101 Exception must not use a string literal, assign to variable first
|
30 | def f_msg_defined():
30 | def f_unfixable():
31 | msg = "hello"
32 | raise RuntimeError("This is an example exception")
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ EM101
|
= help: Assign to variable; remove string literal
Unsafe fix
29 29 |
30 30 | def f_msg_defined():
31 31 | msg = "hello"
32 |- raise RuntimeError("This is an example exception")
32 |+ msg = "This is an example exception"
33 |+ raise RuntimeError(msg)
33 34 |
34 35 |
35 36 | def f_msg_in_nested_scope():
EM.py:39:24: EM101 [*] Exception must not use a string literal, assign to variable first
|
37 | msg = "hello"
@@ -99,7 +88,7 @@ EM.py:39:24: EM101 [*] Exception must not use a string literal, assign to variab
41 42 |
42 43 | def f_msg_in_parent_scope():
EM.py:46:28: EM101 [*] Exception must not use a string literal, assign to variable first
EM.py:46:28: EM101 Exception must not use a string literal, assign to variable first
|
45 | def nested():
46 | raise RuntimeError("This is an example exception")
@@ -107,17 +96,6 @@ EM.py:46:28: EM101 [*] Exception must not use a string literal, assign to variab
|
= help: Assign to variable; remove string literal
Unsafe fix
43 43 | msg = "hello"
44 44 |
45 45 | def nested():
46 |- raise RuntimeError("This is an example exception")
46 |+ msg = "This is an example exception"
47 |+ raise RuntimeError(msg)
47 48 |
48 49 |
49 50 | def f_fix_indentation_check(foo):
EM.py:51:28: EM101 [*] Exception must not use a string literal, assign to variable first
|
49 | def f_fix_indentation_check(foo):

View File

@@ -97,26 +97,15 @@ EM.py:22:24: EM103 [*] Exception must not use a `.format()` string directly, ass
24 25 |
25 26 | def f_ok():
EM.py:32:24: EM101 [*] Exception must not use a string literal, assign to variable first
EM.py:32:24: EM101 Exception must not use a string literal, assign to variable first
|
30 | def f_msg_defined():
30 | def f_unfixable():
31 | msg = "hello"
32 | raise RuntimeError("This is an example exception")
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ EM101
|
= help: Assign to variable; remove string literal
Unsafe fix
29 29 |
30 30 | def f_msg_defined():
31 31 | msg = "hello"
32 |- raise RuntimeError("This is an example exception")
32 |+ msg = "This is an example exception"
33 |+ raise RuntimeError(msg)
33 34 |
34 35 |
35 36 | def f_msg_in_nested_scope():
EM.py:39:24: EM101 [*] Exception must not use a string literal, assign to variable first
|
37 | msg = "hello"
@@ -137,7 +126,7 @@ EM.py:39:24: EM101 [*] Exception must not use a string literal, assign to variab
41 42 |
42 43 | def f_msg_in_parent_scope():
EM.py:46:28: EM101 [*] Exception must not use a string literal, assign to variable first
EM.py:46:28: EM101 Exception must not use a string literal, assign to variable first
|
45 | def nested():
46 | raise RuntimeError("This is an example exception")
@@ -145,17 +134,6 @@ EM.py:46:28: EM101 [*] Exception must not use a string literal, assign to variab
|
= help: Assign to variable; remove string literal
Unsafe fix
43 43 | msg = "hello"
44 44 |
45 45 | def nested():
46 |- raise RuntimeError("This is an example exception")
46 |+ msg = "This is an example exception"
47 |+ raise RuntimeError(msg)
47 48 |
48 49 |
49 50 | def f_fix_indentation_check(foo):
EM.py:51:28: EM101 [*] Exception must not use a string literal, assign to variable first
|
49 | def f_fix_indentation_check(foo):

View File

@@ -1,7 +1,8 @@
use ruff_python_ast::{self as ast, Expr};
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers::is_docstring_stmt;
use ruff_python_ast::{self as ast, StringLike};
use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
@@ -43,27 +44,25 @@ impl AlwaysFixableViolation for StringOrBytesTooLong {
}
/// PYI053
pub(crate) fn string_or_bytes_too_long(checker: &mut Checker, string: StringLike) {
pub(crate) fn string_or_bytes_too_long(checker: &mut Checker, expr: &Expr) {
// Ignore docstrings.
if is_docstring_stmt(checker.semantic().current_statement()) {
return;
}
let length = match string {
StringLike::StringLiteral(ast::ExprStringLiteral { value, .. }) => value.chars().count(),
StringLike::BytesLiteral(ast::ExprBytesLiteral { value, .. }) => value.len(),
StringLike::FStringLiteral(ast::FStringLiteralElement { value, .. }) => {
value.chars().count()
}
let length = match expr {
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => value.chars().count(),
Expr::BytesLiteral(ast::ExprBytesLiteral { value, .. }) => value.len(),
_ => return,
};
if length <= 50 {
return;
}
let mut diagnostic = Diagnostic::new(StringOrBytesTooLong, string.range());
let mut diagnostic = Diagnostic::new(StringOrBytesTooLong, expr.range());
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
"...".to_string(),
string.range(),
expr.range(),
)));
checker.diagnostics.push(diagnostic);
}

View File

@@ -90,7 +90,7 @@ PYI053.pyi:30:14: PYI053 [*] String and bytes literals longer than 50 characters
30 | qux: bytes = b"51 character byte stringggggggggggggggggggggggggggg\xff" # Error: PYI053
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI053
31 |
32 | ffoo: str = f"50 character stringggggggggggggggggggggggggggggggg" # OK
32 | class Demo:
|
= help: Replace with `...`
@@ -101,28 +101,7 @@ PYI053.pyi:30:14: PYI053 [*] String and bytes literals longer than 50 characters
30 |-qux: bytes = b"51 character byte stringggggggggggggggggggggggggggg\xff" # Error: PYI053
30 |+qux: bytes = ... # Error: PYI053
31 31 |
32 32 | ffoo: str = f"50 character stringggggggggggggggggggggggggggggggg" # OK
33 33 |
PYI053.pyi:34:15: PYI053 [*] String and bytes literals longer than 50 characters are not permitted
|
32 | ffoo: str = f"50 character stringggggggggggggggggggggggggggggggg" # OK
33 |
34 | fbar: str = f"51 character stringgggggggggggggggggggggggggggggggg" # Error: PYI053
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI053
35 |
36 | class Demo:
|
= help: Replace with `...`
Safe fix
31 31 |
32 32 | ffoo: str = f"50 character stringggggggggggggggggggggggggggggggg" # OK
33 33 |
34 |-fbar: str = f"51 character stringgggggggggggggggggggggggggggggggg" # Error: PYI053
34 |+fbar: str = f"..." # Error: PYI053
35 35 |
36 36 | class Demo:
37 37 | """Docstrings are excluded from this rule. Some padding.""" # OK
32 32 | class Demo:
33 33 | """Docstrings are excluded from this rule. Some padding.""" # OK

View File

@@ -33,7 +33,7 @@ use super::unittest_assert::UnittestAssert;
/// Checks for assertions that combine multiple independent conditions.
///
/// ## Why is this bad?
/// Composite assertion statements are harder to debug upon failure, as the
/// Composite assertion statements are harder debug upon failure, as the
/// failure message will not indicate which condition failed.
///
/// ## Example

View File

@@ -58,25 +58,15 @@ pub(super) fn is_empty_or_null_string(expr: &Expr) -> bool {
Expr::FString(ast::ExprFString { value, .. }) => {
value.parts().all(|f_string_part| match f_string_part {
ast::FStringPart::Literal(literal) => literal.is_empty(),
ast::FStringPart::FString(f_string) => f_string
.elements
.iter()
.all(is_empty_or_null_fstring_element),
ast::FStringPart::FString(f_string) => {
f_string.values.iter().all(is_empty_or_null_string)
}
})
}
_ => false,
}
}
fn is_empty_or_null_fstring_element(element: &ast::FStringElement) -> bool {
match element {
ast::FStringElement::Literal(ast::FStringLiteralElement { value, .. }) => value.is_empty(),
ast::FStringElement::Expression(ast::FStringExpressionElement { expression, .. }) => {
is_empty_or_null_string(expression)
}
}
}
pub(super) fn split_names(names: &str) -> Vec<&str> {
// Match the following pytest code:
// [x.strip() for x in argnames.split(",") if x.strip()]

View File

@@ -1,23 +1,25 @@
use ruff_python_ast::{self as ast, Arguments, ConversionFlag, Expr};
use ruff_text_size::TextRange;
/// Wrap an expression in a [`ast::FStringElement::Expression`] with no special formatting.
fn to_f_string_expression_element(inner: &Expr) -> ast::FStringElement {
ast::FStringElement::Expression(ast::FStringExpressionElement {
expression: Box::new(inner.clone()),
/// Wrap an expression in a `FormattedValue` with no special formatting.
fn to_formatted_value_expr(inner: &Expr) -> Expr {
let node = ast::ExprFormattedValue {
value: Box::new(inner.clone()),
debug_text: None,
conversion: ConversionFlag::None,
format_spec: None,
range: TextRange::default(),
})
};
node.into()
}
/// Convert a string to a [`ast::FStringElement::Literal`].
pub(super) fn to_f_string_literal_element(s: &str) -> ast::FStringElement {
ast::FStringElement::Literal(ast::FStringLiteralElement {
value: s.to_owned(),
range: TextRange::default(),
})
/// Convert a string to a constant string expression.
pub(super) fn to_constant_string(s: &str) -> Expr {
let node = ast::StringLiteral {
value: s.to_string(),
..ast::StringLiteral::default()
};
node.into()
}
/// Figure out if `expr` represents a "simple" call
@@ -49,19 +51,15 @@ fn is_simple_callee(func: &Expr) -> bool {
}
/// Convert an expression to a f-string element (if it looks like a good idea).
pub(super) fn to_f_string_element(expr: &Expr) -> Option<ast::FStringElement> {
pub(super) fn to_f_string_element(expr: &Expr) -> Option<Expr> {
match expr {
Expr::StringLiteral(ast::ExprStringLiteral { value, range }) => {
Some(ast::FStringElement::Literal(ast::FStringLiteralElement {
value: value.to_string(),
range: *range,
}))
}
// These are directly handled by `unparse_f_string_element`:
Expr::StringLiteral(_) | Expr::FString(_) | Expr::FormattedValue(_) => Some(expr.clone()),
// These should be pretty safe to wrap in a formatted value.
Expr::NumberLiteral(_) | Expr::BooleanLiteral(_) | Expr::Name(_) | Expr::Attribute(_) => {
Some(to_f_string_expression_element(expr))
Some(to_formatted_value_expr(expr))
}
Expr::Call(_) if is_simple_call(expr) => Some(to_f_string_expression_element(expr)),
Expr::Call(_) if is_simple_call(expr) => Some(to_formatted_value_expr(expr)),
_ => None,
}
}

View File

@@ -78,7 +78,7 @@ fn build_fstring(joiner: &str, joinees: &[Expr]) -> Option<Expr> {
return Some(node.into());
}
let mut f_string_elements = Vec::with_capacity(joinees.len() * 2);
let mut fstring_elems = Vec::with_capacity(joinees.len() * 2);
let mut first = true;
for expr in joinees {
@@ -88,13 +88,13 @@ fn build_fstring(joiner: &str, joinees: &[Expr]) -> Option<Expr> {
return None;
}
if !std::mem::take(&mut first) {
f_string_elements.push(helpers::to_f_string_literal_element(joiner));
fstring_elems.push(helpers::to_constant_string(joiner));
}
f_string_elements.push(helpers::to_f_string_element(expr)?);
fstring_elems.push(helpers::to_f_string_element(expr)?);
}
let node = ast::FString {
elements: f_string_elements,
values: fstring_elems,
range: TextRange::default(),
};
Some(node.into())
@@ -127,7 +127,7 @@ pub(crate) fn static_join_to_fstring(checker: &mut Checker, expr: &Expr, joiner:
};
// Try to build the fstring (internally checks whether e.g. the elements are
// convertible to f-string elements).
// convertible to f-string parts).
let Some(new_expr) = build_fstring(joiner, joinees) else {
return;
};

View File

@@ -200,7 +200,6 @@ fn format_import_block(
// Add a blank lines between direct and from imports.
if settings.from_first
&& lines_between_types > 0
&& !settings.force_sort_within_sections
&& line_insertion == Some(LineInsertion::Necessary)
{
for _ in 0..lines_between_types {
@@ -226,7 +225,6 @@ fn format_import_block(
// Add a blank lines between direct and from imports.
if !settings.from_first
&& lines_between_types > 0
&& !settings.force_sort_within_sections
&& line_insertion == Some(LineInsertion::Necessary)
{
for _ in 0..lines_between_types {
@@ -724,26 +722,6 @@ mod tests {
Ok(())
}
#[test_case(Path::new("force_sort_within_sections_lines_between.py"))]
fn force_sort_within_sections_lines_between(path: &Path) -> Result<()> {
let snapshot = format!("force_sort_within_sections_{}", path.to_string_lossy());
let mut diagnostics = test_path(
Path::new("isort").join(path).as_path(),
&LinterSettings {
isort: super::settings::Settings {
force_sort_within_sections: true,
lines_between_types: 2,
..super::settings::Settings::default()
},
src: vec![test_resource_path("fixtures/isort")],
..LinterSettings::for_rule(Rule::UnsortedImports)
},
)?;
diagnostics.sort_by_key(Ranged::start);
assert_messages!(snapshot, diagnostics);
Ok(())
}
#[test_case(Path::new("comment.py"))]
#[test_case(Path::new("comments_and_newlines.py"))]
#[test_case(Path::new("docstring.py"))]

View File

@@ -112,11 +112,7 @@ pub(super) fn is_django_model_import(name: &str, stmt: &Stmt, semantic: &Semanti
arguments.find_argument("model_name", arguments.args.len().saturating_sub(1))
{
if let Some(string_literal) = argument.as_string_literal_expr() {
if string_literal.value.to_str() == name {
return true;
}
} else {
return true;
return string_literal.value.to_str() == name;
}
}
}
@@ -131,9 +127,7 @@ pub(super) fn is_django_model_import(name: &str, stmt: &Stmt, semantic: &Semanti
if let Some(argument) = arguments.find_argument("dotted_path", 0) {
if let Some(string_literal) = argument.as_string_literal_expr() {
if let Some((.., model)) = string_literal.value.to_str().rsplit_once('.') {
if model == name {
return true;
}
return model == name;
}
}
}

View File

@@ -52,15 +52,6 @@ N806.py:57:5: N806 Variable `Bad` in function should be lowercase
56 | Bad = apps.get_model() # N806
57 | Bad = apps.get_model(model_name="Stream") # N806
| ^^^ N806
58 |
59 | Address: Type = apps.get_model("zerver", variable) # OK
|
N806.py:60:5: N806 Variable `ValidationError` in function should be lowercase
|
59 | Address: Type = apps.get_model("zerver", variable) # OK
60 | ValidationError = import_string(variable) # N806
| ^^^^^^^^^^^^^^^ N806
|

View File

@@ -65,7 +65,6 @@ mod tests {
}
#[test_case(Rule::IsLiteral, Path::new("constant_literals.py"))]
#[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E402.py"))]
#[test_case(Rule::TypeComparison, Path::new("E721.py"))]
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!(

View File

@@ -41,10 +41,6 @@ impl Violation for MultipleImportsOnOneLine {
/// According to [PEP 8], "imports are always put at the top of the file, just after any
/// module comments and docstrings, and before module globals and constants."
///
/// In [preview], this rule makes an exception for `sys.path` modifications,
/// allowing for `sys.path.insert`, `sys.path.append`, and similar
/// modifications between import statements.
///
/// ## Example
/// ```python
/// "One string"

View File

@@ -381,16 +381,20 @@ impl Whitespace {
}
}
if len == content.text_len() {
// All whitespace up to the start of the line -> Indent
(Self::None, TextSize::default())
} else if has_tabs {
if has_tabs {
(Self::Tab, len)
} else {
match count {
0 => (Self::None, TextSize::default()),
1 => (Self::Single, len),
_ => (Self::Many, len),
_ => {
if len == content.text_len() {
// All whitespace up to the start of the line -> Indent
(Self::None, TextSize::default())
} else {
(Self::Many, len)
}
}
}
}
}

View File

@@ -1,4 +1,4 @@
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_parser::TokenKind;
use ruff_text_size::{Ranged, TextRange, TextSize};
@@ -34,15 +34,11 @@ use crate::rules::pycodestyle::rules::logical_lines::{LogicalLine, LogicalLineTo
#[violation]
pub struct UnexpectedSpacesAroundKeywordParameterEquals;
impl AlwaysFixableViolation for UnexpectedSpacesAroundKeywordParameterEquals {
impl Violation for UnexpectedSpacesAroundKeywordParameterEquals {
#[derive_message_formats]
fn message(&self) -> String {
format!("Unexpected spaces around keyword / parameter equals")
}
fn fix_title(&self) -> String {
format!("Remove whitespace")
}
}
/// ## What it does
@@ -169,31 +165,22 @@ pub(crate) fn whitespace_around_named_parameter_equals(
}
}
} else {
// If there's space between the preceding token and the equals sign, report it.
if token.start() != prev_end {
let mut diagnostic = Diagnostic::new(
context.push(
UnexpectedSpacesAroundKeywordParameterEquals,
TextRange::new(prev_end, token.start()),
);
diagnostic.set_fix(Fix::safe_edit(Edit::deletion(prev_end, token.start())));
context.push_diagnostic(diagnostic);
}
// If there's space between the equals sign and the following token, report it.
while let Some(next) = iter.peek() {
if next.kind() == TokenKind::NonLogicalNewline {
iter.next();
} else {
if next.start() != token.end() {
let mut diagnostic = Diagnostic::new(
context.push(
UnexpectedSpacesAroundKeywordParameterEquals,
TextRange::new(token.end(), next.start()),
);
diagnostic.set_fix(Fix::safe_edit(Edit::deletion(
token.end(),
next.start(),
)));
context.push_diagnostic(diagnostic);
}
break;
}

View File

@@ -1,4 +1,4 @@
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_parser::TokenKind;
use ruff_python_trivia::PythonWhitespace;
@@ -66,15 +66,11 @@ impl AlwaysFixableViolation for TooFewSpacesBeforeInlineComment {
#[violation]
pub struct NoSpaceAfterInlineComment;
impl AlwaysFixableViolation for NoSpaceAfterInlineComment {
impl Violation for NoSpaceAfterInlineComment {
#[derive_message_formats]
fn message(&self) -> String {
format!("Inline comment should start with `# `")
}
fn fix_title(&self) -> String {
format!("Format space")
}
}
/// ## What it does
@@ -102,15 +98,11 @@ impl AlwaysFixableViolation for NoSpaceAfterInlineComment {
#[violation]
pub struct NoSpaceAfterBlockComment;
impl AlwaysFixableViolation for NoSpaceAfterBlockComment {
impl Violation for NoSpaceAfterBlockComment {
#[derive_message_formats]
fn message(&self) -> String {
format!("Block comment should start with `# `")
}
fn fix_title(&self) -> String {
format!("Format space")
}
}
/// ## What it does
@@ -138,15 +130,11 @@ impl AlwaysFixableViolation for NoSpaceAfterBlockComment {
#[violation]
pub struct MultipleLeadingHashesForBlockComment;
impl AlwaysFixableViolation for MultipleLeadingHashesForBlockComment {
impl Violation for MultipleLeadingHashesForBlockComment {
#[derive_message_formats]
fn message(&self) -> String {
format!("Too many leading `#` before block comment")
}
fn fix_title(&self) -> String {
format!("Remove leading `#`")
}
}
/// E261, E262, E265, E266
@@ -196,30 +184,14 @@ pub(crate) fn whitespace_before_comment(
if is_inline_comment {
if bad_prefix.is_some() || comment.chars().next().is_some_and(char::is_whitespace) {
let mut diagnostic = Diagnostic::new(NoSpaceAfterInlineComment, range);
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
format_leading_space(token_text),
range,
)));
context.push_diagnostic(diagnostic);
context.push(NoSpaceAfterInlineComment, range);
}
} else if let Some(bad_prefix) = bad_prefix {
if bad_prefix != '!' || !line.is_start_of_file() {
if bad_prefix != '#' {
let mut diagnostic = Diagnostic::new(NoSpaceAfterBlockComment, range);
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
format_leading_space(token_text),
range,
)));
context.push_diagnostic(diagnostic);
context.push(NoSpaceAfterBlockComment, range);
} else if !comment.is_empty() {
let mut diagnostic =
Diagnostic::new(MultipleLeadingHashesForBlockComment, range);
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
format_leading_hashes(token_text),
range,
)));
context.push_diagnostic(diagnostic);
context.push(MultipleLeadingHashesForBlockComment, range);
}
}
}
@@ -228,17 +200,3 @@ pub(crate) fn whitespace_before_comment(
}
}
}
/// Format a comment to have a single space after the `#`.
fn format_leading_space(comment: &str) -> String {
if let Some(rest) = comment.strip_prefix("#:") {
format!("#: {}", rest.trim_start())
} else {
format!("# {}", comment.trim_start_matches('#').trim_start())
}
}
/// Format a comment to strip multiple leading `#` characters.
fn format_leading_hashes(comment: &str) -> String {
format!("# {}", comment.trim_start_matches('#').trim_start())
}

View File

@@ -1,7 +1,7 @@
---
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
---
E25.py:2:12: E251 [*] Unexpected spaces around keyword / parameter equals
E25.py:2:12: E251 Unexpected spaces around keyword / parameter equals
|
1 | #: E251 E251
2 | def foo(bar = False):
@@ -9,17 +9,8 @@ E25.py:2:12: E251 [*] Unexpected spaces around keyword / parameter equals
3 | '''Test function with an error in declaration'''
4 | pass
|
= help: Remove whitespace
Safe fix
1 1 | #: E251 E251
2 |-def foo(bar = False):
2 |+def foo(bar= False):
3 3 | '''Test function with an error in declaration'''
4 4 | pass
5 5 | #: E251
E25.py:2:14: E251 [*] Unexpected spaces around keyword / parameter equals
E25.py:2:14: E251 Unexpected spaces around keyword / parameter equals
|
1 | #: E251 E251
2 | def foo(bar = False):
@@ -27,17 +18,8 @@ E25.py:2:14: E251 [*] Unexpected spaces around keyword / parameter equals
3 | '''Test function with an error in declaration'''
4 | pass
|
= help: Remove whitespace
Safe fix
1 1 | #: E251 E251
2 |-def foo(bar = False):
2 |+def foo(bar =False):
3 3 | '''Test function with an error in declaration'''
4 4 | pass
5 5 | #: E251
E25.py:6:9: E251 [*] Unexpected spaces around keyword / parameter equals
E25.py:6:9: E251 Unexpected spaces around keyword / parameter equals
|
4 | pass
5 | #: E251
@@ -46,19 +28,8 @@ E25.py:6:9: E251 [*] Unexpected spaces around keyword / parameter equals
7 | #: E251
8 | foo(bar =True)
|
= help: Remove whitespace
Safe fix
3 3 | '''Test function with an error in declaration'''
4 4 | pass
5 5 | #: E251
6 |-foo(bar= True)
6 |+foo(bar=True)
7 7 | #: E251
8 8 | foo(bar =True)
9 9 | #: E251 E251
E25.py:8:8: E251 [*] Unexpected spaces around keyword / parameter equals
E25.py:8:8: E251 Unexpected spaces around keyword / parameter equals
|
6 | foo(bar= True)
7 | #: E251
@@ -67,19 +38,8 @@ E25.py:8:8: E251 [*] Unexpected spaces around keyword / parameter equals
9 | #: E251 E251
10 | foo(bar = True)
|
= help: Remove whitespace
Safe fix
5 5 | #: E251
6 6 | foo(bar= True)
7 7 | #: E251
8 |-foo(bar =True)
8 |+foo(bar=True)
9 9 | #: E251 E251
10 10 | foo(bar = True)
11 11 | #: E251
E25.py:10:8: E251 [*] Unexpected spaces around keyword / parameter equals
E25.py:10:8: E251 Unexpected spaces around keyword / parameter equals
|
8 | foo(bar =True)
9 | #: E251 E251
@@ -88,19 +48,8 @@ E25.py:10:8: E251 [*] Unexpected spaces around keyword / parameter equals
11 | #: E251
12 | y = bar(root= "sdasd")
|
= help: Remove whitespace
Safe fix
7 7 | #: E251
8 8 | foo(bar =True)
9 9 | #: E251 E251
10 |-foo(bar = True)
10 |+foo(bar= True)
11 11 | #: E251
12 12 | y = bar(root= "sdasd")
13 13 | #: E251:2:29
E25.py:10:10: E251 [*] Unexpected spaces around keyword / parameter equals
E25.py:10:10: E251 Unexpected spaces around keyword / parameter equals
|
8 | foo(bar =True)
9 | #: E251 E251
@@ -109,19 +58,8 @@ E25.py:10:10: E251 [*] Unexpected spaces around keyword / parameter equals
11 | #: E251
12 | y = bar(root= "sdasd")
|
= help: Remove whitespace
Safe fix
7 7 | #: E251
8 8 | foo(bar =True)
9 9 | #: E251 E251
10 |-foo(bar = True)
10 |+foo(bar =True)
11 11 | #: E251
12 12 | y = bar(root= "sdasd")
13 13 | #: E251:2:29
E25.py:12:14: E251 [*] Unexpected spaces around keyword / parameter equals
E25.py:12:14: E251 Unexpected spaces around keyword / parameter equals
|
10 | foo(bar = True)
11 | #: E251
@@ -130,19 +68,8 @@ E25.py:12:14: E251 [*] Unexpected spaces around keyword / parameter equals
13 | #: E251:2:29
14 | parser.add_argument('--long-option',
|
= help: Remove whitespace
Safe fix
9 9 | #: E251 E251
10 10 | foo(bar = True)
11 11 | #: E251
12 |-y = bar(root= "sdasd")
12 |+y = bar(root="sdasd")
13 13 | #: E251:2:29
14 14 | parser.add_argument('--long-option',
15 15 | default=
E25.py:15:29: E251 [*] Unexpected spaces around keyword / parameter equals
E25.py:15:29: E251 Unexpected spaces around keyword / parameter equals
|
13 | #: E251:2:29
14 | parser.add_argument('--long-option',
@@ -153,20 +80,8 @@ E25.py:15:29: E251 [*] Unexpected spaces around keyword / parameter equals
17 | #: E251:1:45
18 | parser.add_argument('--long-option', default
|
= help: Remove whitespace
Safe fix
12 12 | y = bar(root= "sdasd")
13 13 | #: E251:2:29
14 14 | parser.add_argument('--long-option',
15 |- default=
16 |- "/rather/long/filesystem/path/here/blah/blah/blah")
15 |+ default="/rather/long/filesystem/path/here/blah/blah/blah")
17 16 | #: E251:1:45
18 17 | parser.add_argument('--long-option', default
19 18 | ="/rather/long/filesystem/path/here/blah/blah/blah")
E25.py:18:45: E251 [*] Unexpected spaces around keyword / parameter equals
E25.py:18:45: E251 Unexpected spaces around keyword / parameter equals
|
16 | "/rather/long/filesystem/path/here/blah/blah/blah")
17 | #: E251:1:45
@@ -177,20 +92,8 @@ E25.py:18:45: E251 [*] Unexpected spaces around keyword / parameter equals
20 | #: E251:3:8 E251:3:10
21 | foo(True,
|
= help: Remove whitespace
Safe fix
15 15 | default=
16 16 | "/rather/long/filesystem/path/here/blah/blah/blah")
17 17 | #: E251:1:45
18 |-parser.add_argument('--long-option', default
19 |- ="/rather/long/filesystem/path/here/blah/blah/blah")
18 |+parser.add_argument('--long-option', default="/rather/long/filesystem/path/here/blah/blah/blah")
20 19 | #: E251:3:8 E251:3:10
21 20 | foo(True,
22 21 | baz=(1, 2),
E25.py:23:8: E251 [*] Unexpected spaces around keyword / parameter equals
E25.py:23:8: E251 Unexpected spaces around keyword / parameter equals
|
21 | foo(True,
22 | baz=(1, 2),
@@ -199,19 +102,8 @@ E25.py:23:8: E251 [*] Unexpected spaces around keyword / parameter equals
24 | )
25 | #: Okay
|
= help: Remove whitespace
Safe fix
20 20 | #: E251:3:8 E251:3:10
21 21 | foo(True,
22 22 | baz=(1, 2),
23 |- biz = 'foo'
23 |+ biz= 'foo'
24 24 | )
25 25 | #: Okay
26 26 | foo(bar=(1 == 1))
E25.py:23:10: E251 [*] Unexpected spaces around keyword / parameter equals
E25.py:23:10: E251 Unexpected spaces around keyword / parameter equals
|
21 | foo(True,
22 | baz=(1, 2),
@@ -220,16 +112,5 @@ E25.py:23:10: E251 [*] Unexpected spaces around keyword / parameter equals
24 | )
25 | #: Okay
|
= help: Remove whitespace
Safe fix
20 20 | #: E251:3:8 E251:3:10
21 21 | foo(True,
22 22 | baz=(1, 2),
23 |- biz = 'foo'
23 |+ biz ='foo'
24 24 | )
25 25 | #: Okay
26 26 | foo(bar=(1 == 1))

View File

@@ -1,7 +1,7 @@
---
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
---
E26.py:4:12: E262 [*] Inline comment should start with `# `
E26.py:4:12: E262 Inline comment should start with `# `
|
2 | pass # an inline comment
3 | #: E262:1:12
@@ -10,19 +10,8 @@ E26.py:4:12: E262 [*] Inline comment should start with `# `
5 | #: E262:1:12
6 | x = x + 1 # Increment x
|
= help: Format space
Safe fix
1 1 | #: E261:1:5
2 2 | pass # an inline comment
3 3 | #: E262:1:12
4 |-x = x + 1 #Increment x
4 |+x = x + 1 # Increment x
5 5 | #: E262:1:12
6 6 | x = x + 1 # Increment x
7 7 | #: E262:1:12
E26.py:6:12: E262 [*] Inline comment should start with `# `
E26.py:6:12: E262 Inline comment should start with `# `
|
4 | x = x + 1 #Increment x
5 | #: E262:1:12
@@ -31,19 +20,8 @@ E26.py:6:12: E262 [*] Inline comment should start with `# `
7 | #: E262:1:12
8 | x = y + 1 #: Increment x
|
= help: Format space
Safe fix
3 3 | #: E262:1:12
4 4 | x = x + 1 #Increment x
5 5 | #: E262:1:12
6 |-x = x + 1 # Increment x
6 |+x = x + 1 # Increment x
7 7 | #: E262:1:12
8 8 | x = y + 1 #: Increment x
9 9 | #: E265:1:1
E26.py:8:12: E262 [*] Inline comment should start with `# `
E26.py:8:12: E262 Inline comment should start with `# `
|
6 | x = x + 1 # Increment x
7 | #: E262:1:12
@@ -52,19 +30,8 @@ E26.py:8:12: E262 [*] Inline comment should start with `# `
9 | #: E265:1:1
10 | #Block comment
|
= help: Format space
Safe fix
5 5 | #: E262:1:12
6 6 | x = x + 1 # Increment x
7 7 | #: E262:1:12
8 |-x = y + 1 #: Increment x
8 |+x = y + 1 #: Increment x
9 9 | #: E265:1:1
10 10 | #Block comment
11 11 | a = 1
E26.py:63:9: E262 [*] Inline comment should start with `# `
E26.py:63:9: E262 Inline comment should start with `# `
|
61 | # -*- coding: utf8 -*-
62 | #  (One space one NBSP) Ok for block comment
@@ -73,19 +40,8 @@ E26.py:63:9: E262 [*] Inline comment should start with `# `
64 | #: E262:2:9
65 | # (Two spaces) Ok for block comment
|
= help: Format space
Safe fix
60 60 | #: E262:3:9
61 61 | # -*- coding: utf8 -*-
62 62 | #  (One space one NBSP) Ok for block comment
63 |-a = 42 #  (One space one NBSP)
63 |+a = 42 # (One space one NBSP)
64 64 | #: E262:2:9
65 65 | # (Two spaces) Ok for block comment
66 66 | a = 42 # (Two spaces)
E26.py:66:9: E262 [*] Inline comment should start with `# `
E26.py:66:9: E262 Inline comment should start with `# `
|
64 | #: E262:2:9
65 | # (Two spaces) Ok for block comment
@@ -94,52 +50,5 @@ E26.py:66:9: E262 [*] Inline comment should start with `# `
67 |
68 | #: E265:5:1
|
= help: Format space
Safe fix
63 63 | a = 42 #  (One space one NBSP)
64 64 | #: E262:2:9
65 65 | # (Two spaces) Ok for block comment
66 |-a = 42 # (Two spaces)
66 |+a = 42 # (Two spaces)
67 67 |
68 68 | #: E265:5:1
69 69 | ### Means test is not done yet
E26.py:84:8: E262 [*] Inline comment should start with `# `
|
82 | ## Foo
83 |
84 | a = 1 ## Foo
| ^^^^^^ E262
85 |
86 | a = 1 #:Foo
|
= help: Format space
Safe fix
81 81 | #: E266:1:3
82 82 | ## Foo
83 83 |
84 |-a = 1 ## Foo
84 |+a = 1 # Foo
85 85 |
86 86 | a = 1 #:Foo
E26.py:86:8: E262 [*] Inline comment should start with `# `
|
84 | a = 1 ## Foo
85 |
86 | a = 1 #:Foo
| ^^^^^ E262
|
= help: Format space
Safe fix
83 83 |
84 84 | a = 1 ## Foo
85 85 |
86 |-a = 1 #:Foo
86 |+a = 1 #: Foo

View File

@@ -1,7 +1,7 @@
---
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
---
E26.py:10:1: E265 [*] Block comment should start with `# `
E26.py:10:1: E265 Block comment should start with `# `
|
8 | x = y + 1 #: Increment x
9 | #: E265:1:1
@@ -10,19 +10,8 @@ E26.py:10:1: E265 [*] Block comment should start with `# `
11 | a = 1
12 | #: E265:2:1
|
= help: Format space
Safe fix
7 7 | #: E262:1:12
8 8 | x = y + 1 #: Increment x
9 9 | #: E265:1:1
10 |-#Block comment
10 |+# Block comment
11 11 | a = 1
12 12 | #: E265:2:1
13 13 | m = 42
E26.py:14:1: E265 [*] Block comment should start with `# `
E26.py:14:1: E265 Block comment should start with `# `
|
12 | #: E265:2:1
13 | m = 42
@@ -31,19 +20,8 @@ E26.py:14:1: E265 [*] Block comment should start with `# `
15 | mx = 42 - 42
16 | #: E266:3:5 E266:6:5
|
= help: Format space
Safe fix
11 11 | a = 1
12 12 | #: E265:2:1
13 13 | m = 42
14 |-#! This is important
14 |+# ! This is important
15 15 | mx = 42 - 42
16 16 | #: E266:3:5 E266:6:5
17 17 | def how_it_feel(r):
E26.py:25:1: E265 [*] Block comment should start with `# `
E26.py:25:1: E265 Block comment should start with `# `
|
23 | return
24 | #: E265:1:1 E266:2:1
@@ -52,19 +30,8 @@ E26.py:25:1: E265 [*] Block comment should start with `# `
26 | ## logging.error()
27 | #: W291:1:42
|
= help: Format space
Safe fix
22 22 | ### Of course it is unused
23 23 | return
24 24 | #: E265:1:1 E266:2:1
25 |-##if DEBUG:
25 |+# if DEBUG:
26 26 | ## logging.error()
27 27 | #: W291:1:42
28 28 | #########################################
E26.py:32:1: E265 [*] Block comment should start with `# `
E26.py:32:1: E265 Block comment should start with `# `
|
31 | #: Okay
32 | #!/usr/bin/env python
@@ -72,19 +39,8 @@ E26.py:32:1: E265 [*] Block comment should start with `# `
33 |
34 | pass # an inline comment
|
= help: Format space
Safe fix
29 29 | #:
30 30 |
31 31 | #: Okay
32 |-#!/usr/bin/env python
32 |+# !/usr/bin/env python
33 33 |
34 34 | pass # an inline comment
35 35 | x = x + 1 # Increment x
E26.py:73:1: E265 [*] Block comment should start with `# `
E26.py:73:1: E265 Block comment should start with `# `
|
71 | # F Means test is failing (F)
72 | # EF Means test is giving error and Failing
@@ -92,37 +48,5 @@ E26.py:73:1: E265 [*] Block comment should start with `# `
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E265
74 | # 8 Means test runs forever
|
= help: Format space
Safe fix
70 70 | # E Means test is giving error (E)
71 71 | # F Means test is failing (F)
72 72 | # EF Means test is giving error and Failing
73 |-#! Means test is segfaulting
73 |+# ! Means test is segfaulting
74 74 | # 8 Means test runs forever
75 75 |
76 76 | #: Colon prefix is okay
E26.py:78:1: E265 [*] Block comment should start with `# `
|
76 | #: Colon prefix is okay
77 |
78 | ###This is a variable ###
| ^^^^^^^^^^^^^^^^^^^^^^^^^ E265
79 |
80 | # We should strip the space, but preserve the hashes.
|
= help: Format space
Safe fix
75 75 |
76 76 | #: Colon prefix is okay
77 77 |
78 |-###This is a variable ###
78 |+# This is a variable ###
79 79 |
80 80 | # We should strip the space, but preserve the hashes.
81 81 | #: E266:1:3

View File

@@ -1,7 +1,7 @@
---
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
---
E26.py:19:5: E266 [*] Too many leading `#` before block comment
E26.py:19:5: E266 Too many leading `#` before block comment
|
17 | def how_it_feel(r):
18 |
@@ -9,19 +9,8 @@ E26.py:19:5: E266 [*] Too many leading `#` before block comment
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ E266
20 | a = 42
|
= help: Remove leading `#`
Safe fix
16 16 | #: E266:3:5 E266:6:5
17 17 | def how_it_feel(r):
18 18 |
19 |- ### This is a variable ###
19 |+ # This is a variable ###
20 20 | a = 42
21 21 |
22 22 | ### Of course it is unused
E26.py:22:5: E266 [*] Too many leading `#` before block comment
E26.py:22:5: E266 Too many leading `#` before block comment
|
20 | a = 42
21 |
@@ -30,19 +19,8 @@ E26.py:22:5: E266 [*] Too many leading `#` before block comment
23 | return
24 | #: E265:1:1 E266:2:1
|
= help: Remove leading `#`
Safe fix
19 19 | ### This is a variable ###
20 20 | a = 42
21 21 |
22 |- ### Of course it is unused
22 |+ # Of course it is unused
23 23 | return
24 24 | #: E265:1:1 E266:2:1
25 25 | ##if DEBUG:
E26.py:26:1: E266 [*] Too many leading `#` before block comment
E26.py:26:1: E266 Too many leading `#` before block comment
|
24 | #: E265:1:1 E266:2:1
25 | ##if DEBUG:
@@ -51,19 +29,8 @@ E26.py:26:1: E266 [*] Too many leading `#` before block comment
27 | #: W291:1:42
28 | #########################################
|
= help: Remove leading `#`
Safe fix
23 23 | return
24 24 | #: E265:1:1 E266:2:1
25 25 | ##if DEBUG:
26 |-## logging.error()
26 |+# logging.error()
27 27 | #: W291:1:42
28 28 | #########################################
29 29 | #:
E26.py:69:1: E266 [*] Too many leading `#` before block comment
E26.py:69:1: E266 Too many leading `#` before block comment
|
68 | #: E265:5:1
69 | ### Means test is not done yet
@@ -71,37 +38,5 @@ E26.py:69:1: E266 [*] Too many leading `#` before block comment
70 | # E Means test is giving error (E)
71 | # F Means test is failing (F)
|
= help: Remove leading `#`
Safe fix
66 66 | a = 42 # (Two spaces)
67 67 |
68 68 | #: E265:5:1
69 |-### Means test is not done yet
69 |+# Means test is not done yet
70 70 | # E Means test is giving error (E)
71 71 | # F Means test is failing (F)
72 72 | # EF Means test is giving error and Failing
E26.py:82:1: E266 [*] Too many leading `#` before block comment
|
80 | # We should strip the space, but preserve the hashes.
81 | #: E266:1:3
82 | ## Foo
| ^^^^^^^ E266
83 |
84 | a = 1 ## Foo
|
= help: Remove leading `#`
Safe fix
79 79 |
80 80 | # We should strip the space, but preserve the hashes.
81 81 | #: E266:1:3
82 |-## Foo
82 |+# Foo
83 83 |
84 84 | a = 1 ## Foo
85 85 |

View File

@@ -1,57 +1,27 @@
---
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
---
E402.py:25:1: E402 Module level import not at top of file
E402.py:24:1: E402 Module level import not at top of file
|
23 | sys.path.insert(0, "some/path")
24 |
25 | import f
| ^^^^^^^^ E402
26 |
27 | import matplotlib
|
E402.py:27:1: E402 Module level import not at top of file
|
25 | import f
26 |
27 | import matplotlib
| ^^^^^^^^^^^^^^^^^ E402
28 |
29 | matplotlib.use("Agg")
|
E402.py:31:1: E402 Module level import not at top of file
|
29 | matplotlib.use("Agg")
30 |
31 | import g
| ^^^^^^^^ E402
32 |
33 | __some__magic = 1
|
E402.py:35:1: E402 Module level import not at top of file
|
33 | __some__magic = 1
34 |
35 | import h
22 | __some__magic = 1
23 |
24 | import f
| ^^^^^^^^ E402
|
E402.py:45:1: E402 Module level import not at top of file
E402.py:34:1: E402 Module level import not at top of file
|
43 | import j
44 |
45 | import k; import l
32 | import g
33 |
34 | import h; import i
| ^^^^^^^^ E402
|
E402.py:45:11: E402 Module level import not at top of file
E402.py:34:11: E402 Module level import not at top of file
|
43 | import j
44 |
45 | import k; import l
32 | import g
33 |
34 | import h; import i
| ^^^^^^^^ E402
|

View File

@@ -1,28 +0,0 @@
---
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
---
E402.py:35:1: E402 Module level import not at top of file
|
33 | __some__magic = 1
34 |
35 | import h
| ^^^^^^^^ E402
|
E402.py:45:1: E402 Module level import not at top of file
|
43 | import j
44 |
45 | import k; import l
| ^^^^^^^^ E402
|
E402.py:45:11: E402 Module level import not at top of file
|
43 | import j
44 |
45 | import k; import l
| ^^^^^^^^ E402
|

View File

@@ -1,7 +1,7 @@
---
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
---
shebang.py:3:1: E265 [*] Block comment should start with `# `
shebang.py:3:1: E265 Block comment should start with `# `
|
1 | #!/usr/bin/python
2 | #
@@ -9,13 +9,5 @@ shebang.py:3:1: E265 [*] Block comment should start with `# `
| ^^ E265
4 | #:
|
= help: Format space
Safe fix
1 1 | #!/usr/bin/python
2 2 | #
3 |-#!
3 |+# !
4 4 | #:

View File

@@ -1,6 +1,6 @@
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast as ast;
use ruff_python_ast::{self as ast, Expr};
use ruff_source_file::Locator;
use ruff_text_size::{Ranged, TextRange, TextSize};
@@ -47,12 +47,11 @@ impl AlwaysFixableViolation for FStringMissingPlaceholders {
/// F541
pub(crate) fn f_string_missing_placeholders(checker: &mut Checker, expr: &ast::ExprFString) {
if expr.value.f_strings().any(|f_string| {
f_string
.elements
.iter()
.any(ast::FStringElement::is_expression)
}) {
if expr
.value
.f_strings()
.any(|f_string| f_string.values.iter().any(Expr::is_formatted_value_expr))
{
return;
}

View File

@@ -247,14 +247,6 @@ fn remove_unused_variable(binding: &Binding, checker: &Checker) -> Option<Fix> {
Some(Fix::unsafe_edit(edit).isolate(isolation))
};
}
} else {
let name = binding.name(checker.locator());
let renamed = format!("_{name}");
if checker.settings.dummy_variable_rgx.is_match(&renamed) {
let edit = Edit::range_replacement(renamed, binding.range());
return Some(Fix::unsafe_edit(edit).isolate(isolation));
}
}
}

View File

@@ -57,7 +57,7 @@ F841_0.py:20:5: F841 [*] Local variable `foo` is assigned to but never used
22 21 |
23 22 | bar = (1, 2)
F841_0.py:21:6: F841 [*] Local variable `a` is assigned to but never used
F841_0.py:21:6: F841 Local variable `a` is assigned to but never used
|
19 | def f():
20 | foo = (1, 2)
@@ -68,17 +68,7 @@ F841_0.py:21:6: F841 [*] Local variable `a` is assigned to but never used
|
= help: Remove assignment to unused variable `a`
Unsafe fix
18 18 |
19 19 | def f():
20 20 | foo = (1, 2)
21 |- (a, b) = (1, 2)
21 |+ (_a, b) = (1, 2)
22 22 |
23 23 | bar = (1, 2)
24 24 | (c, d) = bar
F841_0.py:21:9: F841 [*] Local variable `b` is assigned to but never used
F841_0.py:21:9: F841 Local variable `b` is assigned to but never used
|
19 | def f():
20 | foo = (1, 2)
@@ -89,16 +79,6 @@ F841_0.py:21:9: F841 [*] Local variable `b` is assigned to but never used
|
= help: Remove assignment to unused variable `b`
Unsafe fix
18 18 |
19 19 | def f():
20 20 | foo = (1, 2)
21 |- (a, b) = (1, 2)
21 |+ (a, _b) = (1, 2)
22 22 |
23 23 | bar = (1, 2)
24 24 | (c, d) = bar
F841_0.py:26:14: F841 [*] Local variable `baz` is assigned to but never used
|
24 | (c, d) = bar

View File

@@ -1,7 +1,7 @@
---
source: crates/ruff_linter/src/rules/pyflakes/mod.rs
---
F841_1.py:6:5: F841 [*] Local variable `x` is assigned to but never used
F841_1.py:6:5: F841 Local variable `x` is assigned to but never used
|
5 | def f():
6 | x, y = 1, 2 # this triggers F841 as it's just a simple assignment where unpacking isn't needed
@@ -9,17 +9,7 @@ F841_1.py:6:5: F841 [*] Local variable `x` is assigned to but never used
|
= help: Remove assignment to unused variable `x`
Unsafe fix
3 3 |
4 4 |
5 5 | def f():
6 |- x, y = 1, 2 # this triggers F841 as it's just a simple assignment where unpacking isn't needed
6 |+ _x, y = 1, 2 # this triggers F841 as it's just a simple assignment where unpacking isn't needed
7 7 |
8 8 |
9 9 | def f():
F841_1.py:6:8: F841 [*] Local variable `y` is assigned to but never used
F841_1.py:6:8: F841 Local variable `y` is assigned to but never used
|
5 | def f():
6 | x, y = 1, 2 # this triggers F841 as it's just a simple assignment where unpacking isn't needed
@@ -27,16 +17,6 @@ F841_1.py:6:8: F841 [*] Local variable `y` is assigned to but never used
|
= help: Remove assignment to unused variable `y`
Unsafe fix
3 3 |
4 4 |
5 5 | def f():
6 |- x, y = 1, 2 # this triggers F841 as it's just a simple assignment where unpacking isn't needed
6 |+ x, _y = 1, 2 # this triggers F841 as it's just a simple assignment where unpacking isn't needed
7 7 |
8 8 |
9 9 | def f():
F841_1.py:16:14: F841 [*] Local variable `coords` is assigned to but never used
|
15 | def f():
@@ -73,7 +53,7 @@ F841_1.py:20:5: F841 [*] Local variable `coords` is assigned to but never used
22 22 |
23 23 | def f():
F841_1.py:24:6: F841 [*] Local variable `a` is assigned to but never used
F841_1.py:24:6: F841 Local variable `a` is assigned to but never used
|
23 | def f():
24 | (a, b) = (x, y) = 1, 2 # this triggers F841 on everything
@@ -81,14 +61,7 @@ F841_1.py:24:6: F841 [*] Local variable `a` is assigned to but never used
|
= help: Remove assignment to unused variable `a`
Unsafe fix
21 21 |
22 22 |
23 23 | def f():
24 |- (a, b) = (x, y) = 1, 2 # this triggers F841 on everything
24 |+ (_a, b) = (x, y) = 1, 2 # this triggers F841 on everything
F841_1.py:24:9: F841 [*] Local variable `b` is assigned to but never used
F841_1.py:24:9: F841 Local variable `b` is assigned to but never used
|
23 | def f():
24 | (a, b) = (x, y) = 1, 2 # this triggers F841 on everything
@@ -96,14 +69,7 @@ F841_1.py:24:9: F841 [*] Local variable `b` is assigned to but never used
|
= help: Remove assignment to unused variable `b`
Unsafe fix
21 21 |
22 22 |
23 23 | def f():
24 |- (a, b) = (x, y) = 1, 2 # this triggers F841 on everything
24 |+ (a, _b) = (x, y) = 1, 2 # this triggers F841 on everything
F841_1.py:24:15: F841 [*] Local variable `x` is assigned to but never used
F841_1.py:24:15: F841 Local variable `x` is assigned to but never used
|
23 | def f():
24 | (a, b) = (x, y) = 1, 2 # this triggers F841 on everything
@@ -111,14 +77,7 @@ F841_1.py:24:15: F841 [*] Local variable `x` is assigned to but never used
|
= help: Remove assignment to unused variable `x`
Unsafe fix
21 21 |
22 22 |
23 23 | def f():
24 |- (a, b) = (x, y) = 1, 2 # this triggers F841 on everything
24 |+ (a, b) = (_x, y) = 1, 2 # this triggers F841 on everything
F841_1.py:24:18: F841 [*] Local variable `y` is assigned to but never used
F841_1.py:24:18: F841 Local variable `y` is assigned to but never used
|
23 | def f():
24 | (a, b) = (x, y) = 1, 2 # this triggers F841 on everything
@@ -126,11 +85,4 @@ F841_1.py:24:18: F841 [*] Local variable `y` is assigned to but never used
|
= help: Remove assignment to unused variable `y`
Unsafe fix
21 21 |
22 22 |
23 23 | def f():
24 |- (a, b) = (x, y) = 1, 2 # this triggers F841 on everything
24 |+ (a, b) = (x, _y) = 1, 2 # this triggers F841 on everything

View File

@@ -156,7 +156,7 @@ F841_3.py:27:46: F841 [*] Local variable `z3` is assigned to but never used
29 29 |
30 30 |
F841_3.py:32:6: F841 [*] Local variable `x1` is assigned to but never used
F841_3.py:32:6: F841 Local variable `x1` is assigned to but never used
|
31 | def f():
32 | (x1, y1) = (1, 2)
@@ -166,17 +166,7 @@ F841_3.py:32:6: F841 [*] Local variable `x1` is assigned to but never used
|
= help: Remove assignment to unused variable `x1`
Unsafe fix
29 29 |
30 30 |
31 31 | def f():
32 |- (x1, y1) = (1, 2)
32 |+ (_x1, y1) = (1, 2)
33 33 | (x2, y2) = coords2 = (1, 2)
34 34 | coords3 = (x3, y3) = (1, 2)
35 35 |
F841_3.py:32:10: F841 [*] Local variable `y1` is assigned to but never used
F841_3.py:32:10: F841 Local variable `y1` is assigned to but never used
|
31 | def f():
32 | (x1, y1) = (1, 2)
@@ -186,16 +176,6 @@ F841_3.py:32:10: F841 [*] Local variable `y1` is assigned to but never used
|
= help: Remove assignment to unused variable `y1`
Unsafe fix
29 29 |
30 30 |
31 31 | def f():
32 |- (x1, y1) = (1, 2)
32 |+ (x1, _y1) = (1, 2)
33 33 | (x2, y2) = coords2 = (1, 2)
34 34 | coords3 = (x3, y3) = (1, 2)
35 35 |
F841_3.py:33:16: F841 [*] Local variable `coords2` is assigned to but never used
|
31 | def f():

View File

@@ -20,7 +20,7 @@ F841_4.py:12:5: F841 [*] Local variable `a` is assigned to but never used
14 14 |
15 15 |
F841_4.py:13:5: F841 [*] Local variable `b` is assigned to but never used
F841_4.py:13:5: F841 Local variable `b` is assigned to but never used
|
11 | def bar():
12 | a = foo()
@@ -29,17 +29,7 @@ F841_4.py:13:5: F841 [*] Local variable `b` is assigned to but never used
|
= help: Remove assignment to unused variable `b`
Unsafe fix
10 10 |
11 11 | def bar():
12 12 | a = foo()
13 |- b, c = foo()
13 |+ _b, c = foo()
14 14 |
15 15 |
16 16 | def baz():
F841_4.py:13:8: F841 [*] Local variable `c` is assigned to but never used
F841_4.py:13:8: F841 Local variable `c` is assigned to but never used
|
11 | def bar():
12 | a = foo()
@@ -48,14 +38,4 @@ F841_4.py:13:8: F841 [*] Local variable `c` is assigned to but never used
|
= help: Remove assignment to unused variable `c`
Unsafe fix
10 10 |
11 11 | def bar():
12 12 | a = foo()
13 |- b, c = foo()
13 |+ b, _c = foo()
14 14 |
15 15 |
16 16 | def baz():

View File

@@ -72,26 +72,24 @@ pub(crate) fn assert_on_string_literal(checker: &mut Checker, test: &Expr) {
Expr::FString(ast::ExprFString { value, .. }) => {
let kind = if value.parts().all(|f_string_part| match f_string_part {
ast::FStringPart::Literal(literal) => literal.is_empty(),
ast::FStringPart::FString(f_string) => {
f_string.elements.iter().all(|element| match element {
ast::FStringElement::Literal(ast::FStringLiteralElement {
value, ..
}) => value.is_empty(),
ast::FStringElement::Expression(_) => false,
})
}
ast::FStringPart::FString(f_string) => f_string.values.iter().all(|value| {
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = value {
value.is_empty()
} else {
false
}
}),
}) {
Kind::Empty
} else if value.parts().any(|f_string_part| match f_string_part {
ast::FStringPart::Literal(literal) => !literal.is_empty(),
ast::FStringPart::FString(f_string) => {
f_string.elements.iter().any(|element| match element {
ast::FStringElement::Literal(ast::FStringLiteralElement {
value, ..
}) => !value.is_empty(),
ast::FStringElement::Expression(_) => false,
})
}
ast::FStringPart::FString(f_string) => f_string.values.iter().any(|value| {
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = value {
!value.is_empty()
} else {
false
}
}),
}) {
Kind::NonEmpty
} else {

View File

@@ -1,10 +1,9 @@
use ruff_diagnostics::{AlwaysFixableViolation, Applicability, Diagnostic, Fix};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast as ast;
use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
use crate::fix::edits::add_argument;
/// ## What it does
/// Checks for uses of `subprocess.run` without an explicit `check` argument.
@@ -37,25 +36,16 @@ use crate::fix::edits::add_argument;
/// subprocess.run(["ls", "nonexistent"], check=False) # Explicitly no check.
/// ```
///
/// ## Fix safety
/// This rule's fix is marked as unsafe for function calls that contain
/// `**kwargs`, as adding a `check` keyword argument to such a call may lead
/// to a duplicate keyword argument error.
///
/// ## References
/// - [Python documentation: `subprocess.run`](https://docs.python.org/3/library/subprocess.html#subprocess.run)
#[violation]
pub struct SubprocessRunWithoutCheck;
impl AlwaysFixableViolation for SubprocessRunWithoutCheck {
impl Violation for SubprocessRunWithoutCheck {
#[derive_message_formats]
fn message(&self) -> String {
format!("`subprocess.run` without explicit `check` argument")
}
fn fix_title(&self) -> String {
"Add explicit `check=False`".to_string()
}
}
/// PLW1510
@@ -66,27 +56,10 @@ pub(crate) fn subprocess_run_without_check(checker: &mut Checker, call: &ast::Ex
.is_some_and(|call_path| matches!(call_path.as_slice(), ["subprocess", "run"]))
{
if call.arguments.find_keyword("check").is_none() {
let mut diagnostic = Diagnostic::new(SubprocessRunWithoutCheck, call.func.range());
diagnostic.set_fix(Fix::applicable_edit(
add_argument(
"check=False",
&call.arguments,
checker.indexer().comment_ranges(),
checker.locator().contents(),
),
// If the function call contains `**kwargs`, mark the fix as unsafe.
if call
.arguments
.keywords
.iter()
.any(|keyword| keyword.arg.is_none())
{
Applicability::Unsafe
} else {
Applicability::Safe
},
checker.diagnostics.push(Diagnostic::new(
SubprocessRunWithoutCheck,
call.func.range(),
));
checker.diagnostics.push(diagnostic);
}
}
}

View File

@@ -1,87 +1,22 @@
---
source: crates/ruff_linter/src/rules/pylint/mod.rs
---
subprocess_run_without_check.py:4:1: PLW1510 [*] `subprocess.run` without explicit `check` argument
subprocess_run_without_check.py:4:1: PLW1510 `subprocess.run` without explicit `check` argument
|
3 | # Errors.
4 | subprocess.run("ls")
| ^^^^^^^^^^^^^^ PLW1510
5 | subprocess.run("ls", shell=True)
6 | subprocess.run(
|
= help: Add explicit `check=False`
Safe fix
1 1 | import subprocess
2 2 |
3 3 | # Errors.
4 |-subprocess.run("ls")
4 |+subprocess.run("ls", check=False)
5 5 | subprocess.run("ls", shell=True)
6 6 | subprocess.run(
7 7 | ["ls"],
subprocess_run_without_check.py:5:1: PLW1510 [*] `subprocess.run` without explicit `check` argument
subprocess_run_without_check.py:5:1: PLW1510 `subprocess.run` without explicit `check` argument
|
3 | # Errors.
4 | subprocess.run("ls")
5 | subprocess.run("ls", shell=True)
| ^^^^^^^^^^^^^^ PLW1510
6 | subprocess.run(
7 | ["ls"],
6 |
7 | # Non-errors.
|
= help: Add explicit `check=False`
Safe fix
2 2 |
3 3 | # Errors.
4 4 | subprocess.run("ls")
5 |-subprocess.run("ls", shell=True)
5 |+subprocess.run("ls", shell=True, check=False)
6 6 | subprocess.run(
7 7 | ["ls"],
8 8 | shell=False,
subprocess_run_without_check.py:6:1: PLW1510 [*] `subprocess.run` without explicit `check` argument
|
4 | subprocess.run("ls")
5 | subprocess.run("ls", shell=True)
6 | subprocess.run(
| ^^^^^^^^^^^^^^ PLW1510
7 | ["ls"],
8 | shell=False,
|
= help: Add explicit `check=False`
Safe fix
5 5 | subprocess.run("ls", shell=True)
6 6 | subprocess.run(
7 7 | ["ls"],
8 |- shell=False,
8 |+ shell=False, check=False,
9 9 | )
10 10 | subprocess.run(["ls"], **kwargs)
11 11 |
subprocess_run_without_check.py:10:1: PLW1510 [*] `subprocess.run` without explicit `check` argument
|
8 | shell=False,
9 | )
10 | subprocess.run(["ls"], **kwargs)
| ^^^^^^^^^^^^^^ PLW1510
11 |
12 | # Non-errors.
|
= help: Add explicit `check=False`
Unsafe fix
7 7 | ["ls"],
8 8 | shell=False,
9 9 | )
10 |-subprocess.run(["ls"], **kwargs)
10 |+subprocess.run(["ls"], **kwargs, check=False)
11 11 |
12 12 | # Non-errors.
13 13 | subprocess.run("ls", check=True)

View File

@@ -180,6 +180,7 @@ fn is_allowed_value(expr: &Expr) -> bool {
| Expr::GeneratorExp(_)
| Expr::Compare(_)
| Expr::Call(_)
| Expr::FormattedValue(_)
| Expr::FString(_)
| Expr::StringLiteral(_)
| Expr::BytesLiteral(_)

View File

@@ -29,7 +29,6 @@ mod tests {
#[test_case(Rule::IsinstanceTypeNone, Path::new("FURB168.py"))]
#[test_case(Rule::TypeNoneComparison, Path::new("FURB169.py"))]
#[test_case(Rule::RedundantLogBase, Path::new("FURB163.py"))]
#[test_case(Rule::HashlibDigestHex, Path::new("FURB181.py"))]
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
let diagnostics = test_path(

View File

@@ -14,7 +14,7 @@ use crate::rules::refurb::helpers::generate_method_call;
/// dictionary.
///
/// ## Why is this bad?
/// It is faster and more succinct to remove all items via the `clear()`
/// It's is faster and more succinct to remove all items via the `clear()`
/// method.
///
/// ## Known problems

View File

@@ -1,120 +0,0 @@
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{Expr, ExprAttribute, ExprCall};
use ruff_text_size::{Ranged, TextRange};
use crate::checkers::ast::Checker;
/// ## What it does
/// Checks for the use of `.digest().hex()` on a hashlib hash, like `sha512`.
///
/// ## Why is this bad?
/// When generating a hex digest from a hash, it's preferable to use the
/// `.hexdigest()` method, rather than calling `.digest()` and then `.hex()`,
/// as the former is more concise and readable.
///
/// ## Example
/// ```python
/// from hashlib import sha512
///
/// hashed = sha512(b"some data").digest().hex()
/// ```
///
/// Use instead:
/// ```python
/// from hashlib import sha512
///
/// hashed = sha512(b"some data").hexdigest()
/// ```
///
/// ## Fix safety
/// This rule's fix is marked as unsafe, as the target of the `.digest()` call
/// could be a user-defined class that implements a `.hex()` method, rather
/// than a hashlib hash object.
///
/// ## References
/// - [Python documentation: `hashlib`](https://docs.python.org/3/library/hashlib.html)
#[violation]
pub struct HashlibDigestHex;
impl Violation for HashlibDigestHex {
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
#[derive_message_formats]
fn message(&self) -> String {
format!("Use of hashlib's `.digest().hex()`")
}
fn fix_title(&self) -> Option<String> {
Some("Replace with `.hexdigest()`".to_string())
}
}
/// FURB181
pub(crate) fn hashlib_digest_hex(checker: &mut Checker, call: &ExprCall) {
if !call.arguments.is_empty() {
return;
}
let Expr::Attribute(ExprAttribute { attr, value, .. }) = call.func.as_ref() else {
return;
};
if attr.as_str() != "hex" {
return;
}
let Expr::Call(ExprCall {
func, arguments, ..
}) = value.as_ref()
else {
return;
};
let Expr::Attribute(ExprAttribute { attr, value, .. }) = func.as_ref() else {
return;
};
if attr.as_str() != "digest" {
return;
}
let Expr::Call(ExprCall { func, .. }) = value.as_ref() else {
return;
};
if checker.semantic().resolve_call_path(func).is_some_and(
|call_path: smallvec::SmallVec<[&str; 8]>| {
matches!(
call_path.as_slice(),
[
"hashlib",
"md5"
| "sha1"
| "sha224"
| "sha256"
| "sha384"
| "sha512"
| "blake2b"
| "blake2s"
| "sha3_224"
| "sha3_256"
| "sha3_384"
| "sha3_512"
| "shake_128"
| "shake_256"
| "_Hash"
]
)
},
) {
let mut diagnostic = Diagnostic::new(HashlibDigestHex, call.range());
if arguments.is_empty() {
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
".hexdigest".to_string(),
TextRange::new(value.end(), call.func.end()),
)));
}
checker.diagnostics.push(diagnostic);
}
}

View File

@@ -53,17 +53,23 @@ pub(crate) fn math_constant(checker: &mut Checker, literal: &ast::ExprNumberLite
let Number::Float(value) = literal.value else {
return;
};
if let Some(constant) = Constant::from_value(value) {
let mut diagnostic = Diagnostic::new(
MathConstant {
literal: checker.locator().slice(literal).into(),
constant: constant.name(),
},
literal.range(),
);
diagnostic.try_set_fix(|| convert_to_constant(literal, constant.name(), checker));
checker.diagnostics.push(diagnostic);
for (real_value, constant) in [
(std::f64::consts::PI, "pi"),
(std::f64::consts::E, "e"),
(std::f64::consts::TAU, "tau"),
] {
if (value - real_value).abs() < 1e-2 {
let mut diagnostic = Diagnostic::new(
MathConstant {
literal: checker.locator().slice(literal).into(),
constant,
},
literal.range(),
);
diagnostic.try_set_fix(|| convert_to_constant(literal, constant, checker));
checker.diagnostics.push(diagnostic);
return;
}
}
}
@@ -82,33 +88,3 @@ fn convert_to_constant(
[edit],
))
}
#[derive(Debug, Clone, Copy)]
enum Constant {
Pi,
E,
Tau,
}
impl Constant {
#[allow(clippy::approx_constant)]
fn from_value(value: f64) -> Option<Self> {
if (3.14..3.15).contains(&value) {
Some(Self::Pi)
} else if (2.71..2.72).contains(&value) {
Some(Self::E)
} else if (6.28..6.29).contains(&value) {
Some(Self::Tau)
} else {
None
}
}
fn name(self) -> &'static str {
match self {
Constant::Pi => "pi",
Constant::E => "e",
Constant::Tau => "tau",
}
}
}

View File

@@ -1,6 +1,5 @@
pub(crate) use check_and_remove_from_set::*;
pub(crate) use delete_full_slice::*;
pub(crate) use hashlib_digest_hex::*;
pub(crate) use if_expr_min_max::*;
pub(crate) use implicit_cwd::*;
pub(crate) use isinstance_type_none::*;
@@ -17,7 +16,6 @@ pub(crate) use unnecessary_enumerate::*;
mod check_and_remove_from_set;
mod delete_full_slice;
mod hashlib_digest_hex;
mod if_expr_min_max;
mod implicit_cwd;
mod isinstance_type_none;

View File

@@ -130,9 +130,6 @@ fn is_number_literal(expr: &Expr, value: i8) -> bool {
if let Expr::NumberLiteral(number_literal) = expr {
if let Number::Int(number) = &number_literal.value {
return number.as_i8().is_some_and(|number| number == value);
} else if let Number::Float(number) = number_literal.value {
#[allow(clippy::float_cmp)]
return number == f64::from(value);
}
}
false

View File

@@ -43,7 +43,6 @@ FURB152.py:5:5: FURB152 [*] Replace `6.28` with `math.tau`
6 |+C = math.tau * r # FURB152
6 7 |
7 8 | e = 2.71 # FURB152
8 9 |
FURB152.py:7:5: FURB152 [*] Replace `2.71` with `math.e`
|
@@ -51,8 +50,6 @@ FURB152.py:7:5: FURB152 [*] Replace `2.71` with `math.e`
6 |
7 | e = 2.71 # FURB152
| ^^^^ FURB152
8 |
9 | r = 3.15 # OK
|
= help: Use `math.e`
@@ -66,59 +63,5 @@ FURB152.py:7:5: FURB152 [*] Replace `2.71` with `math.e`
6 7 |
7 |-e = 2.71 # FURB152
8 |+e = math.e # FURB152
8 9 |
9 10 | r = 3.15 # OK
10 11 |
FURB152.py:11:5: FURB152 [*] Replace `3.141` with `math.pi`
|
9 | r = 3.15 # OK
10 |
11 | r = 3.141 # FURB152
| ^^^^^ FURB152
12 |
13 | r = 3.1415 # FURB152
|
= help: Use `math.pi`
Safe fix
1 |+import math
1 2 | r = 3.1 # OK
2 3 |
3 4 | A = 3.14 * r ** 2 # FURB152
--------------------------------------------------------------------------------
8 9 |
9 10 | r = 3.15 # OK
10 11 |
11 |-r = 3.141 # FURB152
12 |+r = math.pi # FURB152
12 13 |
13 14 | r = 3.1415 # FURB152
14 15 |
FURB152.py:13:5: FURB152 [*] Replace `3.1415` with `math.pi`
|
11 | r = 3.141 # FURB152
12 |
13 | r = 3.1415 # FURB152
| ^^^^^^ FURB152
14 |
15 | e = 2.7 # OK
|
= help: Use `math.pi`
Safe fix
1 |+import math
1 2 | r = 3.1 # OK
2 3 |
3 4 | A = 3.14 * r ** 2 # FURB152
--------------------------------------------------------------------------------
10 11 |
11 12 | r = 3.141 # FURB152
12 13 |
13 |-r = 3.1415 # FURB152
14 |+r = math.pi # FURB152
14 15 |
15 16 | e = 2.7 # OK

View File

@@ -187,7 +187,7 @@ FURB163.py:16:1: FURB163 [*] Prefer `math.log10(1)` over `math.log` with a redun
16 |+math.log10(1)
17 17 | special_log(1, math.e)
18 18 | special_log(1, special_e)
19 19 | math.log(1, 2.0)
19 19 |
FURB163.py:17:1: FURB163 [*] Prefer `math.log(1)` over `math.log` with a redundant base
|
@@ -196,7 +196,6 @@ FURB163.py:17:1: FURB163 [*] Prefer `math.log(1)` over `math.log` with a redunda
17 | special_log(1, math.e)
| ^^^^^^^^^^^^^^^^^^^^^^ FURB163
18 | special_log(1, special_e)
19 | math.log(1, 2.0)
|
= help: Replace with `math.log(1)`
@@ -207,8 +206,8 @@ FURB163.py:17:1: FURB163 [*] Prefer `math.log(1)` over `math.log` with a redunda
17 |-special_log(1, math.e)
17 |+math.log(1)
18 18 | special_log(1, special_e)
19 19 | math.log(1, 2.0)
20 20 | math.log(1, 10.0)
19 19 |
20 20 | # Ok.
FURB163.py:18:1: FURB163 [*] Prefer `math.log(1)` over `math.log` with a redundant base
|
@@ -216,8 +215,8 @@ FURB163.py:18:1: FURB163 [*] Prefer `math.log(1)` over `math.log` with a redunda
17 | special_log(1, math.e)
18 | special_log(1, special_e)
| ^^^^^^^^^^^^^^^^^^^^^^^^^ FURB163
19 | math.log(1, 2.0)
20 | math.log(1, 10.0)
19 |
20 | # Ok.
|
= help: Replace with `math.log(1)`
@@ -227,49 +226,8 @@ FURB163.py:18:1: FURB163 [*] Prefer `math.log(1)` over `math.log` with a redunda
17 17 | special_log(1, math.e)
18 |-special_log(1, special_e)
18 |+math.log(1)
19 19 | math.log(1, 2.0)
20 20 | math.log(1, 10.0)
21 21 |
FURB163.py:19:1: FURB163 [*] Prefer `math.log2(1)` over `math.log` with a redundant base
|
17 | special_log(1, math.e)
18 | special_log(1, special_e)
19 | math.log(1, 2.0)
| ^^^^^^^^^^^^^^^^ FURB163
20 | math.log(1, 10.0)
|
= help: Replace with `math.log2(1)`
Safe fix
16 16 | special_log(1, 10)
17 17 | special_log(1, math.e)
18 18 | special_log(1, special_e)
19 |-math.log(1, 2.0)
19 |+math.log2(1)
20 20 | math.log(1, 10.0)
21 21 |
22 22 | # Ok.
FURB163.py:20:1: FURB163 [*] Prefer `math.log10(1)` over `math.log` with a redundant base
|
18 | special_log(1, special_e)
19 | math.log(1, 2.0)
20 | math.log(1, 10.0)
| ^^^^^^^^^^^^^^^^^ FURB163
21 |
22 | # Ok.
|
= help: Replace with `math.log10(1)`
Safe fix
17 17 | special_log(1, math.e)
18 18 | special_log(1, special_e)
19 19 | math.log(1, 2.0)
20 |-math.log(1, 10.0)
20 |+math.log10(1)
21 21 |
22 22 | # Ok.
23 23 | math.log2(1)
19 19 |
20 20 | # Ok.
21 21 | math.log2(1)

View File

@@ -1,339 +0,0 @@
---
source: crates/ruff_linter/src/rules/refurb/mod.rs
---
FURB181.py:19:1: FURB181 [*] Use of hashlib's `.digest().hex()`
|
17 | # these will match
18 |
19 | blake2b().digest().hex()
| ^^^^^^^^^^^^^^^^^^^^^^^^ FURB181
20 | blake2s().digest().hex()
21 | md5().digest().hex()
|
= help: Replace with `.hexdigest()`
Unsafe fix
16 16 |
17 17 | # these will match
18 18 |
19 |-blake2b().digest().hex()
19 |+blake2b().hexdigest()
20 20 | blake2s().digest().hex()
21 21 | md5().digest().hex()
22 22 | sha1().digest().hex()
FURB181.py:20:1: FURB181 [*] Use of hashlib's `.digest().hex()`
|
19 | blake2b().digest().hex()
20 | blake2s().digest().hex()
| ^^^^^^^^^^^^^^^^^^^^^^^^ FURB181
21 | md5().digest().hex()
22 | sha1().digest().hex()
|
= help: Replace with `.hexdigest()`
Unsafe fix
17 17 | # these will match
18 18 |
19 19 | blake2b().digest().hex()
20 |-blake2s().digest().hex()
20 |+blake2s().hexdigest()
21 21 | md5().digest().hex()
22 22 | sha1().digest().hex()
23 23 | sha224().digest().hex()
FURB181.py:21:1: FURB181 [*] Use of hashlib's `.digest().hex()`
|
19 | blake2b().digest().hex()
20 | blake2s().digest().hex()
21 | md5().digest().hex()
| ^^^^^^^^^^^^^^^^^^^^ FURB181
22 | sha1().digest().hex()
23 | sha224().digest().hex()
|
= help: Replace with `.hexdigest()`
Unsafe fix
18 18 |
19 19 | blake2b().digest().hex()
20 20 | blake2s().digest().hex()
21 |-md5().digest().hex()
21 |+md5().hexdigest()
22 22 | sha1().digest().hex()
23 23 | sha224().digest().hex()
24 24 | sha256().digest().hex()
FURB181.py:22:1: FURB181 [*] Use of hashlib's `.digest().hex()`
|
20 | blake2s().digest().hex()
21 | md5().digest().hex()
22 | sha1().digest().hex()
| ^^^^^^^^^^^^^^^^^^^^^ FURB181
23 | sha224().digest().hex()
24 | sha256().digest().hex()
|
= help: Replace with `.hexdigest()`
Unsafe fix
19 19 | blake2b().digest().hex()
20 20 | blake2s().digest().hex()
21 21 | md5().digest().hex()
22 |-sha1().digest().hex()
22 |+sha1().hexdigest()
23 23 | sha224().digest().hex()
24 24 | sha256().digest().hex()
25 25 | sha384().digest().hex()
FURB181.py:23:1: FURB181 [*] Use of hashlib's `.digest().hex()`
|
21 | md5().digest().hex()
22 | sha1().digest().hex()
23 | sha224().digest().hex()
| ^^^^^^^^^^^^^^^^^^^^^^^ FURB181
24 | sha256().digest().hex()
25 | sha384().digest().hex()
|
= help: Replace with `.hexdigest()`
Unsafe fix
20 20 | blake2s().digest().hex()
21 21 | md5().digest().hex()
22 22 | sha1().digest().hex()
23 |-sha224().digest().hex()
23 |+sha224().hexdigest()
24 24 | sha256().digest().hex()
25 25 | sha384().digest().hex()
26 26 | sha3_224().digest().hex()
FURB181.py:24:1: FURB181 [*] Use of hashlib's `.digest().hex()`
|
22 | sha1().digest().hex()
23 | sha224().digest().hex()
24 | sha256().digest().hex()
| ^^^^^^^^^^^^^^^^^^^^^^^ FURB181
25 | sha384().digest().hex()
26 | sha3_224().digest().hex()
|
= help: Replace with `.hexdigest()`
Unsafe fix
21 21 | md5().digest().hex()
22 22 | sha1().digest().hex()
23 23 | sha224().digest().hex()
24 |-sha256().digest().hex()
24 |+sha256().hexdigest()
25 25 | sha384().digest().hex()
26 26 | sha3_224().digest().hex()
27 27 | sha3_256().digest().hex()
FURB181.py:25:1: FURB181 [*] Use of hashlib's `.digest().hex()`
|
23 | sha224().digest().hex()
24 | sha256().digest().hex()
25 | sha384().digest().hex()
| ^^^^^^^^^^^^^^^^^^^^^^^ FURB181
26 | sha3_224().digest().hex()
27 | sha3_256().digest().hex()
|
= help: Replace with `.hexdigest()`
Unsafe fix
22 22 | sha1().digest().hex()
23 23 | sha224().digest().hex()
24 24 | sha256().digest().hex()
25 |-sha384().digest().hex()
25 |+sha384().hexdigest()
26 26 | sha3_224().digest().hex()
27 27 | sha3_256().digest().hex()
28 28 | sha3_384().digest().hex()
FURB181.py:26:1: FURB181 [*] Use of hashlib's `.digest().hex()`
|
24 | sha256().digest().hex()
25 | sha384().digest().hex()
26 | sha3_224().digest().hex()
| ^^^^^^^^^^^^^^^^^^^^^^^^^ FURB181
27 | sha3_256().digest().hex()
28 | sha3_384().digest().hex()
|
= help: Replace with `.hexdigest()`
Unsafe fix
23 23 | sha224().digest().hex()
24 24 | sha256().digest().hex()
25 25 | sha384().digest().hex()
26 |-sha3_224().digest().hex()
26 |+sha3_224().hexdigest()
27 27 | sha3_256().digest().hex()
28 28 | sha3_384().digest().hex()
29 29 | sha3_512().digest().hex()
FURB181.py:27:1: FURB181 [*] Use of hashlib's `.digest().hex()`
|
25 | sha384().digest().hex()
26 | sha3_224().digest().hex()
27 | sha3_256().digest().hex()
| ^^^^^^^^^^^^^^^^^^^^^^^^^ FURB181
28 | sha3_384().digest().hex()
29 | sha3_512().digest().hex()
|
= help: Replace with `.hexdigest()`
Unsafe fix
24 24 | sha256().digest().hex()
25 25 | sha384().digest().hex()
26 26 | sha3_224().digest().hex()
27 |-sha3_256().digest().hex()
27 |+sha3_256().hexdigest()
28 28 | sha3_384().digest().hex()
29 29 | sha3_512().digest().hex()
30 30 | sha512().digest().hex()
FURB181.py:28:1: FURB181 [*] Use of hashlib's `.digest().hex()`
|
26 | sha3_224().digest().hex()
27 | sha3_256().digest().hex()
28 | sha3_384().digest().hex()
| ^^^^^^^^^^^^^^^^^^^^^^^^^ FURB181
29 | sha3_512().digest().hex()
30 | sha512().digest().hex()
|
= help: Replace with `.hexdigest()`
Unsafe fix
25 25 | sha384().digest().hex()
26 26 | sha3_224().digest().hex()
27 27 | sha3_256().digest().hex()
28 |-sha3_384().digest().hex()
28 |+sha3_384().hexdigest()
29 29 | sha3_512().digest().hex()
30 30 | sha512().digest().hex()
31 31 | shake_128().digest(10).hex()
FURB181.py:29:1: FURB181 [*] Use of hashlib's `.digest().hex()`
|
27 | sha3_256().digest().hex()
28 | sha3_384().digest().hex()
29 | sha3_512().digest().hex()
| ^^^^^^^^^^^^^^^^^^^^^^^^^ FURB181
30 | sha512().digest().hex()
31 | shake_128().digest(10).hex()
|
= help: Replace with `.hexdigest()`
Unsafe fix
26 26 | sha3_224().digest().hex()
27 27 | sha3_256().digest().hex()
28 28 | sha3_384().digest().hex()
29 |-sha3_512().digest().hex()
29 |+sha3_512().hexdigest()
30 30 | sha512().digest().hex()
31 31 | shake_128().digest(10).hex()
32 32 | shake_256().digest(10).hex()
FURB181.py:30:1: FURB181 [*] Use of hashlib's `.digest().hex()`
|
28 | sha3_384().digest().hex()
29 | sha3_512().digest().hex()
30 | sha512().digest().hex()
| ^^^^^^^^^^^^^^^^^^^^^^^ FURB181
31 | shake_128().digest(10).hex()
32 | shake_256().digest(10).hex()
|
= help: Replace with `.hexdigest()`
Unsafe fix
27 27 | sha3_256().digest().hex()
28 28 | sha3_384().digest().hex()
29 29 | sha3_512().digest().hex()
30 |-sha512().digest().hex()
30 |+sha512().hexdigest()
31 31 | shake_128().digest(10).hex()
32 32 | shake_256().digest(10).hex()
33 33 |
FURB181.py:31:1: FURB181 Use of hashlib's `.digest().hex()`
|
29 | sha3_512().digest().hex()
30 | sha512().digest().hex()
31 | shake_128().digest(10).hex()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FURB181
32 | shake_256().digest(10).hex()
|
= help: Replace with `.hexdigest()`
FURB181.py:32:1: FURB181 Use of hashlib's `.digest().hex()`
|
30 | sha512().digest().hex()
31 | shake_128().digest(10).hex()
32 | shake_256().digest(10).hex()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FURB181
33 |
34 | hashlib.sha256().digest().hex()
|
= help: Replace with `.hexdigest()`
FURB181.py:34:1: FURB181 [*] Use of hashlib's `.digest().hex()`
|
32 | shake_256().digest(10).hex()
33 |
34 | hashlib.sha256().digest().hex()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FURB181
35 |
36 | sha256(b"text").digest().hex()
|
= help: Replace with `.hexdigest()`
Unsafe fix
31 31 | shake_128().digest(10).hex()
32 32 | shake_256().digest(10).hex()
33 33 |
34 |-hashlib.sha256().digest().hex()
34 |+hashlib.sha256().hexdigest()
35 35 |
36 36 | sha256(b"text").digest().hex()
37 37 |
FURB181.py:36:1: FURB181 [*] Use of hashlib's `.digest().hex()`
|
34 | hashlib.sha256().digest().hex()
35 |
36 | sha256(b"text").digest().hex()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FURB181
37 |
38 | hash_algo().digest().hex()
|
= help: Replace with `.hexdigest()`
Unsafe fix
33 33 |
34 34 | hashlib.sha256().digest().hex()
35 35 |
36 |-sha256(b"text").digest().hex()
36 |+sha256(b"text").hexdigest()
37 37 |
38 38 | hash_algo().digest().hex()
39 39 |
FURB181.py:38:1: FURB181 [*] Use of hashlib's `.digest().hex()`
|
36 | sha256(b"text").digest().hex()
37 |
38 | hash_algo().digest().hex()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ FURB181
39 |
40 | # not yet supported
|
= help: Replace with `.hexdigest()`
Unsafe fix
35 35 |
36 36 | sha256(b"text").digest().hex()
37 37 |
38 |-hash_algo().digest().hex()
38 |+hash_algo().hexdigest()
39 39 |
40 40 | # not yet supported
41 41 | h = sha256()

View File

@@ -1,12 +1,14 @@
use std::fmt;
use ast::Stmt;
use ruff_python_ast::{self as ast, Expr};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{self as ast, Expr};
use ruff_python_semantic::{analyze::typing, Binding, SemanticModel};
use ruff_python_semantic::analyze::typing;
use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
/// ## What it does
/// Checks for `asyncio.create_task` and `asyncio.ensure_future` calls
/// that do not store a reference to the returned result.
@@ -64,34 +66,35 @@ impl Violation for AsyncioDanglingTask {
}
/// RUF006
pub(crate) fn asyncio_dangling_task(expr: &Expr, semantic: &SemanticModel) -> Option<Diagnostic> {
pub(crate) fn asyncio_dangling_task(checker: &mut Checker, expr: &Expr) {
let Expr::Call(ast::ExprCall { func, .. }) = expr else {
return None;
return;
};
// Ex) `asyncio.create_task(...)`
if let Some(method) =
semantic
.resolve_call_path(func)
.and_then(|call_path| match call_path.as_slice() {
["asyncio", "create_task"] => Some(Method::CreateTask),
["asyncio", "ensure_future"] => Some(Method::EnsureFuture),
_ => None,
})
if let Some(method) = checker
.semantic()
.resolve_call_path(func)
.and_then(|call_path| match call_path.as_slice() {
["asyncio", "create_task"] => Some(Method::CreateTask),
["asyncio", "ensure_future"] => Some(Method::EnsureFuture),
_ => None,
})
{
return Some(Diagnostic::new(
checker.diagnostics.push(Diagnostic::new(
AsyncioDanglingTask { method },
expr.range(),
));
return;
}
// Ex) `loop = asyncio.get_running_loop(); loop.create_task(...)`
if let Expr::Attribute(ast::ExprAttribute { attr, value, .. }) = func.as_ref() {
if attr == "create_task" {
if typing::resolve_assignment(value, semantic).is_some_and(|call_path| {
if typing::resolve_assignment(value, checker.semantic()).is_some_and(|call_path| {
matches!(call_path.as_slice(), ["asyncio", "get_running_loop"])
}) {
return Some(Diagnostic::new(
checker.diagnostics.push(Diagnostic::new(
AsyncioDanglingTask {
method: Method::CreateTask,
},
@@ -100,28 +103,6 @@ pub(crate) fn asyncio_dangling_task(expr: &Expr, semantic: &SemanticModel) -> Op
}
}
}
None
}
/// RUF006
pub(crate) fn asyncio_dangling_binding(
binding: &Binding,
semantic: &SemanticModel,
) -> Option<Diagnostic> {
if binding.is_used() || !binding.kind.is_assignment() {
return None;
}
let source = binding.source?;
match semantic.statement(source) {
Stmt::Assign(ast::StmtAssign { value, targets, .. }) if targets.len() == 1 => {
asyncio_dangling_task(value, semantic)
}
Stmt::AnnAssign(ast::StmtAnnAssign {
value: Some(value), ..
}) => asyncio_dangling_task(value, semantic),
_ => None,
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]

View File

@@ -53,12 +53,10 @@ impl AlwaysFixableViolation for ExplicitFStringTypeConversion {
/// RUF010
pub(crate) fn explicit_f_string_type_conversion(checker: &mut Checker, f_string: &ast::FString) {
for (index, element) in f_string.elements.iter().enumerate() {
let Some(ast::FStringExpressionElement {
expression,
conversion,
..
}) = element.as_expression()
for (index, expr) in f_string.values.iter().enumerate() {
let Some(ast::ExprFormattedValue {
value, conversion, ..
}) = expr.as_formatted_value_expr()
else {
continue;
};
@@ -77,7 +75,7 @@ pub(crate) fn explicit_f_string_type_conversion(checker: &mut Checker, f_string:
range: _,
},
..
}) = expression.as_ref()
}) = value.as_ref()
else {
continue;
};
@@ -112,7 +110,7 @@ pub(crate) fn explicit_f_string_type_conversion(checker: &mut Checker, f_string:
continue;
}
let mut diagnostic = Diagnostic::new(ExplicitFStringTypeConversion, expression.range());
let mut diagnostic = Diagnostic::new(ExplicitFStringTypeConversion, value.range());
diagnostic.try_set_fix(|| {
convert_call_to_conversion_flag(f_string, index, checker.locator(), checker.stylist())
});

View File

@@ -24,10 +24,6 @@ use crate::rules::ruff::rules::helpers::{
/// `typing.ClassVar`. When mutability is not required, values should be
/// immutable types, like `tuple` or `frozenset`.
///
/// As a heuristic, this rule is only applied to classes with at least one
/// annotated attribute, as unannotated classes are assumed to be deliberately
/// untyped.
///
/// ## Examples
/// ```python
/// class A:
@@ -56,10 +52,6 @@ impl Violation for MutableClassDefault {
/// RUF012
pub(crate) fn mutable_class_default(checker: &mut Checker, class_def: &ast::StmtClassDef) {
if !class_def.body.iter().any(Stmt::is_ann_assign_stmt) {
return;
}
for statement in &class_def.body {
match statement {
Stmt::AnnAssign(ast::StmtAnnAssign {

View File

@@ -635,6 +635,7 @@ impl<'stmt> BasicBlocksBuilder<'stmt> {
| Expr::Set(_)
| Expr::Compare(_)
| Expr::Call(_)
| Expr::FormattedValue(_)
| Expr::FString(_)
| Expr::StringLiteral(_)
| Expr::BytesLiteral(_)

View File

@@ -17,27 +17,11 @@ RUF006.py:11:5: RUF006 Store a reference to the return value of `asyncio.ensure_
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF006
|
RUF006.py:68:12: RUF006 Store a reference to the return value of `asyncio.create_task`
RUF006.py:79:5: RUF006 Store a reference to the return value of `asyncio.create_task`
|
66 | # Error
67 | def f():
68 | task = asyncio.create_task(coordinator.ws_connect())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF006
|
RUF006.py:74:26: RUF006 Store a reference to the return value of `asyncio.create_task`
|
72 | def f():
73 | loop = asyncio.get_running_loop()
74 | task: asyncio.Task = loop.create_task(coordinator.ws_connect())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF006
|
RUF006.py:97:5: RUF006 Store a reference to the return value of `asyncio.create_task`
|
95 | def f():
96 | loop = asyncio.get_running_loop()
97 | loop.create_task(coordinator.ws_connect()) # Error
77 | def f():
78 | loop = asyncio.get_running_loop()
79 | loop.create_task(coordinator.ws_connect()) # Error
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF006
|

View File

@@ -98,12 +98,8 @@ fn contains_message(expr: &Expr) -> bool {
}
}
ast::FStringPart::FString(f_string) => {
for literal in f_string
.elements
.iter()
.filter_map(|element| element.as_literal())
{
if literal.chars().any(char::is_whitespace) {
for value in &f_string.values {
if contains_message(value) {
return true;
}
}

View File

@@ -119,21 +119,16 @@ impl From<bool> for PreviewMode {
}
}
/// Toggle for unsafe fixes.
/// `Hint` will not apply unsafe fixes but a message will be shown when they are available.
/// `Disabled` will not apply unsafe fixes or show a message.
/// `Enabled` will apply unsafe fixes.
#[derive(Debug, Copy, Clone, CacheKey, Default, PartialEq, Eq, is_macro::Is)]
pub enum UnsafeFixes {
#[default]
Hint,
Disabled,
Enabled,
}
impl From<bool> for UnsafeFixes {
fn from(value: bool) -> Self {
if value {
fn from(version: bool) -> Self {
if version {
UnsafeFixes::Enabled
} else {
UnsafeFixes::Disabled
@@ -145,7 +140,7 @@ impl UnsafeFixes {
pub fn required_applicability(&self) -> Applicability {
match self {
Self::Enabled => Applicability::Unsafe,
Self::Disabled | Self::Hint => Applicability::Safe,
Self::Disabled => Applicability::Safe,
}
}
}

View File

@@ -19,20 +19,5 @@ ipy_escape_command.ipynb:cell 1:5:8: F401 [*] `os` imported but unused
5 |-import os
6 5 |
7 6 | _ = math.pi
8 7 | %%timeit
ipy_escape_command.ipynb:cell 2:2:8: F401 [*] `sys` imported but unused
|
1 | %%timeit
2 | import sys
| ^^^ F401
|
= help: Remove unused import: `sys`
Safe fix
6 6 |
7 7 | _ = math.pi
8 8 | %%timeit
9 |-import sys

View File

@@ -4,10 +4,5 @@
"id": "1",
"metadata": {},
"outputs": [],
"source": [
"%%script bash\n",
"for i in 1 2 3; do\n",
" echo $i\n",
"done"
]
"source": ["%%timeit\n", "print('hello world')"]
}

View File

@@ -1,11 +0,0 @@
{
"execution_count": null,
"cell_type": "code",
"id": "1",
"metadata": {},
"outputs": [],
"source": [
"%%timeit\n",
"print('hello world')"
]
}

View File

@@ -26,18 +26,6 @@
"%%timeit\n",
"import sys"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "36dedfd1-6c03-4894-bea6-6c1687b82b3c",
"metadata": {},
"outputs": [],
"source": [
"%%random\n",
"# This cell is ignored\n",
"import pathlib"
]
}
],
"metadata": {

View File

@@ -22,19 +22,8 @@
"metadata": {},
"outputs": [],
"source": [
"%%timeit"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4b6d7faa-72b3-4087-8670-fe6d35e41fb6",
"metadata": {},
"outputs": [],
"source": [
"%%random\n",
"# This cell is ignored\n",
"import pathlib"
"%%timeit\n",
"import sys"
]
}
],

View File

@@ -170,50 +170,7 @@ impl Cell {
}
// Detect cell magics (which operate on multiple lines).
lines.any(|line| {
line.split_whitespace().next().is_some_and(|first| {
if first.len() < 2 {
return false;
}
let (token, command) = first.split_at(2);
// These cell magics are special in that the lines following them are valid
// Python code and the variables defined in that scope are available to the
// rest of the notebook.
//
// For example:
//
// Cell 1:
// ```python
// x = 1
// ```
//
// Cell 2:
// ```python
// %%time
// y = x
// ```
//
// Cell 3:
// ```python
// print(y) # Here, `y` is available.
// ```
//
// This is to avoid false positives when these variables are referenced
// elsewhere in the notebook.
token == "%%"
&& !matches!(
command,
"capture"
| "debug"
| "prun"
| "pypy"
| "python"
| "python3"
| "time"
| "timeit"
)
})
})
lines.any(|line| line.trim_start().starts_with("%%"))
}
}

View File

@@ -426,7 +426,6 @@ mod tests {
#[test_case(Path::new("code_and_magic.json"), true; "code_and_magic")]
#[test_case(Path::new("only_code.json"), true; "only_code")]
#[test_case(Path::new("cell_magic.json"), false; "cell_magic")]
#[test_case(Path::new("valid_cell_magic.json"), true; "valid_cell_magic")]
#[test_case(Path::new("automagic.json"), false; "automagic")]
#[test_case(Path::new("automagics.json"), false; "automagics")]
#[test_case(Path::new("automagic_before_code.json"), false; "automagic_before_code")]

View File

@@ -509,41 +509,6 @@ impl<'a> From<&'a ast::ExceptHandler> for ComparableExceptHandler<'a> {
}
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub enum ComparableFStringElement<'a> {
Literal(&'a str),
FStringExpressionElement(FStringExpressionElement<'a>),
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct FStringExpressionElement<'a> {
expression: ComparableExpr<'a>,
debug_text: Option<&'a ast::DebugText>,
conversion: ast::ConversionFlag,
format_spec: Option<Vec<ComparableFStringElement<'a>>>,
}
impl<'a> From<&'a ast::FStringElement> for ComparableFStringElement<'a> {
fn from(fstring_element: &'a ast::FStringElement) -> Self {
match fstring_element {
ast::FStringElement::Literal(ast::FStringLiteralElement { value, .. }) => {
Self::Literal(value)
}
ast::FStringElement::Expression(formatted_value) => {
Self::FStringExpressionElement(FStringExpressionElement {
expression: (&formatted_value.expression).into(),
debug_text: formatted_value.debug_text.as_ref(),
conversion: formatted_value.conversion,
format_spec: formatted_value
.format_spec
.as_ref()
.map(|spec| spec.elements.iter().map(Into::into).collect()),
})
}
}
}
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct ComparableElifElseClause<'a> {
test: Option<ComparableExpr<'a>>,
@@ -597,13 +562,13 @@ impl<'a> From<ast::LiteralExpressionRef<'a>> for ComparableLiteral<'a> {
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct ComparableFString<'a> {
elements: Vec<ComparableFStringElement<'a>>,
values: Vec<ComparableExpr<'a>>,
}
impl<'a> From<&'a ast::FString> for ComparableFString<'a> {
fn from(fstring: &'a ast::FString) -> Self {
Self {
elements: fstring.elements.iter().map(Into::into).collect(),
values: fstring.values.iter().map(Into::into).collect(),
}
}
}
@@ -752,11 +717,11 @@ pub struct ExprCall<'a> {
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct ExprFStringExpressionElement<'a> {
pub struct ExprFormattedValue<'a> {
value: Box<ComparableExpr<'a>>,
debug_text: Option<&'a ast::DebugText>,
conversion: ast::ConversionFlag,
format_spec: Vec<ComparableFStringElement<'a>>,
format_spec: Option<Box<ComparableExpr<'a>>>,
}
#[derive(Debug, PartialEq, Eq, Hash)]
@@ -848,7 +813,7 @@ pub enum ComparableExpr<'a> {
YieldFrom(ExprYieldFrom<'a>),
Compare(ExprCompare<'a>),
Call(ExprCall<'a>),
FStringExpressionElement(ExprFStringExpressionElement<'a>),
FormattedValue(ExprFormattedValue<'a>),
FString(ExprFString<'a>),
StringLiteral(ExprStringLiteral<'a>),
BytesLiteral(ExprBytesLiteral<'a>),
@@ -1010,6 +975,18 @@ impl<'a> From<&'a ast::Expr> for ComparableExpr<'a> {
func: func.into(),
arguments: arguments.into(),
}),
ast::Expr::FormattedValue(ast::ExprFormattedValue {
value,
conversion,
debug_text,
format_spec,
range: _,
}) => Self::FormattedValue(ExprFormattedValue {
value: value.into(),
conversion: *conversion,
debug_text: debug_text.as_ref(),
format_spec: format_spec.as_ref().map(Into::into),
}),
ast::Expr::FString(ast::ExprFString { value, range: _ }) => {
Self::FString(ExprFString {
parts: value.parts().map(Into::into).collect(),

View File

@@ -23,6 +23,7 @@ pub enum ExpressionRef<'a> {
YieldFrom(&'a ast::ExprYieldFrom),
Compare(&'a ast::ExprCompare),
Call(&'a ast::ExprCall),
FormattedValue(&'a ast::ExprFormattedValue),
FString(&'a ast::ExprFString),
StringLiteral(&'a ast::ExprStringLiteral),
BytesLiteral(&'a ast::ExprBytesLiteral),
@@ -66,6 +67,7 @@ impl<'a> From<&'a Expr> for ExpressionRef<'a> {
Expr::YieldFrom(value) => ExpressionRef::YieldFrom(value),
Expr::Compare(value) => ExpressionRef::Compare(value),
Expr::Call(value) => ExpressionRef::Call(value),
Expr::FormattedValue(value) => ExpressionRef::FormattedValue(value),
Expr::FString(value) => ExpressionRef::FString(value),
Expr::StringLiteral(value) => ExpressionRef::StringLiteral(value),
Expr::BytesLiteral(value) => ExpressionRef::BytesLiteral(value),
@@ -170,6 +172,11 @@ impl<'a> From<&'a ast::ExprCall> for ExpressionRef<'a> {
Self::Call(value)
}
}
impl<'a> From<&'a ast::ExprFormattedValue> for ExpressionRef<'a> {
fn from(value: &'a ast::ExprFormattedValue) -> Self {
Self::FormattedValue(value)
}
}
impl<'a> From<&'a ast::ExprFString> for ExpressionRef<'a> {
fn from(value: &'a ast::ExprFString) -> Self {
Self::FString(value)
@@ -266,6 +273,7 @@ impl<'a> From<ExpressionRef<'a>> for AnyNodeRef<'a> {
ExpressionRef::YieldFrom(expression) => AnyNodeRef::ExprYieldFrom(expression),
ExpressionRef::Compare(expression) => AnyNodeRef::ExprCompare(expression),
ExpressionRef::Call(expression) => AnyNodeRef::ExprCall(expression),
ExpressionRef::FormattedValue(expression) => AnyNodeRef::ExprFormattedValue(expression),
ExpressionRef::FString(expression) => AnyNodeRef::ExprFString(expression),
ExpressionRef::StringLiteral(expression) => AnyNodeRef::ExprStringLiteral(expression),
ExpressionRef::BytesLiteral(expression) => AnyNodeRef::ExprBytesLiteral(expression),
@@ -309,6 +317,7 @@ impl Ranged for ExpressionRef<'_> {
ExpressionRef::YieldFrom(expression) => expression.range(),
ExpressionRef::Compare(expression) => expression.range(),
ExpressionRef::Call(expression) => expression.range(),
ExpressionRef::FormattedValue(expression) => expression.range(),
ExpressionRef::FString(expression) => expression.range(),
ExpressionRef::StringLiteral(expression) => expression.range(),
ExpressionRef::BytesLiteral(expression) => expression.range(),
@@ -393,41 +402,3 @@ impl LiteralExpressionRef<'_> {
}
}
}
/// An enum that holds a reference to a string-like literal from the AST.
/// This includes string literals, bytes literals, and the literal parts of
/// f-strings.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum StringLike<'a> {
StringLiteral(&'a ast::ExprStringLiteral),
BytesLiteral(&'a ast::ExprBytesLiteral),
FStringLiteral(&'a ast::FStringLiteralElement),
}
impl<'a> From<&'a ast::ExprStringLiteral> for StringLike<'a> {
fn from(value: &'a ast::ExprStringLiteral) -> Self {
StringLike::StringLiteral(value)
}
}
impl<'a> From<&'a ast::ExprBytesLiteral> for StringLike<'a> {
fn from(value: &'a ast::ExprBytesLiteral) -> Self {
StringLike::BytesLiteral(value)
}
}
impl<'a> From<&'a ast::FStringLiteralElement> for StringLike<'a> {
fn from(value: &'a ast::FStringLiteralElement) -> Self {
StringLike::FStringLiteral(value)
}
}
impl Ranged for StringLike<'_> {
fn range(&self) -> TextRange {
match self {
StringLike::StringLiteral(literal) => literal.range(),
StringLike::BytesLiteral(literal) => literal.range(),
StringLike::FStringLiteral(literal) => literal.range(),
}
}
}

View File

@@ -12,8 +12,8 @@ use crate::parenthesize::parenthesized_range;
use crate::statement_visitor::StatementVisitor;
use crate::visitor::Visitor;
use crate::{
self as ast, Arguments, CmpOp, ExceptHandler, Expr, FStringElement, MatchCase, Operator,
Pattern, Stmt, TypeParam,
self as ast, Arguments, CmpOp, ExceptHandler, Expr, MatchCase, Operator, Pattern, Stmt,
TypeParam,
};
use crate::{AnyNodeRef, ExprContext};
@@ -136,9 +136,9 @@ pub fn any_over_expr(expr: &Expr, func: &dyn Fn(&Expr) -> bool) -> bool {
Expr::BoolOp(ast::ExprBoolOp { values, .. }) => {
values.iter().any(|expr| any_over_expr(expr, func))
}
Expr::FString(ast::ExprFString { value, .. }) => value
.elements()
.any(|expr| any_over_f_string_element(expr, func)),
Expr::FString(ast::ExprFString { value, .. }) => {
value.elements().any(|expr| any_over_expr(expr, func))
}
Expr::NamedExpr(ast::ExprNamedExpr {
target,
value,
@@ -231,6 +231,14 @@ pub fn any_over_expr(expr: &Expr, func: &dyn Fn(&Expr) -> bool) -> bool {
.iter()
.any(|keyword| any_over_expr(&keyword.value, func))
}
Expr::FormattedValue(ast::ExprFormattedValue {
value, format_spec, ..
}) => {
any_over_expr(value, func)
|| format_spec
.as_ref()
.is_some_and(|value| any_over_expr(value, func))
}
Expr::Subscript(ast::ExprSubscript { value, slice, .. }) => {
any_over_expr(value, func) || any_over_expr(slice, func)
}
@@ -307,24 +315,6 @@ pub fn any_over_pattern(pattern: &Pattern, func: &dyn Fn(&Expr) -> bool) -> bool
}
}
pub fn any_over_f_string_element(element: &FStringElement, func: &dyn Fn(&Expr) -> bool) -> bool {
match element {
FStringElement::Literal(_) => false,
FStringElement::Expression(ast::FStringExpressionElement {
expression,
format_spec,
..
}) => {
any_over_expr(expression, func)
|| format_spec.as_ref().is_some_and(|spec| {
spec.elements
.iter()
.any(|spec_element| any_over_f_string_element(spec_element, func))
})
}
}
}
pub fn any_over_stmt(stmt: &Stmt, func: &dyn Fn(&Expr) -> bool) -> bool {
match stmt {
Stmt::FunctionDef(ast::StmtFunctionDef {
@@ -1328,18 +1318,16 @@ impl Truthiness {
Expr::FString(ast::ExprFString { value, .. }) => {
if value.parts().all(|part| match part {
ast::FStringPart::Literal(string_literal) => string_literal.is_empty(),
ast::FStringPart::FString(f_string) => f_string.elements.is_empty(),
ast::FStringPart::FString(f_string) => f_string.values.is_empty(),
}) {
Self::Falsey
} else if value
.elements()
.any(|f_string_element| match f_string_element {
ast::FStringElement::Literal(ast::FStringLiteralElement {
value, ..
}) => !value.is_empty(),
ast::FStringElement::Expression(_) => true,
})
{
} else if value.elements().any(|expr| {
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = &expr {
!value.is_empty()
} else {
false
}
}) {
Self::Truthy
} else {
Self::Unknown

View File

@@ -1,7 +1,7 @@
use crate::visitor::preorder::PreorderVisitor;
use crate::{
self as ast, Alias, ArgOrKeyword, Arguments, Comprehension, Decorator, ExceptHandler, Expr,
FStringElement, Keyword, MatchCase, Mod, Parameter, ParameterWithDefault, Parameters, Pattern,
Keyword, MatchCase, Mod, Parameter, ParameterWithDefault, Parameters, Pattern,
PatternArguments, PatternKeyword, Stmt, TypeParam, TypeParamParamSpec, TypeParamTypeVar,
TypeParamTypeVarTuple, TypeParams, WithItem,
};
@@ -71,6 +71,7 @@ pub enum AnyNode {
ExprYieldFrom(ast::ExprYieldFrom),
ExprCompare(ast::ExprCompare),
ExprCall(ast::ExprCall),
ExprFormattedValue(ast::ExprFormattedValue),
ExprFString(ast::ExprFString),
ExprStringLiteral(ast::ExprStringLiteral),
ExprBytesLiteral(ast::ExprBytesLiteral),
@@ -87,8 +88,6 @@ pub enum AnyNode {
ExprSlice(ast::ExprSlice),
ExprIpyEscapeCommand(ast::ExprIpyEscapeCommand),
ExceptHandlerExceptHandler(ast::ExceptHandlerExceptHandler),
FStringExpressionElement(ast::FStringExpressionElement),
FStringLiteralElement(ast::FStringLiteralElement),
PatternMatchValue(ast::PatternMatchValue),
PatternMatchSingleton(ast::PatternMatchSingleton),
PatternMatchSequence(ast::PatternMatchSequence),
@@ -167,8 +166,7 @@ impl AnyNode {
| AnyNode::ExprYieldFrom(_)
| AnyNode::ExprCompare(_)
| AnyNode::ExprCall(_)
| AnyNode::FStringExpressionElement(_)
| AnyNode::FStringLiteralElement(_)
| AnyNode::ExprFormattedValue(_)
| AnyNode::ExprFString(_)
| AnyNode::ExprStringLiteral(_)
| AnyNode::ExprBytesLiteral(_)
@@ -235,6 +233,7 @@ impl AnyNode {
AnyNode::ExprYieldFrom(node) => Some(Expr::YieldFrom(node)),
AnyNode::ExprCompare(node) => Some(Expr::Compare(node)),
AnyNode::ExprCall(node) => Some(Expr::Call(node)),
AnyNode::ExprFormattedValue(node) => Some(Expr::FormattedValue(node)),
AnyNode::ExprFString(node) => Some(Expr::FString(node)),
AnyNode::ExprStringLiteral(node) => Some(Expr::StringLiteral(node)),
AnyNode::ExprBytesLiteral(node) => Some(Expr::BytesLiteral(node)),
@@ -279,8 +278,6 @@ impl AnyNode {
| AnyNode::StmtContinue(_)
| AnyNode::StmtIpyEscapeCommand(_)
| AnyNode::ExceptHandlerExceptHandler(_)
| AnyNode::FStringExpressionElement(_)
| AnyNode::FStringLiteralElement(_)
| AnyNode::PatternMatchValue(_)
| AnyNode::PatternMatchSingleton(_)
| AnyNode::PatternMatchSequence(_)
@@ -359,8 +356,7 @@ impl AnyNode {
| AnyNode::ExprYieldFrom(_)
| AnyNode::ExprCompare(_)
| AnyNode::ExprCall(_)
| AnyNode::FStringExpressionElement(_)
| AnyNode::FStringLiteralElement(_)
| AnyNode::ExprFormattedValue(_)
| AnyNode::ExprFString(_)
| AnyNode::ExprStringLiteral(_)
| AnyNode::ExprBytesLiteral(_)
@@ -463,8 +459,7 @@ impl AnyNode {
| AnyNode::ExprYieldFrom(_)
| AnyNode::ExprCompare(_)
| AnyNode::ExprCall(_)
| AnyNode::FStringExpressionElement(_)
| AnyNode::FStringLiteralElement(_)
| AnyNode::ExprFormattedValue(_)
| AnyNode::ExprFString(_)
| AnyNode::ExprStringLiteral(_)
| AnyNode::ExprBytesLiteral(_)
@@ -552,8 +547,7 @@ impl AnyNode {
| AnyNode::ExprYieldFrom(_)
| AnyNode::ExprCompare(_)
| AnyNode::ExprCall(_)
| AnyNode::FStringExpressionElement(_)
| AnyNode::FStringLiteralElement(_)
| AnyNode::ExprFormattedValue(_)
| AnyNode::ExprFString(_)
| AnyNode::ExprStringLiteral(_)
| AnyNode::ExprBytesLiteral(_)
@@ -666,8 +660,7 @@ impl AnyNode {
Self::ExprYieldFrom(node) => AnyNodeRef::ExprYieldFrom(node),
Self::ExprCompare(node) => AnyNodeRef::ExprCompare(node),
Self::ExprCall(node) => AnyNodeRef::ExprCall(node),
Self::FStringExpressionElement(node) => AnyNodeRef::FStringExpressionElement(node),
Self::FStringLiteralElement(node) => AnyNodeRef::FStringLiteralElement(node),
Self::ExprFormattedValue(node) => AnyNodeRef::ExprFormattedValue(node),
Self::ExprFString(node) => AnyNodeRef::ExprFString(node),
Self::ExprStringLiteral(node) => AnyNodeRef::ExprStringLiteral(node),
Self::ExprBytesLiteral(node) => AnyNodeRef::ExprBytesLiteral(node),
@@ -2628,12 +2621,12 @@ impl AstNode for ast::ExprCall {
visitor.visit_arguments(arguments);
}
}
impl AstNode for ast::FStringExpressionElement {
impl AstNode for ast::ExprFormattedValue {
fn cast(kind: AnyNode) -> Option<Self>
where
Self: Sized,
{
if let AnyNode::FStringExpressionElement(node) = kind {
if let AnyNode::ExprFormattedValue(node) = kind {
Some(node)
} else {
None
@@ -2641,7 +2634,7 @@ impl AstNode for ast::FStringExpressionElement {
}
fn cast_ref(kind: AnyNodeRef) -> Option<&Self> {
if let AnyNodeRef::FStringExpressionElement(node) = kind {
if let AnyNodeRef::ExprFormattedValue(node) = kind {
Some(node)
} else {
None
@@ -2660,54 +2653,16 @@ impl AstNode for ast::FStringExpressionElement {
where
V: PreorderVisitor<'a> + ?Sized,
{
let ast::FStringExpressionElement {
expression,
format_spec,
..
let ast::ExprFormattedValue {
value, format_spec, ..
} = self;
visitor.visit_expr(expression);
visitor.visit_expr(value);
if let Some(format_spec) = format_spec {
for spec_part in &format_spec.elements {
visitor.visit_f_string_element(spec_part);
}
if let Some(expr) = format_spec {
visitor.visit_format_spec(expr);
}
}
}
impl AstNode for ast::FStringLiteralElement {
fn cast(kind: AnyNode) -> Option<Self>
where
Self: Sized,
{
if let AnyNode::FStringLiteralElement(node) = kind {
Some(node)
} else {
None
}
}
fn cast_ref(kind: AnyNodeRef) -> Option<&Self> {
if let AnyNodeRef::FStringLiteralElement(node) = kind {
Some(node)
} else {
None
}
}
fn as_any_node_ref(&self) -> AnyNodeRef {
AnyNodeRef::from(self)
}
fn into_any_node(self) -> AnyNode {
AnyNode::from(self)
}
fn visit_preorder<'a, V>(&'a self, _visitor: &mut V)
where
V: PreorderVisitor<'a> + ?Sized,
{
}
}
impl AstNode for ast::ExprFString {
fn cast(kind: AnyNode) -> Option<Self>
where
@@ -4384,10 +4339,10 @@ impl AstNode for ast::FString {
where
V: PreorderVisitor<'a> + ?Sized,
{
let ast::FString { elements, range: _ } = self;
let ast::FString { values, range: _ } = self;
for fstring_element in elements {
visitor.visit_f_string_element(fstring_element);
for expr in values {
visitor.visit_expr(expr);
}
}
}
@@ -4512,6 +4467,7 @@ impl From<Expr> for AnyNode {
Expr::YieldFrom(node) => AnyNode::ExprYieldFrom(node),
Expr::Compare(node) => AnyNode::ExprCompare(node),
Expr::Call(node) => AnyNode::ExprCall(node),
Expr::FormattedValue(node) => AnyNode::ExprFormattedValue(node),
Expr::FString(node) => AnyNode::ExprFString(node),
Expr::StringLiteral(node) => AnyNode::ExprStringLiteral(node),
Expr::BytesLiteral(node) => AnyNode::ExprBytesLiteral(node),
@@ -4540,15 +4496,6 @@ impl From<Mod> for AnyNode {
}
}
impl From<FStringElement> for AnyNode {
fn from(element: FStringElement) -> Self {
match element {
FStringElement::Literal(node) => AnyNode::FStringLiteralElement(node),
FStringElement::Expression(node) => AnyNode::FStringExpressionElement(node),
}
}
}
impl From<Pattern> for AnyNode {
fn from(pattern: Pattern) -> Self {
match pattern {
@@ -4842,15 +4789,9 @@ impl From<ast::ExprCall> for AnyNode {
}
}
impl From<ast::FStringExpressionElement> for AnyNode {
fn from(node: ast::FStringExpressionElement) -> Self {
AnyNode::FStringExpressionElement(node)
}
}
impl From<ast::FStringLiteralElement> for AnyNode {
fn from(node: ast::FStringLiteralElement) -> Self {
AnyNode::FStringLiteralElement(node)
impl From<ast::ExprFormattedValue> for AnyNode {
fn from(node: ast::ExprFormattedValue) -> Self {
AnyNode::ExprFormattedValue(node)
}
}
@@ -5148,8 +5089,7 @@ impl Ranged for AnyNode {
AnyNode::ExprYieldFrom(node) => node.range(),
AnyNode::ExprCompare(node) => node.range(),
AnyNode::ExprCall(node) => node.range(),
AnyNode::FStringExpressionElement(node) => node.range(),
AnyNode::FStringLiteralElement(node) => node.range(),
AnyNode::ExprFormattedValue(node) => node.range(),
AnyNode::ExprFString(node) => node.range(),
AnyNode::ExprStringLiteral(node) => node.range(),
AnyNode::ExprBytesLiteral(node) => node.range(),
@@ -5244,8 +5184,7 @@ pub enum AnyNodeRef<'a> {
ExprYieldFrom(&'a ast::ExprYieldFrom),
ExprCompare(&'a ast::ExprCompare),
ExprCall(&'a ast::ExprCall),
FStringExpressionElement(&'a ast::FStringExpressionElement),
FStringLiteralElement(&'a ast::FStringLiteralElement),
ExprFormattedValue(&'a ast::ExprFormattedValue),
ExprFString(&'a ast::ExprFString),
ExprStringLiteral(&'a ast::ExprStringLiteral),
ExprBytesLiteral(&'a ast::ExprBytesLiteral),
@@ -5339,8 +5278,7 @@ impl<'a> AnyNodeRef<'a> {
AnyNodeRef::ExprYieldFrom(node) => NonNull::from(*node).cast(),
AnyNodeRef::ExprCompare(node) => NonNull::from(*node).cast(),
AnyNodeRef::ExprCall(node) => NonNull::from(*node).cast(),
AnyNodeRef::FStringExpressionElement(node) => NonNull::from(*node).cast(),
AnyNodeRef::FStringLiteralElement(node) => NonNull::from(*node).cast(),
AnyNodeRef::ExprFormattedValue(node) => NonNull::from(*node).cast(),
AnyNodeRef::ExprFString(node) => NonNull::from(*node).cast(),
AnyNodeRef::ExprStringLiteral(node) => NonNull::from(*node).cast(),
AnyNodeRef::ExprBytesLiteral(node) => NonNull::from(*node).cast(),
@@ -5440,8 +5378,7 @@ impl<'a> AnyNodeRef<'a> {
AnyNodeRef::ExprYieldFrom(_) => NodeKind::ExprYieldFrom,
AnyNodeRef::ExprCompare(_) => NodeKind::ExprCompare,
AnyNodeRef::ExprCall(_) => NodeKind::ExprCall,
AnyNodeRef::FStringExpressionElement(_) => NodeKind::FStringExpressionElement,
AnyNodeRef::FStringLiteralElement(_) => NodeKind::FStringLiteralElement,
AnyNodeRef::ExprFormattedValue(_) => NodeKind::ExprFormattedValue,
AnyNodeRef::ExprFString(_) => NodeKind::ExprFString,
AnyNodeRef::ExprStringLiteral(_) => NodeKind::ExprStringLiteral,
AnyNodeRef::ExprBytesLiteral(_) => NodeKind::ExprBytesLiteral,
@@ -5536,8 +5473,7 @@ impl<'a> AnyNodeRef<'a> {
| AnyNodeRef::ExprYieldFrom(_)
| AnyNodeRef::ExprCompare(_)
| AnyNodeRef::ExprCall(_)
| AnyNodeRef::FStringExpressionElement(_)
| AnyNodeRef::FStringLiteralElement(_)
| AnyNodeRef::ExprFormattedValue(_)
| AnyNodeRef::ExprFString(_)
| AnyNodeRef::ExprStringLiteral(_)
| AnyNodeRef::ExprBytesLiteral(_)
@@ -5604,6 +5540,7 @@ impl<'a> AnyNodeRef<'a> {
| AnyNodeRef::ExprYieldFrom(_)
| AnyNodeRef::ExprCompare(_)
| AnyNodeRef::ExprCall(_)
| AnyNodeRef::ExprFormattedValue(_)
| AnyNodeRef::ExprFString(_)
| AnyNodeRef::ExprStringLiteral(_)
| AnyNodeRef::ExprBytesLiteral(_)
@@ -5648,8 +5585,6 @@ impl<'a> AnyNodeRef<'a> {
| AnyNodeRef::StmtContinue(_)
| AnyNodeRef::StmtIpyEscapeCommand(_)
| AnyNodeRef::ExceptHandlerExceptHandler(_)
| AnyNodeRef::FStringExpressionElement(_)
| AnyNodeRef::FStringLiteralElement(_)
| AnyNodeRef::PatternMatchValue(_)
| AnyNodeRef::PatternMatchSingleton(_)
| AnyNodeRef::PatternMatchSequence(_)
@@ -5727,8 +5662,7 @@ impl<'a> AnyNodeRef<'a> {
| AnyNodeRef::ExprYieldFrom(_)
| AnyNodeRef::ExprCompare(_)
| AnyNodeRef::ExprCall(_)
| AnyNodeRef::FStringExpressionElement(_)
| AnyNodeRef::FStringLiteralElement(_)
| AnyNodeRef::ExprFormattedValue(_)
| AnyNodeRef::ExprFString(_)
| AnyNodeRef::ExprStringLiteral(_)
| AnyNodeRef::ExprBytesLiteral(_)
@@ -5831,8 +5765,7 @@ impl<'a> AnyNodeRef<'a> {
| AnyNodeRef::ExprYieldFrom(_)
| AnyNodeRef::ExprCompare(_)
| AnyNodeRef::ExprCall(_)
| AnyNodeRef::FStringExpressionElement(_)
| AnyNodeRef::FStringLiteralElement(_)
| AnyNodeRef::ExprFormattedValue(_)
| AnyNodeRef::ExprFString(_)
| AnyNodeRef::ExprStringLiteral(_)
| AnyNodeRef::ExprBytesLiteral(_)
@@ -5920,8 +5853,7 @@ impl<'a> AnyNodeRef<'a> {
| AnyNodeRef::ExprYieldFrom(_)
| AnyNodeRef::ExprCompare(_)
| AnyNodeRef::ExprCall(_)
| AnyNodeRef::FStringExpressionElement(_)
| AnyNodeRef::FStringLiteralElement(_)
| AnyNodeRef::ExprFormattedValue(_)
| AnyNodeRef::ExprFString(_)
| AnyNodeRef::ExprStringLiteral(_)
| AnyNodeRef::ExprBytesLiteral(_)
@@ -6043,8 +5975,7 @@ impl<'a> AnyNodeRef<'a> {
AnyNodeRef::ExprYieldFrom(node) => node.visit_preorder(visitor),
AnyNodeRef::ExprCompare(node) => node.visit_preorder(visitor),
AnyNodeRef::ExprCall(node) => node.visit_preorder(visitor),
AnyNodeRef::FStringExpressionElement(node) => node.visit_preorder(visitor),
AnyNodeRef::FStringLiteralElement(node) => node.visit_preorder(visitor),
AnyNodeRef::ExprFormattedValue(node) => node.visit_preorder(visitor),
AnyNodeRef::ExprFString(node) => node.visit_preorder(visitor),
AnyNodeRef::ExprStringLiteral(node) => node.visit_preorder(visitor),
AnyNodeRef::ExprBytesLiteral(node) => node.visit_preorder(visitor),
@@ -6423,15 +6354,9 @@ impl<'a> From<&'a ast::ExprCall> for AnyNodeRef<'a> {
}
}
impl<'a> From<&'a ast::FStringExpressionElement> for AnyNodeRef<'a> {
fn from(node: &'a ast::FStringExpressionElement) -> Self {
AnyNodeRef::FStringExpressionElement(node)
}
}
impl<'a> From<&'a ast::FStringLiteralElement> for AnyNodeRef<'a> {
fn from(node: &'a ast::FStringLiteralElement) -> Self {
AnyNodeRef::FStringLiteralElement(node)
impl<'a> From<&'a ast::ExprFormattedValue> for AnyNodeRef<'a> {
fn from(node: &'a ast::ExprFormattedValue) -> Self {
AnyNodeRef::ExprFormattedValue(node)
}
}
@@ -6690,6 +6615,7 @@ impl<'a> From<&'a Expr> for AnyNodeRef<'a> {
Expr::YieldFrom(node) => AnyNodeRef::ExprYieldFrom(node),
Expr::Compare(node) => AnyNodeRef::ExprCompare(node),
Expr::Call(node) => AnyNodeRef::ExprCall(node),
Expr::FormattedValue(node) => AnyNodeRef::ExprFormattedValue(node),
Expr::FString(node) => AnyNodeRef::ExprFString(node),
Expr::StringLiteral(node) => AnyNodeRef::ExprStringLiteral(node),
Expr::BytesLiteral(node) => AnyNodeRef::ExprBytesLiteral(node),
@@ -6718,15 +6644,6 @@ impl<'a> From<&'a Mod> for AnyNodeRef<'a> {
}
}
impl<'a> From<&'a FStringElement> for AnyNodeRef<'a> {
fn from(element: &'a FStringElement) -> Self {
match element {
FStringElement::Expression(node) => AnyNodeRef::FStringExpressionElement(node),
FStringElement::Literal(node) => AnyNodeRef::FStringLiteralElement(node),
}
}
}
impl<'a> From<&'a Pattern> for AnyNodeRef<'a> {
fn from(pattern: &'a Pattern) -> Self {
match pattern {
@@ -6855,8 +6772,7 @@ impl Ranged for AnyNodeRef<'_> {
AnyNodeRef::ExprYieldFrom(node) => node.range(),
AnyNodeRef::ExprCompare(node) => node.range(),
AnyNodeRef::ExprCall(node) => node.range(),
AnyNodeRef::FStringExpressionElement(node) => node.range(),
AnyNodeRef::FStringLiteralElement(node) => node.range(),
AnyNodeRef::ExprFormattedValue(node) => node.range(),
AnyNodeRef::ExprFString(node) => node.range(),
AnyNodeRef::ExprStringLiteral(node) => node.range(),
AnyNodeRef::ExprBytesLiteral(node) => node.range(),
@@ -6953,8 +6869,7 @@ pub enum NodeKind {
ExprYieldFrom,
ExprCompare,
ExprCall,
FStringExpressionElement,
FStringLiteralElement,
ExprFormattedValue,
ExprFString,
ExprStringLiteral,
ExprBytesLiteral,

View File

@@ -590,6 +590,8 @@ pub enum Expr {
Compare(ExprCompare),
#[is(name = "call_expr")]
Call(ExprCall),
#[is(name = "formatted_value_expr")]
FormattedValue(ExprFormattedValue),
#[is(name = "f_string_expr")]
FString(ExprFString),
#[is(name = "string_literal_expr")]
@@ -917,51 +919,19 @@ impl From<ExprCall> for Expr {
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct FStringFormatSpec {
pub range: TextRange,
pub elements: Vec<FStringElement>,
}
impl Ranged for FStringFormatSpec {
fn range(&self) -> TextRange {
self.range
}
}
/// See also [FormattedValue](https://docs.python.org/3/library/ast.html#ast.FormattedValue)
#[derive(Clone, Debug, PartialEq)]
pub struct FStringExpressionElement {
pub struct ExprFormattedValue {
pub range: TextRange,
pub expression: Box<Expr>,
pub value: Box<Expr>,
pub debug_text: Option<DebugText>,
pub conversion: ConversionFlag,
pub format_spec: Option<Box<FStringFormatSpec>>,
pub format_spec: Option<Box<Expr>>,
}
impl Ranged for FStringExpressionElement {
fn range(&self) -> TextRange {
self.range
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct FStringLiteralElement {
pub range: TextRange,
pub value: String,
}
impl Ranged for FStringLiteralElement {
fn range(&self) -> TextRange {
self.range
}
}
impl Deref for FStringLiteralElement {
type Target = str;
fn deref(&self) -> &Self::Target {
self.value.as_str()
impl From<ExprFormattedValue> for Expr {
fn from(payload: ExprFormattedValue) -> Self {
Expr::FormattedValue(payload)
}
}
@@ -1094,7 +1064,7 @@ impl FStringValue {
self.parts().filter_map(|part| part.as_f_string())
}
/// Returns an iterator over all the [`FStringElement`] contained in this value.
/// Returns an iterator over all the f-string elements contained in this value.
///
/// An f-string element is what makes up an [`FString`] i.e., it is either a
/// string literal or an expression. In the following example,
@@ -1105,8 +1075,8 @@ impl FStringValue {
///
/// The f-string elements returned would be string literal (`"bar "`),
/// expression (`x`) and string literal (`"qux"`).
pub fn elements(&self) -> impl Iterator<Item = &FStringElement> {
self.f_strings().flat_map(|fstring| fstring.elements.iter())
pub fn elements(&self) -> impl Iterator<Item = &Expr> {
self.f_strings().flat_map(|fstring| fstring.values.iter())
}
}
@@ -1143,7 +1113,7 @@ impl Ranged for FStringPart {
#[derive(Clone, Debug, PartialEq)]
pub struct FString {
pub range: TextRange,
pub elements: Vec<FStringElement>,
pub values: Vec<Expr>,
}
impl Ranged for FString {
@@ -1162,21 +1132,6 @@ impl From<FString> for Expr {
}
}
#[derive(Clone, Debug, PartialEq, is_macro::Is)]
pub enum FStringElement {
Literal(FStringLiteralElement),
Expression(FStringExpressionElement),
}
impl Ranged for FStringElement {
fn range(&self) -> TextRange {
match self {
FStringElement::Literal(node) => node.range(),
FStringElement::Expression(node) => node.range(),
}
}
}
/// An AST node that represents either a single string literal or an implicitly
/// concatenated string literals.
#[derive(Clone, Debug, Default, PartialEq)]
@@ -3528,6 +3483,11 @@ impl Ranged for crate::nodes::ExprCall {
self.range
}
}
impl Ranged for crate::nodes::ExprFormattedValue {
fn range(&self) -> TextRange {
self.range
}
}
impl Ranged for crate::nodes::ExprFString {
fn range(&self) -> TextRange {
self.range
@@ -3593,6 +3553,7 @@ impl Ranged for crate::Expr {
Self::YieldFrom(node) => node.range(),
Self::Compare(node) => node.range(),
Self::Call(node) => node.range(),
Self::FormattedValue(node) => node.range(),
Self::FString(node) => node.range(),
Self::StringLiteral(node) => node.range(),
Self::BytesLiteral(node) => node.range(),

Some files were not shown because too many files have changed in this diff Show More