Compare commits
41 Commits
zb/debug-c
...
charlie/ty
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
92fa77ee53 | ||
|
|
6c0068eeec | ||
|
|
c306f85691 | ||
|
|
b972455ac7 | ||
|
|
5559827a78 | ||
|
|
cb8eea64a8 | ||
|
|
8e9bf84047 | ||
|
|
2993c342d2 | ||
|
|
108260298f | ||
|
|
a53d59f6bd | ||
|
|
1026ece946 | ||
|
|
f452bf8cad | ||
|
|
07380e0657 | ||
|
|
3aa6a30395 | ||
|
|
efb76ffa64 | ||
|
|
4e461cbf03 | ||
|
|
93417b5644 | ||
|
|
b8dd499b2a | ||
|
|
cd2bf26845 | ||
|
|
6e36dcfefe | ||
|
|
febc69ab48 | ||
|
|
6c2613b44e | ||
|
|
cb8a2f5615 | ||
|
|
b7b137abc8 | ||
|
|
f69a35a021 | ||
|
|
829a808526 | ||
|
|
85fc57e7f9 | ||
|
|
20e33bf514 | ||
|
|
b7dd2b5941 | ||
|
|
e043bd46b5 | ||
|
|
d0d88d9375 | ||
|
|
a224f19903 | ||
|
|
2414298289 | ||
|
|
6bbabceead | ||
|
|
04ec11a73d | ||
|
|
b021ede481 | ||
|
|
96ae9fe685 | ||
|
|
cdac90ef68 | ||
|
|
fcc08894cf | ||
|
|
ebc7ac31cb | ||
|
|
981a0703ed |
10
.github/workflows/ci.yaml
vendored
10
.github/workflows/ci.yaml
vendored
@@ -215,7 +215,7 @@ jobs:
|
||||
}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
@@ -338,7 +338,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
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@v4
|
||||
- uses: actions/setup-python@v5
|
||||
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@v4
|
||||
- uses: actions/setup-python@v5
|
||||
- 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@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
|
||||
2
.github/workflows/docs.yaml
vendored
2
.github/workflows/docs.yaml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.ref }}
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
- name: "Add SSH key"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
uses: webfactory/ssh-agent@v0.8.0
|
||||
|
||||
16
.github/workflows/flake8-to-ruff.yaml
vendored
16
.github/workflows/flake8-to-ruff.yaml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
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@v4
|
||||
- uses: actions/setup-python@v5
|
||||
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@v4
|
||||
- uses: actions/setup-python@v5
|
||||
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@v4
|
||||
- uses: actions/setup-python@v5
|
||||
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@v4
|
||||
- uses: actions/setup-python@v5
|
||||
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@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
@@ -197,7 +197,7 @@ jobs:
|
||||
arch: armv7
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
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@v4
|
||||
- uses: actions/setup-python@v5
|
||||
- name: "Publish to PyPi"
|
||||
env:
|
||||
TWINE_USERNAME: __token__
|
||||
|
||||
31
.github/workflows/release.yaml
vendored
31
.github/workflows/release.yaml
vendored
@@ -36,7 +36,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.sha }}
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
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@v4
|
||||
- uses: actions/setup-python@v5
|
||||
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@v4
|
||||
- uses: actions/setup-python@v5
|
||||
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@v4
|
||||
- uses: actions/setup-python@v5
|
||||
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@v4
|
||||
- uses: actions/setup-python@v5
|
||||
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@v4
|
||||
- uses: actions/setup-python@v5
|
||||
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@v4
|
||||
- uses: actions/setup-python@v5
|
||||
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 py3-pip
|
||||
pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links /io/dist/ --force-reinstall
|
||||
ruff --help
|
||||
python -m ruff --help
|
||||
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
|
||||
- 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@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: "Prep README.md"
|
||||
@@ -388,10 +388,11 @@ jobs:
|
||||
distro: alpine_latest
|
||||
githubToken: ${{ github.token }}
|
||||
install: |
|
||||
apk add py3-pip
|
||||
apk add python3
|
||||
run: |
|
||||
pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
|
||||
ruff check --help
|
||||
python -m venv .venv
|
||||
.venv/bin/pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
|
||||
.venv/bin/ruff check --help
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
|
||||
153
Cargo.lock
generated
153
Cargo.lock
generated
@@ -16,14 +16,15 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.3"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
|
||||
checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -381,7 +382,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -606,7 +607,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -617,7 +618,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -790,14 +791,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.22"
|
||||
version = "0.2.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0"
|
||||
checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall 0.3.5",
|
||||
"windows-sys 0.48.0",
|
||||
"redox_syscall 0.4.1",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1122,15 +1123,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "is-macro"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4467ed1321b310c2625c5aa6c1b1ffc5de4d9e42668cf697a08fb033ee8265e"
|
||||
checksum = "bc74b7abae208af9314a406bd7dcc65091230b6e749c09e07a645885fecf34f9"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"pmutil 0.6.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1708,7 +1709,7 @@ checksum = "52a40bc70c2c58040d2d8b167ba9a5ff59fc9dab7ad44771cfde3dcfde7a09c6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2269,7 +2270,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"ruff_python_trivia",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2676,18 +2677,18 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.190"
|
||||
version = "1.0.193"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7"
|
||||
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde-wasm-bindgen"
|
||||
version = "0.6.1"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17ba92964781421b6cef36bf0d7da26d201e96d84e1b10e7ae6ed416e516906d"
|
||||
checksum = "b9b713f70513ae1f8d92665bbbbda5c295c2cf1da5542881ae5eefe20c9af132"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"serde",
|
||||
@@ -2696,13 +2697,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.190"
|
||||
version = "1.0.193"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3"
|
||||
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2764,7 +2765,7 @@ dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2868,7 +2869,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2884,9 +2885,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.39"
|
||||
version = "2.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
|
||||
checksum = "13fa70a4ee923979ffb522cacce59d34421ebdea5625e1073c4326ef9d2dd42e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2973,7 +2974,7 @@ dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2985,7 +2986,7 @@ dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
"test-case-core",
|
||||
]
|
||||
|
||||
@@ -3006,7 +3007,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3168,7 +3169,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3386,7 +3387,7 @@ checksum = "f49e7f3f3db8040a100710a11932239fd30697115e2ba4107080d8252939845e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3480,7 +3481,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -3514,7 +3515,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -3547,7 +3548,7 @@ checksum = "493fcbab756bb764fa37e6bee8cec2dd709eb4273d06d0c282a5e74275ded735"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3644,6 +3645,15 @@ 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"
|
||||
@@ -3674,6 +3684,21 @@ 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"
|
||||
@@ -3686,6 +3711,12 @@ 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"
|
||||
@@ -3698,6 +3729,12 @@ 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"
|
||||
@@ -3710,6 +3747,12 @@ 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"
|
||||
@@ -3722,6 +3765,12 @@ 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"
|
||||
@@ -3734,6 +3783,12 @@ 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"
|
||||
@@ -3746,6 +3801,12 @@ 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"
|
||||
@@ -3758,6 +3819,12 @@ 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"
|
||||
@@ -3796,3 +3863,23 @@ 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",
|
||||
]
|
||||
|
||||
@@ -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.20" }
|
||||
filetime = { version = "0.2.23" }
|
||||
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.0" }
|
||||
is-macro = { version = "0.3.1" }
|
||||
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.190", features = ["derive"] }
|
||||
serde = { version = "1.0.193", 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.39" }
|
||||
syn = { version = "2.0.40" }
|
||||
test-case = { version = "3.2.1" }
|
||||
thiserror = { version = "1.0.50" }
|
||||
toml = { version = "0.7.8" }
|
||||
|
||||
@@ -125,15 +125,7 @@ impl Printer {
|
||||
if let Some(fixables) = fixables {
|
||||
let fix_prefix = format!("[{}]", "*".cyan());
|
||||
|
||||
if self.unsafe_fixes.is_enabled() {
|
||||
if fixables.applicable > 0 {
|
||||
writeln!(
|
||||
writer,
|
||||
"{fix_prefix} {} fixable with the --fix option.",
|
||||
fixables.applicable
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
if self.unsafe_fixes.is_hint() {
|
||||
if fixables.applicable > 0 && fixables.unapplicable_unsafe > 0 {
|
||||
let es = if fixables.unapplicable_unsafe == 1 {
|
||||
""
|
||||
@@ -163,6 +155,14 @@ impl Printer {
|
||||
fixables.unapplicable_unsafe
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
if fixables.applicable > 0 {
|
||||
writeln!(
|
||||
writer,
|
||||
"{fix_prefix} {} fixable with the --fix option.",
|
||||
fixables.applicable
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -1158,6 +1158,44 @@ 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()
|
||||
|
||||
@@ -8,6 +8,7 @@ def func(address):
|
||||
# Error
|
||||
"0.0.0.0"
|
||||
'0.0.0.0'
|
||||
f"0.0.0.0"
|
||||
|
||||
|
||||
# Error
|
||||
|
||||
@@ -5,6 +5,9 @@ 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")
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ def f_ok():
|
||||
raise RuntimeError(msg)
|
||||
|
||||
|
||||
def f_unfixable():
|
||||
def f_msg_defined():
|
||||
msg = "hello"
|
||||
raise RuntimeError("This is an example exception")
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ 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"
|
||||
|
||||
|
||||
@@ -29,6 +29,10 @@ 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
|
||||
|
||||
|
||||
4
crates/ruff_linter/resources/test/fixtures/isort/force_sort_within_sections_lines_between.py
vendored
Normal file
4
crates/ruff_linter/resources/test/fixtures/isort/force_sort_within_sections_lines_between.py
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
from a import x
|
||||
import b
|
||||
from c import y
|
||||
import d
|
||||
@@ -55,3 +55,6 @@ 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
|
||||
|
||||
@@ -72,3 +72,15 @@ 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
|
||||
|
||||
@@ -60,3 +60,6 @@ def f():
|
||||
if (a and
|
||||
b):
|
||||
pass
|
||||
#: Okay
|
||||
def f():
|
||||
return 1
|
||||
|
||||
@@ -19,21 +19,32 @@ if x > 0:
|
||||
else:
|
||||
import e
|
||||
|
||||
__some__magic = 1
|
||||
import sys
|
||||
sys.path.insert(0, "some/path")
|
||||
|
||||
import f
|
||||
|
||||
import matplotlib
|
||||
|
||||
matplotlib.use("Agg")
|
||||
|
||||
import g
|
||||
|
||||
__some__magic = 1
|
||||
|
||||
import h
|
||||
|
||||
|
||||
def foo() -> None:
|
||||
import e
|
||||
import i
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import g
|
||||
import j
|
||||
|
||||
import h; import i
|
||||
import k; import l
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import j; \
|
||||
import k
|
||||
import m; \
|
||||
import n
|
||||
|
||||
@@ -3,6 +3,11 @@ 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)
|
||||
|
||||
@@ -5,3 +5,11 @@ 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
|
||||
|
||||
@@ -16,6 +16,8 @@ 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)
|
||||
@@ -45,3 +47,6 @@ def log(*args):
|
||||
log(1, 2)
|
||||
log(1, 10)
|
||||
log(1, math.e)
|
||||
|
||||
math.log(1, 2.0001)
|
||||
math.log(1, 10.0001)
|
||||
|
||||
57
crates/ruff_linter/resources/test/fixtures/refurb/FURB181.py
vendored
Normal file
57
crates/ruff_linter/resources/test/fixtures/refurb/FURB181.py
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
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()
|
||||
@@ -63,11 +63,29 @@ def f():
|
||||
tasks = [asyncio.create_task(task) for task in tasks]
|
||||
|
||||
|
||||
# OK (false negative)
|
||||
# Error
|
||||
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()))
|
||||
@@ -88,3 +106,19 @@ 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())
|
||||
|
||||
@@ -59,3 +59,7 @@ class F(BaseSettings):
|
||||
without_annotation = []
|
||||
class_variable: ClassVar[list[int]] = []
|
||||
final_variable: Final[list[int]] = []
|
||||
|
||||
|
||||
class E:
|
||||
without_annotation = []
|
||||
|
||||
@@ -3,11 +3,12 @@ use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::codes::Rule;
|
||||
use crate::rules::{flake8_import_conventions, flake8_pyi, pyflakes, pylint};
|
||||
use crate::rules::{flake8_import_conventions, flake8_pyi, pyflakes, pylint, ruff};
|
||||
|
||||
/// 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,
|
||||
@@ -71,5 +72,12 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,6 +356,8 @@ 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();
|
||||
@@ -581,6 +583,9 @@ 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);
|
||||
}
|
||||
@@ -1270,32 +1275,12 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
refurb::rules::math_constant(checker, number_literal);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
|
||||
if checker.enabled(Rule::UnicodeKindPrefix) {
|
||||
for string_part in string.value.parts() {
|
||||
for string_part in 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 {
|
||||
|
||||
@@ -10,6 +10,7 @@ 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;
|
||||
|
||||
@@ -25,5 +26,6 @@ mod module;
|
||||
mod parameter;
|
||||
mod parameters;
|
||||
mod statement;
|
||||
mod string_like;
|
||||
mod suite;
|
||||
mod unresolved_references;
|
||||
|
||||
@@ -1571,7 +1571,11 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
pylint::rules::named_expr_without_context(checker, value);
|
||||
}
|
||||
if checker.enabled(Rule::AsyncioDanglingTask) {
|
||||
ruff::rules::asyncio_dangling_task(checker, value);
|
||||
if let Some(diagnostic) =
|
||||
ruff::rules::asyncio_dangling_task(value, checker.semantic())
|
||||
{
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::RepeatedAppend) {
|
||||
refurb::rules::repeated_append(checker, stmt);
|
||||
|
||||
20
crates/ruff_linter/src/checkers/ast/analyze/string_like.rs
Normal file
20
crates/ruff_linter/src/checkers/ast/analyze/string_like.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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_pattern, Visitor};
|
||||
use ruff_python_ast::visitor::{walk_except_handler, walk_f_string_element, 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::{typing, visibility};
|
||||
use ruff_python_semantic::analyze::{imports, typing, visibility};
|
||||
use ruff_python_semantic::{
|
||||
BindingFlags, BindingId, BindingKind, Exceptions, Export, FromImport, Globals, Import, Module,
|
||||
ModuleKind, NodeId, ScopeId, ScopeKind, SemanticModel, SemanticModelFlags, Snapshot,
|
||||
@@ -303,9 +303,12 @@ 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())
|
||||
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()))
|
||||
{
|
||||
self.semantic.flags |= SemanticModelFlags::IMPORT_BOUNDARY;
|
||||
}
|
||||
@@ -815,8 +818,7 @@ where
|
||||
|
||||
fn visit_expr(&mut self, expr: &'b Expr) {
|
||||
// Step 0: Pre-processing
|
||||
if !self.semantic.in_f_string()
|
||||
&& !self.semantic.in_typing_literal()
|
||||
if !self.semantic.in_typing_literal()
|
||||
&& !self.semantic.in_deferred_type_definition()
|
||||
&& self.semantic.in_type_definition()
|
||||
&& self.semantic.future_annotations()
|
||||
@@ -1238,10 +1240,7 @@ where
|
||||
}
|
||||
}
|
||||
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
|
||||
if self.semantic.in_type_definition()
|
||||
&& !self.semantic.in_typing_literal()
|
||||
&& !self.semantic.in_f_string()
|
||||
{
|
||||
if self.semantic.in_type_definition() && !self.semantic.in_typing_literal() {
|
||||
self.deferred.string_type_definitions.push((
|
||||
expr.range(),
|
||||
value.to_str(),
|
||||
@@ -1271,6 +1270,13 @@ 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();
|
||||
@@ -1326,17 +1332,6 @@ 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
|
||||
@@ -1446,6 +1441,16 @@ 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> {
|
||||
|
||||
@@ -965,6 +965,7 @@ 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),
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::ExprStringLiteral;
|
||||
use ruff_python_ast::{self as ast, StringLike};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for hardcoded bindings to all network interfaces (`0.0.0.0`).
|
||||
@@ -34,10 +37,16 @@ impl Violation for HardcodedBindAllInterfaces {
|
||||
}
|
||||
|
||||
/// S104
|
||||
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
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use ruff_python_ast::{self as ast, Expr};
|
||||
use ruff_python_ast::{self as ast, Expr, StringLike};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
@@ -51,13 +52,19 @@ impl Violation for HardcodedTempFile {
|
||||
}
|
||||
|
||||
/// S108
|
||||
pub(crate) fn hardcoded_tmp_directory(checker: &mut Checker, string: &ast::ExprStringLiteral) {
|
||||
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,
|
||||
};
|
||||
|
||||
if !checker
|
||||
.settings
|
||||
.flake8_bandit
|
||||
.hardcoded_tmp_directory
|
||||
.iter()
|
||||
.any(|prefix| string.value.to_str().starts_with(prefix))
|
||||
.any(|prefix| value.starts_with(prefix))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -76,8 +83,8 @@ pub(crate) fn hardcoded_tmp_directory(checker: &mut Checker, string: &ast::ExprS
|
||||
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
HardcodedTempFile {
|
||||
string: string.value.to_string(),
|
||||
string: value.to_string(),
|
||||
},
|
||||
string.range,
|
||||
string.range(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ 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
|
||||
@@ -15,21 +16,30 @@ 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:14:6: S104 Possible binding to all interfaces
|
||||
S104.py:11:3: S104 Possible binding to all interfaces
|
||||
|
|
||||
13 | # Error
|
||||
14 | func("0.0.0.0")
|
||||
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")
|
||||
| ^^^^^^^^^ S104
|
||||
|
|
||||
|
||||
S104.py:18:9: S104 Possible binding to all interfaces
|
||||
S104.py:19:9: S104 Possible binding to all interfaces
|
||||
|
|
||||
17 | def my_func():
|
||||
18 | x = "0.0.0.0"
|
||||
18 | def my_func():
|
||||
19 | x = "0.0.0.0"
|
||||
| ^^^^^^^^^ S104
|
||||
19 | print(x)
|
||||
20 | print(x)
|
||||
|
|
||||
|
||||
|
||||
|
||||
@@ -10,22 +10,31 @@ S108.py:5:11: S108 Probable insecure usage of temporary file or directory: "/tmp
|
||||
6 | f.write("def")
|
||||
|
|
||||
|
||||
S108.py:8:11: S108 Probable insecure usage of temporary file or directory: "/var/tmp/123"
|
||||
S108.py:8:13: S108 Probable insecure usage of temporary file or directory: "/tmp/abc"
|
||||
|
|
||||
6 | f.write("def")
|
||||
7 |
|
||||
8 | with open("/var/tmp/123", "w") as f:
|
||||
| ^^^^^^^^^^^^^^ S108
|
||||
8 | with open(f"/tmp/abc", "w") as f:
|
||||
| ^^^^^^^^ S108
|
||||
9 | f.write("def")
|
||||
|
|
||||
|
||||
S108.py:11:11: S108 Probable insecure usage of temporary file or directory: "/dev/shm/unit/test"
|
||||
S108.py:11:11: S108 Probable insecure usage of temporary file or directory: "/var/tmp/123"
|
||||
|
|
||||
9 | f.write("def")
|
||||
10 |
|
||||
11 | with open("/dev/shm/unit/test", "w") as f:
|
||||
| ^^^^^^^^^^^^^^^^^^^^ S108
|
||||
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:
|
||||
| ^^^^^^^^^^^^^^^^^^^^ S108
|
||||
15 | f.write("def")
|
||||
|
|
||||
|
||||
|
||||
|
||||
@@ -10,30 +10,39 @@ S108.py:5:11: S108 Probable insecure usage of temporary file or directory: "/tmp
|
||||
6 | f.write("def")
|
||||
|
|
||||
|
||||
S108.py:8:11: S108 Probable insecure usage of temporary file or directory: "/var/tmp/123"
|
||||
S108.py:8:13: S108 Probable insecure usage of temporary file or directory: "/tmp/abc"
|
||||
|
|
||||
6 | f.write("def")
|
||||
7 |
|
||||
8 | with open("/var/tmp/123", "w") as f:
|
||||
| ^^^^^^^^^^^^^^ S108
|
||||
8 | with open(f"/tmp/abc", "w") as f:
|
||||
| ^^^^^^^^ S108
|
||||
9 | f.write("def")
|
||||
|
|
||||
|
||||
S108.py:11:11: S108 Probable insecure usage of temporary file or directory: "/dev/shm/unit/test"
|
||||
S108.py:11:11: S108 Probable insecure usage of temporary file or directory: "/var/tmp/123"
|
||||
|
|
||||
9 | f.write("def")
|
||||
10 |
|
||||
11 | with open("/dev/shm/unit/test", "w") as f:
|
||||
| ^^^^^^^^^^^^^^^^^^^^ S108
|
||||
11 | with open("/var/tmp/123", "w") as f:
|
||||
| ^^^^^^^^^^^^^^ S108
|
||||
12 | f.write("def")
|
||||
|
|
||||
|
||||
S108.py:15:11: S108 Probable insecure usage of temporary file or directory: "/foo/bar"
|
||||
S108.py:14:11: S108 Probable insecure usage of temporary file or directory: "/dev/shm/unit/test"
|
||||
|
|
||||
14 | # not ok by config
|
||||
15 | with open("/foo/bar", "w") as f:
|
||||
12 | f.write("def")
|
||||
13 |
|
||||
14 | with open("/dev/shm/unit/test", "w") as f:
|
||||
| ^^^^^^^^^^^^^^^^^^^^ S108
|
||||
15 | f.write("def")
|
||||
|
|
||||
|
||||
S108.py:18: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:
|
||||
| ^^^^^^^^^^ S108
|
||||
16 | f.write("def")
|
||||
19 | f.write("def")
|
||||
|
|
||||
|
||||
|
||||
|
||||
@@ -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_formatted_value_expr) {
|
||||
if parent.is_some_and(Expr::is_f_string_expr) {
|
||||
content = format!(" {content} ");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,15 +191,13 @@ pub(crate) fn string_in_exception(checker: &mut Checker, stmt: &Stmt, exc: &Expr
|
||||
if let Some(indentation) =
|
||||
whitespace::indentation(checker.locator(), stmt)
|
||||
{
|
||||
if checker.semantic().is_available("msg") {
|
||||
diagnostic.set_fix(generate_fix(
|
||||
stmt,
|
||||
first,
|
||||
indentation,
|
||||
checker.stylist(),
|
||||
checker.locator(),
|
||||
));
|
||||
}
|
||||
diagnostic.set_fix(generate_fix(
|
||||
stmt,
|
||||
first,
|
||||
indentation,
|
||||
checker.stylist(),
|
||||
checker.locator(),
|
||||
));
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
@@ -211,15 +209,13 @@ 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)
|
||||
{
|
||||
if checker.semantic().is_available("msg") {
|
||||
diagnostic.set_fix(generate_fix(
|
||||
stmt,
|
||||
first,
|
||||
indentation,
|
||||
checker.stylist(),
|
||||
checker.locator(),
|
||||
));
|
||||
}
|
||||
diagnostic.set_fix(generate_fix(
|
||||
stmt,
|
||||
first,
|
||||
indentation,
|
||||
checker.stylist(),
|
||||
checker.locator(),
|
||||
));
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
@@ -236,15 +232,13 @@ pub(crate) fn string_in_exception(checker: &mut Checker, stmt: &Stmt, exc: &Expr
|
||||
if let Some(indentation) =
|
||||
whitespace::indentation(checker.locator(), stmt)
|
||||
{
|
||||
if checker.semantic().is_available("msg") {
|
||||
diagnostic.set_fix(generate_fix(
|
||||
stmt,
|
||||
first,
|
||||
indentation,
|
||||
checker.stylist(),
|
||||
checker.locator(),
|
||||
));
|
||||
}
|
||||
diagnostic.set_fix(generate_fix(
|
||||
stmt,
|
||||
first,
|
||||
indentation,
|
||||
checker.stylist(),
|
||||
checker.locator(),
|
||||
));
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -59,15 +59,26 @@ 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_unfixable():
|
||||
30 | def f_msg_defined():
|
||||
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"
|
||||
@@ -88,7 +99,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")
|
||||
@@ -96,6 +107,17 @@ EM.py:46:28: EM101 Exception must not use a string literal, assign to variable f
|
||||
|
|
||||
= 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):
|
||||
|
||||
@@ -97,15 +97,26 @@ 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_unfixable():
|
||||
30 | def f_msg_defined():
|
||||
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"
|
||||
@@ -126,7 +137,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")
|
||||
@@ -134,6 +145,17 @@ EM.py:46:28: EM101 Exception must not use a string literal, assign to variable f
|
||||
|
|
||||
= 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):
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
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;
|
||||
@@ -44,25 +43,27 @@ impl AlwaysFixableViolation for StringOrBytesTooLong {
|
||||
}
|
||||
|
||||
/// PYI053
|
||||
pub(crate) fn string_or_bytes_too_long(checker: &mut Checker, expr: &Expr) {
|
||||
pub(crate) fn string_or_bytes_too_long(checker: &mut Checker, string: StringLike) {
|
||||
// Ignore docstrings.
|
||||
if is_docstring_stmt(checker.semantic().current_statement()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let length = match expr {
|
||||
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => value.chars().count(),
|
||||
Expr::BytesLiteral(ast::ExprBytesLiteral { value, .. }) => value.len(),
|
||||
_ => 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()
|
||||
}
|
||||
};
|
||||
if length <= 50 {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(StringOrBytesTooLong, expr.range());
|
||||
let mut diagnostic = Diagnostic::new(StringOrBytesTooLong, string.range());
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
"...".to_string(),
|
||||
expr.range(),
|
||||
string.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -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 | class Demo:
|
||||
32 | ffoo: str = f"50 character stringggggggggggggggggggggggggggggggg" # OK
|
||||
|
|
||||
= help: Replace with `...`
|
||||
|
||||
@@ -101,7 +101,28 @@ 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 | class Demo:
|
||||
33 33 | """Docstrings are excluded from this rule. Some padding.""" # OK
|
||||
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
|
||||
|
||||
|
||||
|
||||
@@ -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 debug upon failure, as the
|
||||
/// Composite assertion statements are harder to debug upon failure, as the
|
||||
/// failure message will not indicate which condition failed.
|
||||
///
|
||||
/// ## Example
|
||||
|
||||
@@ -58,15 +58,25 @@ 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.values.iter().all(is_empty_or_null_string)
|
||||
}
|
||||
ast::FStringPart::FString(f_string) => f_string
|
||||
.elements
|
||||
.iter()
|
||||
.all(is_empty_or_null_fstring_element),
|
||||
})
|
||||
}
|
||||
_ => 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()]
|
||||
|
||||
@@ -1,25 +1,23 @@
|
||||
use ruff_python_ast::{self as ast, Arguments, ConversionFlag, Expr};
|
||||
use ruff_text_size::TextRange;
|
||||
|
||||
/// 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()),
|
||||
/// 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()),
|
||||
debug_text: None,
|
||||
conversion: ConversionFlag::None,
|
||||
format_spec: None,
|
||||
range: TextRange::default(),
|
||||
};
|
||||
node.into()
|
||||
})
|
||||
}
|
||||
|
||||
/// 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()
|
||||
/// 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(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Figure out if `expr` represents a "simple" call
|
||||
@@ -51,15 +49,19 @@ 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<Expr> {
|
||||
pub(super) fn to_f_string_element(expr: &Expr) -> Option<ast::FStringElement> {
|
||||
match expr {
|
||||
// These are directly handled by `unparse_f_string_element`:
|
||||
Expr::StringLiteral(_) | Expr::FString(_) | Expr::FormattedValue(_) => Some(expr.clone()),
|
||||
Expr::StringLiteral(ast::ExprStringLiteral { value, range }) => {
|
||||
Some(ast::FStringElement::Literal(ast::FStringLiteralElement {
|
||||
value: value.to_string(),
|
||||
range: *range,
|
||||
}))
|
||||
}
|
||||
// These should be pretty safe to wrap in a formatted value.
|
||||
Expr::NumberLiteral(_) | Expr::BooleanLiteral(_) | Expr::Name(_) | Expr::Attribute(_) => {
|
||||
Some(to_formatted_value_expr(expr))
|
||||
Some(to_f_string_expression_element(expr))
|
||||
}
|
||||
Expr::Call(_) if is_simple_call(expr) => Some(to_formatted_value_expr(expr)),
|
||||
Expr::Call(_) if is_simple_call(expr) => Some(to_f_string_expression_element(expr)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ fn build_fstring(joiner: &str, joinees: &[Expr]) -> Option<Expr> {
|
||||
return Some(node.into());
|
||||
}
|
||||
|
||||
let mut fstring_elems = Vec::with_capacity(joinees.len() * 2);
|
||||
let mut f_string_elements = 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) {
|
||||
fstring_elems.push(helpers::to_constant_string(joiner));
|
||||
f_string_elements.push(helpers::to_f_string_literal_element(joiner));
|
||||
}
|
||||
fstring_elems.push(helpers::to_f_string_element(expr)?);
|
||||
f_string_elements.push(helpers::to_f_string_element(expr)?);
|
||||
}
|
||||
|
||||
let node = ast::FString {
|
||||
values: fstring_elems,
|
||||
elements: f_string_elements,
|
||||
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 parts).
|
||||
// convertible to f-string elements).
|
||||
let Some(new_expr) = build_fstring(joiner, joinees) else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -200,6 +200,7 @@ 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 {
|
||||
@@ -225,6 +226,7 @@ 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 {
|
||||
@@ -722,6 +724,26 @@ 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"))]
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/isort/mod.rs
|
||||
---
|
||||
|
||||
@@ -112,7 +112,11 @@ 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() {
|
||||
return string_literal.value.to_str() == name;
|
||||
if string_literal.value.to_str() == name {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,7 +131,9 @@ 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('.') {
|
||||
return model == name;
|
||||
if model == name {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,15 @@ 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
|
||||
|
|
||||
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ 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!(
|
||||
|
||||
@@ -41,6 +41,10 @@ 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"
|
||||
|
||||
@@ -381,20 +381,16 @@ impl Whitespace {
|
||||
}
|
||||
}
|
||||
|
||||
if has_tabs {
|
||||
if len == content.text_len() {
|
||||
// All whitespace up to the start of the line -> Indent
|
||||
(Self::None, TextSize::default())
|
||||
} else if has_tabs {
|
||||
(Self::Tab, len)
|
||||
} else {
|
||||
match count {
|
||||
0 => (Self::None, TextSize::default()),
|
||||
1 => (Self::Single, len),
|
||||
_ => {
|
||||
if len == content.text_len() {
|
||||
// All whitespace up to the start of the line -> Indent
|
||||
(Self::None, TextSize::default())
|
||||
} else {
|
||||
(Self::Many, len)
|
||||
}
|
||||
}
|
||||
_ => (Self::Many, len),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix, Violation};
|
||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_parser::TokenKind;
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
@@ -34,11 +34,15 @@ use crate::rules::pycodestyle::rules::logical_lines::{LogicalLine, LogicalLineTo
|
||||
#[violation]
|
||||
pub struct UnexpectedSpacesAroundKeywordParameterEquals;
|
||||
|
||||
impl Violation for UnexpectedSpacesAroundKeywordParameterEquals {
|
||||
impl AlwaysFixableViolation 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
|
||||
@@ -165,22 +169,31 @@ 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 {
|
||||
context.push(
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
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() {
|
||||
context.push(
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
UnexpectedSpacesAroundKeywordParameterEquals,
|
||||
TextRange::new(token.end(), next.start()),
|
||||
);
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::deletion(
|
||||
token.end(),
|
||||
next.start(),
|
||||
)));
|
||||
context.push_diagnostic(diagnostic);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix, Violation};
|
||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_parser::TokenKind;
|
||||
use ruff_python_trivia::PythonWhitespace;
|
||||
@@ -66,11 +66,15 @@ impl AlwaysFixableViolation for TooFewSpacesBeforeInlineComment {
|
||||
#[violation]
|
||||
pub struct NoSpaceAfterInlineComment;
|
||||
|
||||
impl Violation for NoSpaceAfterInlineComment {
|
||||
impl AlwaysFixableViolation 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
|
||||
@@ -98,11 +102,15 @@ impl Violation for NoSpaceAfterInlineComment {
|
||||
#[violation]
|
||||
pub struct NoSpaceAfterBlockComment;
|
||||
|
||||
impl Violation for NoSpaceAfterBlockComment {
|
||||
impl AlwaysFixableViolation 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
|
||||
@@ -130,11 +138,15 @@ impl Violation for NoSpaceAfterBlockComment {
|
||||
#[violation]
|
||||
pub struct MultipleLeadingHashesForBlockComment;
|
||||
|
||||
impl Violation for MultipleLeadingHashesForBlockComment {
|
||||
impl AlwaysFixableViolation 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
|
||||
@@ -184,14 +196,30 @@ pub(crate) fn whitespace_before_comment(
|
||||
|
||||
if is_inline_comment {
|
||||
if bad_prefix.is_some() || comment.chars().next().is_some_and(char::is_whitespace) {
|
||||
context.push(NoSpaceAfterInlineComment, range);
|
||||
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);
|
||||
}
|
||||
} else if let Some(bad_prefix) = bad_prefix {
|
||||
if bad_prefix != '!' || !line.is_start_of_file() {
|
||||
if bad_prefix != '#' {
|
||||
context.push(NoSpaceAfterBlockComment, range);
|
||||
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);
|
||||
} else if !comment.is_empty() {
|
||||
context.push(MultipleLeadingHashesForBlockComment, range);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -200,3 +228,17 @@ 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())
|
||||
}
|
||||
|
||||
@@ -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,8 +9,17 @@ E25.py:2:12: E251 Unexpected spaces around keyword / parameter equals
|
||||
3 | '''Test function with an error in declaration'''
|
||||
4 | pass
|
||||
|
|
||||
= help: Remove whitespace
|
||||
|
||||
E25.py:2:14: E251 Unexpected spaces around keyword / parameter equals
|
||||
ℹ 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
|
||||
|
|
||||
1 | #: E251 E251
|
||||
2 | def foo(bar = False):
|
||||
@@ -18,8 +27,17 @@ E25.py:2:14: E251 Unexpected spaces around keyword / parameter equals
|
||||
3 | '''Test function with an error in declaration'''
|
||||
4 | pass
|
||||
|
|
||||
= help: Remove whitespace
|
||||
|
||||
E25.py:6:9: E251 Unexpected spaces around keyword / parameter equals
|
||||
ℹ 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
|
||||
|
|
||||
4 | pass
|
||||
5 | #: E251
|
||||
@@ -28,8 +46,19 @@ E25.py:6:9: E251 Unexpected spaces around keyword / parameter equals
|
||||
7 | #: E251
|
||||
8 | foo(bar =True)
|
||||
|
|
||||
= help: Remove whitespace
|
||||
|
||||
E25.py:8:8: E251 Unexpected spaces around keyword / parameter equals
|
||||
ℹ 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
|
||||
|
|
||||
6 | foo(bar= True)
|
||||
7 | #: E251
|
||||
@@ -38,8 +67,19 @@ E25.py:8:8: E251 Unexpected spaces around keyword / parameter equals
|
||||
9 | #: E251 E251
|
||||
10 | foo(bar = True)
|
||||
|
|
||||
= help: Remove whitespace
|
||||
|
||||
E25.py:10:8: E251 Unexpected spaces around keyword / parameter equals
|
||||
ℹ 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
|
||||
|
|
||||
8 | foo(bar =True)
|
||||
9 | #: E251 E251
|
||||
@@ -48,8 +88,19 @@ E25.py:10:8: E251 Unexpected spaces around keyword / parameter equals
|
||||
11 | #: E251
|
||||
12 | y = bar(root= "sdasd")
|
||||
|
|
||||
= help: Remove whitespace
|
||||
|
||||
E25.py:10:10: E251 Unexpected spaces around keyword / parameter equals
|
||||
ℹ 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
|
||||
|
|
||||
8 | foo(bar =True)
|
||||
9 | #: E251 E251
|
||||
@@ -58,8 +109,19 @@ E25.py:10:10: E251 Unexpected spaces around keyword / parameter equals
|
||||
11 | #: E251
|
||||
12 | y = bar(root= "sdasd")
|
||||
|
|
||||
= help: Remove whitespace
|
||||
|
||||
E25.py:12:14: E251 Unexpected spaces around keyword / parameter equals
|
||||
ℹ 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
|
||||
|
|
||||
10 | foo(bar = True)
|
||||
11 | #: E251
|
||||
@@ -68,8 +130,19 @@ E25.py:12:14: E251 Unexpected spaces around keyword / parameter equals
|
||||
13 | #: E251:2:29
|
||||
14 | parser.add_argument('--long-option',
|
||||
|
|
||||
= help: Remove whitespace
|
||||
|
||||
E25.py:15:29: E251 Unexpected spaces around keyword / parameter equals
|
||||
ℹ 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
|
||||
|
|
||||
13 | #: E251:2:29
|
||||
14 | parser.add_argument('--long-option',
|
||||
@@ -80,8 +153,20 @@ 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
|
||||
|
||||
E25.py:18:45: E251 Unexpected spaces around keyword / parameter equals
|
||||
ℹ 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
|
||||
|
|
||||
16 | "/rather/long/filesystem/path/here/blah/blah/blah")
|
||||
17 | #: E251:1:45
|
||||
@@ -92,8 +177,20 @@ E25.py:18:45: E251 Unexpected spaces around keyword / parameter equals
|
||||
20 | #: E251:3:8 E251:3:10
|
||||
21 | foo(True,
|
||||
|
|
||||
= help: Remove whitespace
|
||||
|
||||
E25.py:23:8: E251 Unexpected spaces around keyword / parameter equals
|
||||
ℹ 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
|
||||
|
|
||||
21 | foo(True,
|
||||
22 | baz=(1, 2),
|
||||
@@ -102,8 +199,19 @@ E25.py:23:8: E251 Unexpected spaces around keyword / parameter equals
|
||||
24 | )
|
||||
25 | #: Okay
|
||||
|
|
||||
= help: Remove whitespace
|
||||
|
||||
E25.py:23:10: E251 Unexpected spaces around keyword / parameter equals
|
||||
ℹ 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
|
||||
|
|
||||
21 | foo(True,
|
||||
22 | baz=(1, 2),
|
||||
@@ -112,5 +220,16 @@ 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))
|
||||
|
||||
|
||||
|
||||
@@ -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,8 +10,19 @@ E26.py:4:12: E262 Inline comment should start with `# `
|
||||
5 | #: E262:1:12
|
||||
6 | x = x + 1 # Increment x
|
||||
|
|
||||
= help: Format space
|
||||
|
||||
E26.py:6:12: E262 Inline comment should start with `# `
|
||||
ℹ 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 `# `
|
||||
|
|
||||
4 | x = x + 1 #Increment x
|
||||
5 | #: E262:1:12
|
||||
@@ -20,8 +31,19 @@ E26.py:6:12: E262 Inline comment should start with `# `
|
||||
7 | #: E262:1:12
|
||||
8 | x = y + 1 #: Increment x
|
||||
|
|
||||
= help: Format space
|
||||
|
||||
E26.py:8:12: E262 Inline comment should start with `# `
|
||||
ℹ 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 `# `
|
||||
|
|
||||
6 | x = x + 1 # Increment x
|
||||
7 | #: E262:1:12
|
||||
@@ -30,8 +52,19 @@ E26.py:8:12: E262 Inline comment should start with `# `
|
||||
9 | #: E265:1:1
|
||||
10 | #Block comment
|
||||
|
|
||||
= help: Format space
|
||||
|
||||
E26.py:63:9: E262 Inline comment should start with `# `
|
||||
ℹ 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 `# `
|
||||
|
|
||||
61 | # -*- coding: utf8 -*-
|
||||
62 | # (One space one NBSP) Ok for block comment
|
||||
@@ -40,8 +73,19 @@ E26.py:63:9: E262 Inline comment should start with `# `
|
||||
64 | #: E262:2:9
|
||||
65 | # (Two spaces) Ok for block comment
|
||||
|
|
||||
= help: Format space
|
||||
|
||||
E26.py:66:9: E262 Inline comment should start with `# `
|
||||
ℹ 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 `# `
|
||||
|
|
||||
64 | #: E262:2:9
|
||||
65 | # (Two spaces) Ok for block comment
|
||||
@@ -50,5 +94,52 @@ 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
|
||||
|
||||
|
||||
|
||||
@@ -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,8 +10,19 @@ E26.py:10:1: E265 Block comment should start with `# `
|
||||
11 | a = 1
|
||||
12 | #: E265:2:1
|
||||
|
|
||||
= help: Format space
|
||||
|
||||
E26.py:14:1: E265 Block comment should start with `# `
|
||||
ℹ 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 `# `
|
||||
|
|
||||
12 | #: E265:2:1
|
||||
13 | m = 42
|
||||
@@ -20,8 +31,19 @@ E26.py:14:1: E265 Block comment should start with `# `
|
||||
15 | mx = 42 - 42
|
||||
16 | #: E266:3:5 E266:6:5
|
||||
|
|
||||
= help: Format space
|
||||
|
||||
E26.py:25:1: E265 Block comment should start with `# `
|
||||
ℹ 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 `# `
|
||||
|
|
||||
23 | return
|
||||
24 | #: E265:1:1 E266:2:1
|
||||
@@ -30,8 +52,19 @@ E26.py:25:1: E265 Block comment should start with `# `
|
||||
26 | ## logging.error()
|
||||
27 | #: W291:1:42
|
||||
|
|
||||
= help: Format space
|
||||
|
||||
E26.py:32:1: E265 Block comment should start with `# `
|
||||
ℹ 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 `# `
|
||||
|
|
||||
31 | #: Okay
|
||||
32 | #!/usr/bin/env python
|
||||
@@ -39,8 +72,19 @@ E26.py:32:1: E265 Block comment should start with `# `
|
||||
33 |
|
||||
34 | pass # an inline comment
|
||||
|
|
||||
= help: Format space
|
||||
|
||||
E26.py:73:1: E265 Block comment should start with `# `
|
||||
ℹ 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 `# `
|
||||
|
|
||||
71 | # F Means test is failing (F)
|
||||
72 | # EF Means test is giving error and Failing
|
||||
@@ -48,5 +92,37 @@ 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
|
||||
|
||||
|
||||
|
||||
@@ -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,8 +9,19 @@ E26.py:19:5: E266 Too many leading `#` before block comment
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ E266
|
||||
20 | a = 42
|
||||
|
|
||||
= help: Remove leading `#`
|
||||
|
||||
E26.py:22:5: E266 Too many leading `#` before block comment
|
||||
ℹ 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
|
||||
|
|
||||
20 | a = 42
|
||||
21 |
|
||||
@@ -19,8 +30,19 @@ E26.py:22:5: E266 Too many leading `#` before block comment
|
||||
23 | return
|
||||
24 | #: E265:1:1 E266:2:1
|
||||
|
|
||||
= help: Remove leading `#`
|
||||
|
||||
E26.py:26:1: E266 Too many leading `#` before block comment
|
||||
ℹ 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
|
||||
|
|
||||
24 | #: E265:1:1 E266:2:1
|
||||
25 | ##if DEBUG:
|
||||
@@ -29,8 +51,19 @@ E26.py:26:1: E266 Too many leading `#` before block comment
|
||||
27 | #: W291:1:42
|
||||
28 | #########################################
|
||||
|
|
||||
= help: Remove leading `#`
|
||||
|
||||
E26.py:69:1: E266 Too many leading `#` before block comment
|
||||
ℹ 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
|
||||
|
|
||||
68 | #: E265:5:1
|
||||
69 | ### Means test is not done yet
|
||||
@@ -38,5 +71,37 @@ 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 |
|
||||
|
||||
|
||||
|
||||
@@ -1,27 +1,57 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
|
||||
---
|
||||
E402.py:24:1: E402 Module level import not at top of file
|
||||
E402.py:25:1: E402 Module level import not at top of file
|
||||
|
|
||||
22 | __some__magic = 1
|
||||
23 |
|
||||
24 | import f
|
||||
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
|
||||
| ^^^^^^^^ E402
|
||||
|
|
||||
|
||||
E402.py:34:1: E402 Module level import not at top of file
|
||||
E402.py:45:1: E402 Module level import not at top of file
|
||||
|
|
||||
32 | import g
|
||||
33 |
|
||||
34 | import h; import i
|
||||
43 | import j
|
||||
44 |
|
||||
45 | import k; import l
|
||||
| ^^^^^^^^ E402
|
||||
|
|
||||
|
||||
E402.py:34:11: E402 Module level import not at top of file
|
||||
E402.py:45:11: E402 Module level import not at top of file
|
||||
|
|
||||
32 | import g
|
||||
33 |
|
||||
34 | import h; import i
|
||||
43 | import j
|
||||
44 |
|
||||
45 | import k; import l
|
||||
| ^^^^^^^^ E402
|
||||
|
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
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
|
||||
|
|
||||
|
||||
|
||||
@@ -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,5 +9,13 @@ 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 | #:
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::{self as ast, Expr};
|
||||
use ruff_python_ast as ast;
|
||||
use ruff_source_file::Locator;
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
||||
@@ -47,11 +47,12 @@ 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.values.iter().any(Expr::is_formatted_value_expr))
|
||||
{
|
||||
if expr.value.f_strings().any(|f_string| {
|
||||
f_string
|
||||
.elements
|
||||
.iter()
|
||||
.any(ast::FStringElement::is_expression)
|
||||
}) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -247,6 +247,14 @@ 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,7 +68,17 @@ F841_0.py:21:6: F841 Local variable `a` is assigned to but never used
|
||||
|
|
||||
= help: Remove assignment to unused variable `a`
|
||||
|
||||
F841_0.py:21:9: F841 Local variable `b` is assigned to but never used
|
||||
ℹ 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
|
||||
|
|
||||
19 | def f():
|
||||
20 | foo = (1, 2)
|
||||
@@ -79,6 +89,16 @@ 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
|
||||
|
||||
@@ -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,7 +9,17 @@ F841_1.py:6:5: F841 Local variable `x` is assigned to but never used
|
||||
|
|
||||
= help: Remove assignment to unused variable `x`
|
||||
|
||||
F841_1.py:6:8: F841 Local variable `y` is assigned to but never used
|
||||
ℹ 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
|
||||
|
|
||||
5 | def f():
|
||||
6 | x, y = 1, 2 # this triggers F841 as it's just a simple assignment where unpacking isn't needed
|
||||
@@ -17,6 +27,16 @@ 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():
|
||||
@@ -53,7 +73,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
|
||||
@@ -61,7 +81,14 @@ F841_1.py:24:6: F841 Local variable `a` is assigned to but never used
|
||||
|
|
||||
= help: Remove assignment to unused variable `a`
|
||||
|
||||
F841_1.py:24:9: F841 Local variable `b` is assigned to but never used
|
||||
ℹ 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
|
||||
|
|
||||
23 | def f():
|
||||
24 | (a, b) = (x, y) = 1, 2 # this triggers F841 on everything
|
||||
@@ -69,7 +96,14 @@ F841_1.py:24:9: F841 Local variable `b` is assigned to but never used
|
||||
|
|
||||
= help: Remove assignment to unused variable `b`
|
||||
|
||||
F841_1.py:24:15: F841 Local variable `x` is assigned to but never used
|
||||
ℹ 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
|
||||
|
|
||||
23 | def f():
|
||||
24 | (a, b) = (x, y) = 1, 2 # this triggers F841 on everything
|
||||
@@ -77,7 +111,14 @@ F841_1.py:24:15: F841 Local variable `x` is assigned to but never used
|
||||
|
|
||||
= help: Remove assignment to unused variable `x`
|
||||
|
||||
F841_1.py:24:18: F841 Local variable `y` is assigned to but never used
|
||||
ℹ 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
|
||||
|
|
||||
23 | def f():
|
||||
24 | (a, b) = (x, y) = 1, 2 # this triggers F841 on everything
|
||||
@@ -85,4 +126,11 @@ 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
|
||||
|
||||
|
||||
|
||||
@@ -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,7 +166,17 @@ F841_3.py:32:6: F841 Local variable `x1` is assigned to but never used
|
||||
|
|
||||
= help: Remove assignment to unused variable `x1`
|
||||
|
||||
F841_3.py:32:10: F841 Local variable `y1` is assigned to but never used
|
||||
ℹ 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
|
||||
|
|
||||
31 | def f():
|
||||
32 | (x1, y1) = (1, 2)
|
||||
@@ -176,6 +186,16 @@ 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():
|
||||
|
||||
@@ -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,7 +29,17 @@ F841_4.py:13:5: F841 Local variable `b` is assigned to but never used
|
||||
|
|
||||
= help: Remove assignment to unused variable `b`
|
||||
|
||||
F841_4.py:13:8: F841 Local variable `c` is assigned to but never used
|
||||
ℹ 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
|
||||
|
|
||||
11 | def bar():
|
||||
12 | a = foo()
|
||||
@@ -38,4 +48,14 @@ 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():
|
||||
|
||||
|
||||
|
||||
@@ -72,24 +72,26 @@ 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.values.iter().all(|value| {
|
||||
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = value {
|
||||
value.is_empty()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}),
|
||||
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,
|
||||
})
|
||||
}
|
||||
}) {
|
||||
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.values.iter().any(|value| {
|
||||
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = value {
|
||||
!value.is_empty()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}),
|
||||
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,
|
||||
})
|
||||
}
|
||||
}) {
|
||||
Kind::NonEmpty
|
||||
} else {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_diagnostics::{AlwaysFixableViolation, Applicability, Diagnostic, Fix};
|
||||
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.
|
||||
@@ -36,16 +37,25 @@ use crate::checkers::ast::Checker;
|
||||
/// 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 Violation for SubprocessRunWithoutCheck {
|
||||
impl AlwaysFixableViolation 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
|
||||
@@ -56,10 +66,27 @@ 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() {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
SubprocessRunWithoutCheck,
|
||||
call.func.range(),
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,87 @@
|
||||
---
|
||||
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`
|
||||
|
||||
subprocess_run_without_check.py:5:1: PLW1510 `subprocess.run` without explicit `check` argument
|
||||
ℹ 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
|
||||
|
|
||||
3 | # Errors.
|
||||
4 | subprocess.run("ls")
|
||||
5 | subprocess.run("ls", shell=True)
|
||||
| ^^^^^^^^^^^^^^ PLW1510
|
||||
6 |
|
||||
7 | # Non-errors.
|
||||
6 | subprocess.run(
|
||||
7 | ["ls"],
|
||||
|
|
||||
= 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)
|
||||
|
||||
|
||||
|
||||
@@ -180,7 +180,6 @@ fn is_allowed_value(expr: &Expr) -> bool {
|
||||
| Expr::GeneratorExp(_)
|
||||
| Expr::Compare(_)
|
||||
| Expr::Call(_)
|
||||
| Expr::FormattedValue(_)
|
||||
| Expr::FString(_)
|
||||
| Expr::StringLiteral(_)
|
||||
| Expr::BytesLiteral(_)
|
||||
|
||||
@@ -29,6 +29,7 @@ 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(
|
||||
|
||||
@@ -14,7 +14,7 @@ use crate::rules::refurb::helpers::generate_method_call;
|
||||
/// dictionary.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// It's is faster and more succinct to remove all items via the `clear()`
|
||||
/// It is faster and more succinct to remove all items via the `clear()`
|
||||
/// method.
|
||||
///
|
||||
/// ## Known problems
|
||||
|
||||
120
crates/ruff_linter/src/rules/refurb/rules/hashlib_digest_hex.rs
Normal file
120
crates/ruff_linter/src/rules/refurb/rules/hashlib_digest_hex.rs
Normal file
@@ -0,0 +1,120 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -53,23 +53,17 @@ pub(crate) fn math_constant(checker: &mut Checker, literal: &ast::ExprNumberLite
|
||||
let Number::Float(value) = literal.value else {
|
||||
return;
|
||||
};
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,3 +82,33 @@ 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",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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::*;
|
||||
@@ -16,6 +17,7 @@ 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;
|
||||
|
||||
@@ -130,6 +130,9 @@ 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
|
||||
|
||||
@@ -43,6 +43,7 @@ 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`
|
||||
|
|
||||
@@ -50,6 +51,8 @@ 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`
|
||||
|
||||
@@ -63,5 +66,59 @@ 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
|
||||
|
||||
|
||||
|
||||
@@ -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 |
|
||||
19 19 | math.log(1, 2.0)
|
||||
|
||||
FURB163.py:17:1: FURB163 [*] Prefer `math.log(1)` over `math.log` with a redundant base
|
||||
|
|
||||
@@ -196,6 +196,7 @@ 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)`
|
||||
|
||||
@@ -206,8 +207,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 |
|
||||
20 20 | # Ok.
|
||||
19 19 | math.log(1, 2.0)
|
||||
20 20 | math.log(1, 10.0)
|
||||
|
||||
FURB163.py:18:1: FURB163 [*] Prefer `math.log(1)` over `math.log` with a redundant base
|
||||
|
|
||||
@@ -215,8 +216,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 |
|
||||
20 | # Ok.
|
||||
19 | math.log(1, 2.0)
|
||||
20 | math.log(1, 10.0)
|
||||
|
|
||||
= help: Replace with `math.log(1)`
|
||||
|
||||
@@ -226,8 +227,49 @@ 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 |
|
||||
20 20 | # Ok.
|
||||
21 21 | math.log2(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)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,339 @@
|
||||
---
|
||||
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()
|
||||
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
use std::fmt;
|
||||
|
||||
use ruff_python_ast::{self as ast, Expr};
|
||||
|
||||
use ast::Stmt;
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_semantic::analyze::typing;
|
||||
use ruff_python_ast::{self as ast, Expr};
|
||||
use ruff_python_semantic::{analyze::typing, Binding, SemanticModel};
|
||||
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.
|
||||
@@ -66,35 +64,34 @@ impl Violation for AsyncioDanglingTask {
|
||||
}
|
||||
|
||||
/// RUF006
|
||||
pub(crate) fn asyncio_dangling_task(checker: &mut Checker, expr: &Expr) {
|
||||
pub(crate) fn asyncio_dangling_task(expr: &Expr, semantic: &SemanticModel) -> Option<Diagnostic> {
|
||||
let Expr::Call(ast::ExprCall { func, .. }) = expr else {
|
||||
return;
|
||||
return None;
|
||||
};
|
||||
|
||||
// Ex) `asyncio.create_task(...)`
|
||||
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,
|
||||
})
|
||||
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,
|
||||
})
|
||||
{
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
return Some(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, checker.semantic()).is_some_and(|call_path| {
|
||||
if typing::resolve_assignment(value, semantic).is_some_and(|call_path| {
|
||||
matches!(call_path.as_slice(), ["asyncio", "get_running_loop"])
|
||||
}) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
return Some(Diagnostic::new(
|
||||
AsyncioDanglingTask {
|
||||
method: Method::CreateTask,
|
||||
},
|
||||
@@ -103,6 +100,28 @@ pub(crate) fn asyncio_dangling_task(checker: &mut Checker, expr: &Expr) {
|
||||
}
|
||||
}
|
||||
}
|
||||
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)]
|
||||
|
||||
@@ -53,10 +53,12 @@ impl AlwaysFixableViolation for ExplicitFStringTypeConversion {
|
||||
|
||||
/// RUF010
|
||||
pub(crate) fn explicit_f_string_type_conversion(checker: &mut Checker, f_string: &ast::FString) {
|
||||
for (index, expr) in f_string.values.iter().enumerate() {
|
||||
let Some(ast::ExprFormattedValue {
|
||||
value, conversion, ..
|
||||
}) = expr.as_formatted_value_expr()
|
||||
for (index, element) in f_string.elements.iter().enumerate() {
|
||||
let Some(ast::FStringExpressionElement {
|
||||
expression,
|
||||
conversion,
|
||||
..
|
||||
}) = element.as_expression()
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
@@ -75,7 +77,7 @@ pub(crate) fn explicit_f_string_type_conversion(checker: &mut Checker, f_string:
|
||||
range: _,
|
||||
},
|
||||
..
|
||||
}) = value.as_ref()
|
||||
}) = expression.as_ref()
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
@@ -110,7 +112,7 @@ pub(crate) fn explicit_f_string_type_conversion(checker: &mut Checker, f_string:
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(ExplicitFStringTypeConversion, value.range());
|
||||
let mut diagnostic = Diagnostic::new(ExplicitFStringTypeConversion, expression.range());
|
||||
diagnostic.try_set_fix(|| {
|
||||
convert_call_to_conversion_flag(f_string, index, checker.locator(), checker.stylist())
|
||||
});
|
||||
|
||||
@@ -24,6 +24,10 @@ 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:
|
||||
@@ -52,6 +56,10 @@ 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 {
|
||||
|
||||
@@ -635,7 +635,6 @@ impl<'stmt> BasicBlocksBuilder<'stmt> {
|
||||
| Expr::Set(_)
|
||||
| Expr::Compare(_)
|
||||
| Expr::Call(_)
|
||||
| Expr::FormattedValue(_)
|
||||
| Expr::FString(_)
|
||||
| Expr::StringLiteral(_)
|
||||
| Expr::BytesLiteral(_)
|
||||
|
||||
@@ -17,11 +17,27 @@ RUF006.py:11:5: RUF006 Store a reference to the return value of `asyncio.ensure_
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF006
|
||||
|
|
||||
|
||||
RUF006.py:79:5: RUF006 Store a reference to the return value of `asyncio.create_task`
|
||||
RUF006.py:68:12: RUF006 Store a reference to the return value of `asyncio.create_task`
|
||||
|
|
||||
77 | def f():
|
||||
78 | loop = asyncio.get_running_loop()
|
||||
79 | loop.create_task(coordinator.ws_connect()) # Error
|
||||
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
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF006
|
||||
|
|
||||
|
||||
|
||||
@@ -98,8 +98,12 @@ fn contains_message(expr: &Expr) -> bool {
|
||||
}
|
||||
}
|
||||
ast::FStringPart::FString(f_string) => {
|
||||
for value in &f_string.values {
|
||||
if contains_message(value) {
|
||||
for literal in f_string
|
||||
.elements
|
||||
.iter()
|
||||
.filter_map(|element| element.as_literal())
|
||||
{
|
||||
if literal.chars().any(char::is_whitespace) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,16 +119,21 @@ 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(version: bool) -> Self {
|
||||
if version {
|
||||
fn from(value: bool) -> Self {
|
||||
if value {
|
||||
UnsafeFixes::Enabled
|
||||
} else {
|
||||
UnsafeFixes::Disabled
|
||||
@@ -140,7 +145,7 @@ impl UnsafeFixes {
|
||||
pub fn required_applicability(&self) -> Applicability {
|
||||
match self {
|
||||
Self::Enabled => Applicability::Unsafe,
|
||||
Self::Disabled => Applicability::Safe,
|
||||
Self::Disabled | Self::Hint => Applicability::Safe,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,5 +19,20 @@ 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
|
||||
|
||||
|
||||
|
||||
@@ -4,5 +4,10 @@
|
||||
"id": "1",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["%%timeit\n", "print('hello world')"]
|
||||
"source": [
|
||||
"%%script bash\n",
|
||||
"for i in 1 2 3; do\n",
|
||||
" echo $i\n",
|
||||
"done"
|
||||
]
|
||||
}
|
||||
|
||||
11
crates/ruff_notebook/resources/test/fixtures/jupyter/cell/valid_cell_magic.json
vendored
Normal file
11
crates/ruff_notebook/resources/test/fixtures/jupyter/cell/valid_cell_magic.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"execution_count": null,
|
||||
"cell_type": "code",
|
||||
"id": "1",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%%timeit\n",
|
||||
"print('hello world')"
|
||||
]
|
||||
}
|
||||
@@ -26,6 +26,18 @@
|
||||
"%%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": {
|
||||
|
||||
@@ -22,8 +22,19 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%%timeit\n",
|
||||
"import sys"
|
||||
"%%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"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -170,7 +170,50 @@ impl Cell {
|
||||
}
|
||||
|
||||
// Detect cell magics (which operate on multiple lines).
|
||||
lines.any(|line| line.trim_start().starts_with("%%"))
|
||||
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"
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -426,6 +426,7 @@ 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")]
|
||||
|
||||
@@ -509,6 +509,41 @@ 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>>,
|
||||
@@ -562,13 +597,13 @@ impl<'a> From<ast::LiteralExpressionRef<'a>> for ComparableLiteral<'a> {
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ComparableFString<'a> {
|
||||
values: Vec<ComparableExpr<'a>>,
|
||||
elements: Vec<ComparableFStringElement<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a ast::FString> for ComparableFString<'a> {
|
||||
fn from(fstring: &'a ast::FString) -> Self {
|
||||
Self {
|
||||
values: fstring.values.iter().map(Into::into).collect(),
|
||||
elements: fstring.elements.iter().map(Into::into).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -717,11 +752,11 @@ pub struct ExprCall<'a> {
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ExprFormattedValue<'a> {
|
||||
pub struct ExprFStringExpressionElement<'a> {
|
||||
value: Box<ComparableExpr<'a>>,
|
||||
debug_text: Option<&'a ast::DebugText>,
|
||||
conversion: ast::ConversionFlag,
|
||||
format_spec: Option<Box<ComparableExpr<'a>>>,
|
||||
format_spec: Vec<ComparableFStringElement<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
@@ -813,7 +848,7 @@ pub enum ComparableExpr<'a> {
|
||||
YieldFrom(ExprYieldFrom<'a>),
|
||||
Compare(ExprCompare<'a>),
|
||||
Call(ExprCall<'a>),
|
||||
FormattedValue(ExprFormattedValue<'a>),
|
||||
FStringExpressionElement(ExprFStringExpressionElement<'a>),
|
||||
FString(ExprFString<'a>),
|
||||
StringLiteral(ExprStringLiteral<'a>),
|
||||
BytesLiteral(ExprBytesLiteral<'a>),
|
||||
@@ -975,18 +1010,6 @@ 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(),
|
||||
|
||||
@@ -23,7 +23,6 @@ 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),
|
||||
@@ -67,7 +66,6 @@ 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),
|
||||
@@ -172,11 +170,6 @@ 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)
|
||||
@@ -273,7 +266,6 @@ 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),
|
||||
@@ -317,7 +309,6 @@ 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(),
|
||||
@@ -402,3 +393,41 @@ 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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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, MatchCase, Operator, Pattern, Stmt,
|
||||
TypeParam,
|
||||
self as ast, Arguments, CmpOp, ExceptHandler, Expr, FStringElement, 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_expr(expr, func))
|
||||
}
|
||||
Expr::FString(ast::ExprFString { value, .. }) => value
|
||||
.elements()
|
||||
.any(|expr| any_over_f_string_element(expr, func)),
|
||||
Expr::NamedExpr(ast::ExprNamedExpr {
|
||||
target,
|
||||
value,
|
||||
@@ -231,14 +231,6 @@ 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)
|
||||
}
|
||||
@@ -315,6 +307,24 @@ 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 {
|
||||
@@ -1318,16 +1328,18 @@ 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.values.is_empty(),
|
||||
ast::FStringPart::FString(f_string) => f_string.elements.is_empty(),
|
||||
}) {
|
||||
Self::Falsey
|
||||
} else if value.elements().any(|expr| {
|
||||
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = &expr {
|
||||
!value.is_empty()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}) {
|
||||
} 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,
|
||||
})
|
||||
{
|
||||
Self::Truthy
|
||||
} else {
|
||||
Self::Unknown
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::visitor::preorder::PreorderVisitor;
|
||||
use crate::{
|
||||
self as ast, Alias, ArgOrKeyword, Arguments, Comprehension, Decorator, ExceptHandler, Expr,
|
||||
Keyword, MatchCase, Mod, Parameter, ParameterWithDefault, Parameters, Pattern,
|
||||
FStringElement, Keyword, MatchCase, Mod, Parameter, ParameterWithDefault, Parameters, Pattern,
|
||||
PatternArguments, PatternKeyword, Stmt, TypeParam, TypeParamParamSpec, TypeParamTypeVar,
|
||||
TypeParamTypeVarTuple, TypeParams, WithItem,
|
||||
};
|
||||
@@ -71,7 +71,6 @@ pub enum AnyNode {
|
||||
ExprYieldFrom(ast::ExprYieldFrom),
|
||||
ExprCompare(ast::ExprCompare),
|
||||
ExprCall(ast::ExprCall),
|
||||
ExprFormattedValue(ast::ExprFormattedValue),
|
||||
ExprFString(ast::ExprFString),
|
||||
ExprStringLiteral(ast::ExprStringLiteral),
|
||||
ExprBytesLiteral(ast::ExprBytesLiteral),
|
||||
@@ -88,6 +87,8 @@ 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),
|
||||
@@ -166,7 +167,8 @@ impl AnyNode {
|
||||
| AnyNode::ExprYieldFrom(_)
|
||||
| AnyNode::ExprCompare(_)
|
||||
| AnyNode::ExprCall(_)
|
||||
| AnyNode::ExprFormattedValue(_)
|
||||
| AnyNode::FStringExpressionElement(_)
|
||||
| AnyNode::FStringLiteralElement(_)
|
||||
| AnyNode::ExprFString(_)
|
||||
| AnyNode::ExprStringLiteral(_)
|
||||
| AnyNode::ExprBytesLiteral(_)
|
||||
@@ -233,7 +235,6 @@ 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)),
|
||||
@@ -278,6 +279,8 @@ impl AnyNode {
|
||||
| AnyNode::StmtContinue(_)
|
||||
| AnyNode::StmtIpyEscapeCommand(_)
|
||||
| AnyNode::ExceptHandlerExceptHandler(_)
|
||||
| AnyNode::FStringExpressionElement(_)
|
||||
| AnyNode::FStringLiteralElement(_)
|
||||
| AnyNode::PatternMatchValue(_)
|
||||
| AnyNode::PatternMatchSingleton(_)
|
||||
| AnyNode::PatternMatchSequence(_)
|
||||
@@ -356,7 +359,8 @@ impl AnyNode {
|
||||
| AnyNode::ExprYieldFrom(_)
|
||||
| AnyNode::ExprCompare(_)
|
||||
| AnyNode::ExprCall(_)
|
||||
| AnyNode::ExprFormattedValue(_)
|
||||
| AnyNode::FStringExpressionElement(_)
|
||||
| AnyNode::FStringLiteralElement(_)
|
||||
| AnyNode::ExprFString(_)
|
||||
| AnyNode::ExprStringLiteral(_)
|
||||
| AnyNode::ExprBytesLiteral(_)
|
||||
@@ -459,7 +463,8 @@ impl AnyNode {
|
||||
| AnyNode::ExprYieldFrom(_)
|
||||
| AnyNode::ExprCompare(_)
|
||||
| AnyNode::ExprCall(_)
|
||||
| AnyNode::ExprFormattedValue(_)
|
||||
| AnyNode::FStringExpressionElement(_)
|
||||
| AnyNode::FStringLiteralElement(_)
|
||||
| AnyNode::ExprFString(_)
|
||||
| AnyNode::ExprStringLiteral(_)
|
||||
| AnyNode::ExprBytesLiteral(_)
|
||||
@@ -547,7 +552,8 @@ impl AnyNode {
|
||||
| AnyNode::ExprYieldFrom(_)
|
||||
| AnyNode::ExprCompare(_)
|
||||
| AnyNode::ExprCall(_)
|
||||
| AnyNode::ExprFormattedValue(_)
|
||||
| AnyNode::FStringExpressionElement(_)
|
||||
| AnyNode::FStringLiteralElement(_)
|
||||
| AnyNode::ExprFString(_)
|
||||
| AnyNode::ExprStringLiteral(_)
|
||||
| AnyNode::ExprBytesLiteral(_)
|
||||
@@ -660,7 +666,8 @@ impl AnyNode {
|
||||
Self::ExprYieldFrom(node) => AnyNodeRef::ExprYieldFrom(node),
|
||||
Self::ExprCompare(node) => AnyNodeRef::ExprCompare(node),
|
||||
Self::ExprCall(node) => AnyNodeRef::ExprCall(node),
|
||||
Self::ExprFormattedValue(node) => AnyNodeRef::ExprFormattedValue(node),
|
||||
Self::FStringExpressionElement(node) => AnyNodeRef::FStringExpressionElement(node),
|
||||
Self::FStringLiteralElement(node) => AnyNodeRef::FStringLiteralElement(node),
|
||||
Self::ExprFString(node) => AnyNodeRef::ExprFString(node),
|
||||
Self::ExprStringLiteral(node) => AnyNodeRef::ExprStringLiteral(node),
|
||||
Self::ExprBytesLiteral(node) => AnyNodeRef::ExprBytesLiteral(node),
|
||||
@@ -2621,12 +2628,12 @@ impl AstNode for ast::ExprCall {
|
||||
visitor.visit_arguments(arguments);
|
||||
}
|
||||
}
|
||||
impl AstNode for ast::ExprFormattedValue {
|
||||
impl AstNode for ast::FStringExpressionElement {
|
||||
fn cast(kind: AnyNode) -> Option<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
if let AnyNode::ExprFormattedValue(node) = kind {
|
||||
if let AnyNode::FStringExpressionElement(node) = kind {
|
||||
Some(node)
|
||||
} else {
|
||||
None
|
||||
@@ -2634,7 +2641,7 @@ impl AstNode for ast::ExprFormattedValue {
|
||||
}
|
||||
|
||||
fn cast_ref(kind: AnyNodeRef) -> Option<&Self> {
|
||||
if let AnyNodeRef::ExprFormattedValue(node) = kind {
|
||||
if let AnyNodeRef::FStringExpressionElement(node) = kind {
|
||||
Some(node)
|
||||
} else {
|
||||
None
|
||||
@@ -2653,16 +2660,54 @@ impl AstNode for ast::ExprFormattedValue {
|
||||
where
|
||||
V: PreorderVisitor<'a> + ?Sized,
|
||||
{
|
||||
let ast::ExprFormattedValue {
|
||||
value, format_spec, ..
|
||||
let ast::FStringExpressionElement {
|
||||
expression,
|
||||
format_spec,
|
||||
..
|
||||
} = self;
|
||||
visitor.visit_expr(value);
|
||||
visitor.visit_expr(expression);
|
||||
|
||||
if let Some(expr) = format_spec {
|
||||
visitor.visit_format_spec(expr);
|
||||
if let Some(format_spec) = format_spec {
|
||||
for spec_part in &format_spec.elements {
|
||||
visitor.visit_f_string_element(spec_part);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
@@ -4339,10 +4384,10 @@ impl AstNode for ast::FString {
|
||||
where
|
||||
V: PreorderVisitor<'a> + ?Sized,
|
||||
{
|
||||
let ast::FString { values, range: _ } = self;
|
||||
let ast::FString { elements, range: _ } = self;
|
||||
|
||||
for expr in values {
|
||||
visitor.visit_expr(expr);
|
||||
for fstring_element in elements {
|
||||
visitor.visit_f_string_element(fstring_element);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4467,7 +4512,6 @@ 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),
|
||||
@@ -4496,6 +4540,15 @@ 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 {
|
||||
@@ -4789,9 +4842,15 @@ impl From<ast::ExprCall> for AnyNode {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ast::ExprFormattedValue> for AnyNode {
|
||||
fn from(node: ast::ExprFormattedValue) -> Self {
|
||||
AnyNode::ExprFormattedValue(node)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5089,7 +5148,8 @@ impl Ranged for AnyNode {
|
||||
AnyNode::ExprYieldFrom(node) => node.range(),
|
||||
AnyNode::ExprCompare(node) => node.range(),
|
||||
AnyNode::ExprCall(node) => node.range(),
|
||||
AnyNode::ExprFormattedValue(node) => node.range(),
|
||||
AnyNode::FStringExpressionElement(node) => node.range(),
|
||||
AnyNode::FStringLiteralElement(node) => node.range(),
|
||||
AnyNode::ExprFString(node) => node.range(),
|
||||
AnyNode::ExprStringLiteral(node) => node.range(),
|
||||
AnyNode::ExprBytesLiteral(node) => node.range(),
|
||||
@@ -5184,7 +5244,8 @@ pub enum AnyNodeRef<'a> {
|
||||
ExprYieldFrom(&'a ast::ExprYieldFrom),
|
||||
ExprCompare(&'a ast::ExprCompare),
|
||||
ExprCall(&'a ast::ExprCall),
|
||||
ExprFormattedValue(&'a ast::ExprFormattedValue),
|
||||
FStringExpressionElement(&'a ast::FStringExpressionElement),
|
||||
FStringLiteralElement(&'a ast::FStringLiteralElement),
|
||||
ExprFString(&'a ast::ExprFString),
|
||||
ExprStringLiteral(&'a ast::ExprStringLiteral),
|
||||
ExprBytesLiteral(&'a ast::ExprBytesLiteral),
|
||||
@@ -5278,7 +5339,8 @@ 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::ExprFormattedValue(node) => NonNull::from(*node).cast(),
|
||||
AnyNodeRef::FStringExpressionElement(node) => NonNull::from(*node).cast(),
|
||||
AnyNodeRef::FStringLiteralElement(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(),
|
||||
@@ -5378,7 +5440,8 @@ impl<'a> AnyNodeRef<'a> {
|
||||
AnyNodeRef::ExprYieldFrom(_) => NodeKind::ExprYieldFrom,
|
||||
AnyNodeRef::ExprCompare(_) => NodeKind::ExprCompare,
|
||||
AnyNodeRef::ExprCall(_) => NodeKind::ExprCall,
|
||||
AnyNodeRef::ExprFormattedValue(_) => NodeKind::ExprFormattedValue,
|
||||
AnyNodeRef::FStringExpressionElement(_) => NodeKind::FStringExpressionElement,
|
||||
AnyNodeRef::FStringLiteralElement(_) => NodeKind::FStringLiteralElement,
|
||||
AnyNodeRef::ExprFString(_) => NodeKind::ExprFString,
|
||||
AnyNodeRef::ExprStringLiteral(_) => NodeKind::ExprStringLiteral,
|
||||
AnyNodeRef::ExprBytesLiteral(_) => NodeKind::ExprBytesLiteral,
|
||||
@@ -5473,7 +5536,8 @@ impl<'a> AnyNodeRef<'a> {
|
||||
| AnyNodeRef::ExprYieldFrom(_)
|
||||
| AnyNodeRef::ExprCompare(_)
|
||||
| AnyNodeRef::ExprCall(_)
|
||||
| AnyNodeRef::ExprFormattedValue(_)
|
||||
| AnyNodeRef::FStringExpressionElement(_)
|
||||
| AnyNodeRef::FStringLiteralElement(_)
|
||||
| AnyNodeRef::ExprFString(_)
|
||||
| AnyNodeRef::ExprStringLiteral(_)
|
||||
| AnyNodeRef::ExprBytesLiteral(_)
|
||||
@@ -5540,7 +5604,6 @@ impl<'a> AnyNodeRef<'a> {
|
||||
| AnyNodeRef::ExprYieldFrom(_)
|
||||
| AnyNodeRef::ExprCompare(_)
|
||||
| AnyNodeRef::ExprCall(_)
|
||||
| AnyNodeRef::ExprFormattedValue(_)
|
||||
| AnyNodeRef::ExprFString(_)
|
||||
| AnyNodeRef::ExprStringLiteral(_)
|
||||
| AnyNodeRef::ExprBytesLiteral(_)
|
||||
@@ -5585,6 +5648,8 @@ impl<'a> AnyNodeRef<'a> {
|
||||
| AnyNodeRef::StmtContinue(_)
|
||||
| AnyNodeRef::StmtIpyEscapeCommand(_)
|
||||
| AnyNodeRef::ExceptHandlerExceptHandler(_)
|
||||
| AnyNodeRef::FStringExpressionElement(_)
|
||||
| AnyNodeRef::FStringLiteralElement(_)
|
||||
| AnyNodeRef::PatternMatchValue(_)
|
||||
| AnyNodeRef::PatternMatchSingleton(_)
|
||||
| AnyNodeRef::PatternMatchSequence(_)
|
||||
@@ -5662,7 +5727,8 @@ impl<'a> AnyNodeRef<'a> {
|
||||
| AnyNodeRef::ExprYieldFrom(_)
|
||||
| AnyNodeRef::ExprCompare(_)
|
||||
| AnyNodeRef::ExprCall(_)
|
||||
| AnyNodeRef::ExprFormattedValue(_)
|
||||
| AnyNodeRef::FStringExpressionElement(_)
|
||||
| AnyNodeRef::FStringLiteralElement(_)
|
||||
| AnyNodeRef::ExprFString(_)
|
||||
| AnyNodeRef::ExprStringLiteral(_)
|
||||
| AnyNodeRef::ExprBytesLiteral(_)
|
||||
@@ -5765,7 +5831,8 @@ impl<'a> AnyNodeRef<'a> {
|
||||
| AnyNodeRef::ExprYieldFrom(_)
|
||||
| AnyNodeRef::ExprCompare(_)
|
||||
| AnyNodeRef::ExprCall(_)
|
||||
| AnyNodeRef::ExprFormattedValue(_)
|
||||
| AnyNodeRef::FStringExpressionElement(_)
|
||||
| AnyNodeRef::FStringLiteralElement(_)
|
||||
| AnyNodeRef::ExprFString(_)
|
||||
| AnyNodeRef::ExprStringLiteral(_)
|
||||
| AnyNodeRef::ExprBytesLiteral(_)
|
||||
@@ -5853,7 +5920,8 @@ impl<'a> AnyNodeRef<'a> {
|
||||
| AnyNodeRef::ExprYieldFrom(_)
|
||||
| AnyNodeRef::ExprCompare(_)
|
||||
| AnyNodeRef::ExprCall(_)
|
||||
| AnyNodeRef::ExprFormattedValue(_)
|
||||
| AnyNodeRef::FStringExpressionElement(_)
|
||||
| AnyNodeRef::FStringLiteralElement(_)
|
||||
| AnyNodeRef::ExprFString(_)
|
||||
| AnyNodeRef::ExprStringLiteral(_)
|
||||
| AnyNodeRef::ExprBytesLiteral(_)
|
||||
@@ -5975,7 +6043,8 @@ 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::ExprFormattedValue(node) => node.visit_preorder(visitor),
|
||||
AnyNodeRef::FStringExpressionElement(node) => node.visit_preorder(visitor),
|
||||
AnyNodeRef::FStringLiteralElement(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),
|
||||
@@ -6354,9 +6423,15 @@ impl<'a> From<&'a ast::ExprCall> for AnyNodeRef<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a ast::ExprFormattedValue> for AnyNodeRef<'a> {
|
||||
fn from(node: &'a ast::ExprFormattedValue) -> Self {
|
||||
AnyNodeRef::ExprFormattedValue(node)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6615,7 +6690,6 @@ 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),
|
||||
@@ -6644,6 +6718,15 @@ 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 {
|
||||
@@ -6772,7 +6855,8 @@ impl Ranged for AnyNodeRef<'_> {
|
||||
AnyNodeRef::ExprYieldFrom(node) => node.range(),
|
||||
AnyNodeRef::ExprCompare(node) => node.range(),
|
||||
AnyNodeRef::ExprCall(node) => node.range(),
|
||||
AnyNodeRef::ExprFormattedValue(node) => node.range(),
|
||||
AnyNodeRef::FStringExpressionElement(node) => node.range(),
|
||||
AnyNodeRef::FStringLiteralElement(node) => node.range(),
|
||||
AnyNodeRef::ExprFString(node) => node.range(),
|
||||
AnyNodeRef::ExprStringLiteral(node) => node.range(),
|
||||
AnyNodeRef::ExprBytesLiteral(node) => node.range(),
|
||||
@@ -6869,7 +6953,8 @@ pub enum NodeKind {
|
||||
ExprYieldFrom,
|
||||
ExprCompare,
|
||||
ExprCall,
|
||||
ExprFormattedValue,
|
||||
FStringExpressionElement,
|
||||
FStringLiteralElement,
|
||||
ExprFString,
|
||||
ExprStringLiteral,
|
||||
ExprBytesLiteral,
|
||||
|
||||
@@ -590,8 +590,6 @@ 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")]
|
||||
@@ -919,19 +917,51 @@ impl From<ExprCall> for Expr {
|
||||
}
|
||||
}
|
||||
|
||||
/// See also [FormattedValue](https://docs.python.org/3/library/ast.html#ast.FormattedValue)
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ExprFormattedValue {
|
||||
pub struct FStringFormatSpec {
|
||||
pub range: TextRange,
|
||||
pub value: Box<Expr>,
|
||||
pub debug_text: Option<DebugText>,
|
||||
pub conversion: ConversionFlag,
|
||||
pub format_spec: Option<Box<Expr>>,
|
||||
pub elements: Vec<FStringElement>,
|
||||
}
|
||||
|
||||
impl From<ExprFormattedValue> for Expr {
|
||||
fn from(payload: ExprFormattedValue) -> Self {
|
||||
Expr::FormattedValue(payload)
|
||||
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 range: TextRange,
|
||||
pub expression: Box<Expr>,
|
||||
pub debug_text: Option<DebugText>,
|
||||
pub conversion: ConversionFlag,
|
||||
pub format_spec: Option<Box<FStringFormatSpec>>,
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1064,7 +1094,7 @@ impl FStringValue {
|
||||
self.parts().filter_map(|part| part.as_f_string())
|
||||
}
|
||||
|
||||
/// Returns an iterator over all the f-string elements contained in this value.
|
||||
/// Returns an iterator over all the [`FStringElement`] 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,
|
||||
@@ -1075,8 +1105,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 = &Expr> {
|
||||
self.f_strings().flat_map(|fstring| fstring.values.iter())
|
||||
pub fn elements(&self) -> impl Iterator<Item = &FStringElement> {
|
||||
self.f_strings().flat_map(|fstring| fstring.elements.iter())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1113,7 +1143,7 @@ impl Ranged for FStringPart {
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct FString {
|
||||
pub range: TextRange,
|
||||
pub values: Vec<Expr>,
|
||||
pub elements: Vec<FStringElement>,
|
||||
}
|
||||
|
||||
impl Ranged for FString {
|
||||
@@ -1132,6 +1162,21 @@ 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)]
|
||||
@@ -3483,11 +3528,6 @@ 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
|
||||
@@ -3553,7 +3593,6 @@ 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
Reference in New Issue
Block a user