Compare commits
163 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd638a2e54 | ||
|
|
fa1459d56e | ||
|
|
d93c5811ea | ||
|
|
06e426f509 | ||
|
|
6eb014b3b2 | ||
|
|
d9fd78d907 | ||
|
|
37df07d2e0 | ||
|
|
d5c65b5f1b | ||
|
|
cdc4e86158 | ||
|
|
50ec6d3b0f | ||
|
|
a6eb60cdd5 | ||
|
|
90c04b9cff | ||
|
|
b701cca779 | ||
|
|
ce8953442d | ||
|
|
7d4e513a82 | ||
|
|
35f7f7b66d | ||
|
|
6e02405bd6 | ||
|
|
b657468346 | ||
|
|
f72ed255e5 | ||
|
|
7e9dea0027 | ||
|
|
9545958ad8 | ||
|
|
41faa335d1 | ||
|
|
4cfa350112 | ||
|
|
41f163fc8d | ||
|
|
d21dd994e6 | ||
|
|
6f5a6b8c8b | ||
|
|
35606d7b05 | ||
|
|
3ad257cfea | ||
|
|
b39f960cd1 | ||
|
|
c297d46899 | ||
|
|
d6a100028c | ||
|
|
35d4e03f2a | ||
|
|
41e77bb01d | ||
|
|
2ff3dd5fbe | ||
|
|
0f0e7a521a | ||
|
|
b75663be6d | ||
|
|
4d3d04ee61 | ||
|
|
87422ba362 | ||
|
|
c1d2976fff | ||
|
|
13281cd9ca | ||
|
|
e53652779d | ||
|
|
db4c611c6f | ||
|
|
c25be31eb1 | ||
|
|
a7c533634d | ||
|
|
cfa6883431 | ||
|
|
216aa929af | ||
|
|
9e45424ed6 | ||
|
|
db7f16e276 | ||
|
|
a10a500a26 | ||
|
|
b9fef7cef7 | ||
|
|
34294ccc00 | ||
|
|
a934d01bdb | ||
|
|
0dd590f137 | ||
|
|
909a5c3253 | ||
|
|
5c987874c4 | ||
|
|
0cfe4f9c69 | ||
|
|
6a369e4a30 | ||
|
|
6f97e2c457 | ||
|
|
bebd412469 | ||
|
|
cd1f57b713 | ||
|
|
a0912deb2b | ||
|
|
50ee14a418 | ||
|
|
f5adbbebc5 | ||
|
|
c88e05dc1b | ||
|
|
d658bfc024 | ||
|
|
b0d72c47b4 | ||
|
|
8195873cdf | ||
|
|
bf8108469f | ||
|
|
a2277cfeba | ||
|
|
180541a924 | ||
|
|
34664a0ca0 | ||
|
|
e081455b06 | ||
|
|
4f18fa6733 | ||
|
|
6088a36cd3 | ||
|
|
66a162fa40 | ||
|
|
e6722f92ed | ||
|
|
750c28868f | ||
|
|
5157f584ab | ||
|
|
1c01ec21cb | ||
|
|
879512742f | ||
|
|
a919041dda | ||
|
|
059601d968 | ||
|
|
2ec1701543 | ||
|
|
370c3a5daf | ||
|
|
fdcb78fd8c | ||
|
|
2a744d24e5 | ||
|
|
cc30738148 | ||
|
|
147c6ff1db | ||
|
|
036380e6a8 | ||
|
|
b6587e51ee | ||
|
|
1bc37110d4 | ||
|
|
28acdb76cf | ||
|
|
7b09972c97 | ||
|
|
f8d46d09ef | ||
|
|
294cd95c54 | ||
|
|
d8e709648d | ||
|
|
52cc4d6537 | ||
|
|
08e9d12137 | ||
|
|
39fdc71b49 | ||
|
|
6b0736cf4b | ||
|
|
58269a918a | ||
|
|
9168a12679 | ||
|
|
cb971d3a48 | ||
|
|
57a5071b4e | ||
|
|
976fe364d4 | ||
|
|
028c7855b2 | ||
|
|
e5179f67fd | ||
|
|
c9c199dbca | ||
|
|
70e378b736 | ||
|
|
ca49b00e55 | ||
|
|
f661c90bd7 | ||
|
|
5a84df293f | ||
|
|
23d9309111 | ||
|
|
98ea94fdb7 | ||
|
|
746e1d3436 | ||
|
|
016ff01a04 | ||
|
|
298498e934 | ||
|
|
3ef1c2e303 | ||
|
|
ac028cd9f8 | ||
|
|
c0eb5c28d1 | ||
|
|
a77b4566e4 | ||
|
|
860993187e | ||
|
|
58d4e00604 | ||
|
|
2d95912699 | ||
|
|
4f927fbacc | ||
|
|
2e41301520 | ||
|
|
3179fc110d | ||
|
|
03ae0118b7 | ||
|
|
05176890ee | ||
|
|
849b947b3e | ||
|
|
c314e10e54 | ||
|
|
9eda286dcd | ||
|
|
65a3461519 | ||
|
|
1b8d2df3bf | ||
|
|
179ead0157 | ||
|
|
d451c7a506 | ||
|
|
502ce80c91 | ||
|
|
49d22d8fe2 | ||
|
|
08e0b76587 | ||
|
|
f7515739ac | ||
|
|
53e810ed3e | ||
|
|
66a195f805 | ||
|
|
b8483975a4 | ||
|
|
4dd2032687 | ||
|
|
c6c15d5cf9 | ||
|
|
2bf7b35268 | ||
|
|
6d1adc85fc | ||
|
|
02285c18d1 | ||
|
|
8120d7c974 | ||
|
|
c858804ed4 | ||
|
|
b9d075c252 | ||
|
|
7627e840c9 | ||
|
|
3c03e2cb2e | ||
|
|
aeae63b7ea | ||
|
|
7be17c5f1e | ||
|
|
6128346b08 | ||
|
|
1705574e75 | ||
|
|
15f65fa8d6 | ||
|
|
d1cf0ee52b | ||
|
|
32520ff07f | ||
|
|
3659236580 | ||
|
|
dde69d50b5 | ||
|
|
67198ce7b3 |
6
.gitattributes
vendored
Normal file
6
.gitattributes
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
* text=auto eol=lf
|
||||
|
||||
crates/ruff/resources/test/fixtures/isort/line_ending_crlf.py text eol=crlf
|
||||
crates/ruff/resources/test/fixtures/pycodestyle/W605_1.py text eol=crlf
|
||||
|
||||
ruff.schema.json linguist-generated=true text=auto eol=lf
|
||||
3
.github/workflows/docs.yaml
vendored
3
.github/workflows/docs.yaml
vendored
@@ -13,6 +13,9 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: "Install dependencies"
|
||||
run: |
|
||||
pip install -r docs/requirements.txt
|
||||
|
||||
49
.github/workflows/flake8-to-ruff.yaml
vendored
49
.github/workflows/flake8-to-ruff.yaml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Build wheels - x86_64"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: x86_64
|
||||
args: --release --out dist --sdist -m ./${{ env.CRATE_NAME }}/Cargo.toml
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Build wheels - universal2"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
args: --release --universal2 --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
|
||||
- name: "Install built wheel - universal2"
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
args: --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
|
||||
@@ -102,7 +102,7 @@ jobs:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: auto
|
||||
@@ -128,7 +128,7 @@ jobs:
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: auto
|
||||
@@ -166,7 +166,7 @@ jobs:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: musllinux_1_2
|
||||
@@ -201,7 +201,7 @@ jobs:
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.platform.target }}
|
||||
manylinux: musllinux_1_2
|
||||
@@ -222,40 +222,6 @@ jobs:
|
||||
name: wheels
|
||||
path: dist
|
||||
|
||||
pypy:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
target: [x86_64, aarch64]
|
||||
python-version:
|
||||
- "3.7"
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
exclude:
|
||||
- os: macos-latest
|
||||
target: aarch64
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: pypy${{ matrix.python-version }}
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: auto
|
||||
args: --release --out dist -i pypy${{ matrix.python-version }} -m ./${{ env.CRATE_NAME }}/Cargo.toml
|
||||
- name: "Install built wheel"
|
||||
if: matrix.target == 'x86_64'
|
||||
run: |
|
||||
pip install dist/${{ env.CRATE_NAME }}-*.whl --force-reinstall
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
path: dist
|
||||
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
@@ -267,7 +233,6 @@ jobs:
|
||||
- linux-cross
|
||||
- musllinux
|
||||
- musllinux-cross
|
||||
- pypy
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
|
||||
181
.github/workflows/ruff.yaml
vendored
181
.github/workflows/ruff.yaml
vendored
@@ -2,9 +2,8 @@ name: "[ruff] Release"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
tags:
|
||||
- '**'
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -30,7 +29,7 @@ jobs:
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels - x86_64"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: x86_64
|
||||
args: --release --out dist --sdist
|
||||
@@ -42,6 +41,19 @@ jobs:
|
||||
with:
|
||||
name: wheels
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-x86_64-apple-darwin.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/x86_64-apple-darwin/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: binaries
|
||||
path: |
|
||||
*.tar.gz
|
||||
*.sha256
|
||||
|
||||
macos-universal:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
@@ -53,7 +65,7 @@ jobs:
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels - universal2"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
args: --release --universal2 --out dist
|
||||
- name: "Install built wheel - universal2"
|
||||
@@ -64,26 +76,45 @@ jobs:
|
||||
with:
|
||||
name: wheels
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-aarch64-apple-darwin.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/aarch64-apple-darwin/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: binaries
|
||||
path: |
|
||||
*.tar.gz
|
||||
*.sha256
|
||||
|
||||
windows:
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target: [x64, x86]
|
||||
platform:
|
||||
- target: x86_64-pc-windows-msvc
|
||||
arch: x64
|
||||
- target: i686-pc-windows-msvc
|
||||
arch: x86
|
||||
- target: aarch64-pc-windows-msvc
|
||||
arch: x64
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: ${{ matrix.target }}
|
||||
architecture: ${{ matrix.platform.arch }}
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
target: ${{ matrix.platform.target }}
|
||||
args: --release --out dist
|
||||
- name: "Install built wheel"
|
||||
if: ${{ !startsWith(matrix.platform.target, 'aarch64') }}
|
||||
shell: bash
|
||||
run: |
|
||||
python -m pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
|
||||
@@ -92,12 +123,27 @@ jobs:
|
||||
with:
|
||||
name: wheels
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
shell: bash
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-${{ matrix.platform.target }}.zip
|
||||
7z a $ARCHIVE_FILE ./target/${{ matrix.platform.target }}/release/ruff.exe
|
||||
sha256sum $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: binaries
|
||||
path: |
|
||||
*.zip
|
||||
*.sha256
|
||||
|
||||
linux:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target: [x86_64, i686]
|
||||
target:
|
||||
- x86_64-unknown-linux-gnu
|
||||
- i686-unknown-linux-gnu
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
@@ -107,13 +153,13 @@ jobs:
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: auto
|
||||
args: --release --out dist
|
||||
- name: "Install built wheel"
|
||||
if: matrix.target == 'x86_64'
|
||||
if: ${{ startsWith(matrix.target, 'x86_64') }}
|
||||
run: |
|
||||
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
|
||||
- name: "Upload wheels"
|
||||
@@ -121,12 +167,34 @@ jobs:
|
||||
with:
|
||||
name: wheels
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-${{ matrix.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: binaries
|
||||
path: |
|
||||
*.tar.gz
|
||||
*.sha256
|
||||
|
||||
linux-cross:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target: [aarch64, armv7, s390x, ppc64le, ppc64]
|
||||
platform:
|
||||
- target: aarch64-unknown-linux-gnu
|
||||
arch: aarch64
|
||||
- target: armv7-unknown-linux-gnueabihf
|
||||
arch: armv7
|
||||
- target: s390x-unknown-linux-gnu
|
||||
arch: s390x
|
||||
- target: powerpc64le-unknown-linux-gnu
|
||||
arch: ppc64le
|
||||
- target: powerpc64-unknown-linux-gnu
|
||||
arch: ppc64
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
@@ -135,16 +203,16 @@ jobs:
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
target: ${{ matrix.platform.target }}
|
||||
manylinux: auto
|
||||
args: --release --out dist
|
||||
- uses: uraimo/run-on-arch-action@v2.5.0
|
||||
if: matrix.target != 'ppc64'
|
||||
if: matrix.platform.arch != 'ppc64'
|
||||
name: Install built wheel
|
||||
with:
|
||||
arch: ${{ matrix.target }}
|
||||
arch: ${{ matrix.platform.arch }}
|
||||
distro: ubuntu20.04
|
||||
githubToken: ${{ github.token }}
|
||||
install: |
|
||||
@@ -158,6 +226,18 @@ jobs:
|
||||
with:
|
||||
name: wheels
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-${{ matrix.platform.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.platform.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: binaries
|
||||
path: |
|
||||
*.tar.gz
|
||||
*.sha256
|
||||
|
||||
musllinux:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -175,7 +255,7 @@ jobs:
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: musllinux_1_2
|
||||
@@ -194,6 +274,18 @@ jobs:
|
||||
with:
|
||||
name: wheels
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-${{ matrix.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: binaries
|
||||
path: |
|
||||
*.tar.gz
|
||||
*.sha256
|
||||
|
||||
musllinux-cross:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -212,7 +304,7 @@ jobs:
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.platform.target }}
|
||||
manylinux: musllinux_1_2
|
||||
@@ -232,42 +324,18 @@ jobs:
|
||||
with:
|
||||
name: wheels
|
||||
path: dist
|
||||
|
||||
pypy:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
target: [x86_64, aarch64]
|
||||
python-version:
|
||||
- "3.7"
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
exclude:
|
||||
- os: macos-latest
|
||||
target: aarch64
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: pypy${{ matrix.python-version }}
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels"
|
||||
uses: messense/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: auto
|
||||
args: --release --out dist -i pypy${{ matrix.python-version }}
|
||||
- name: "Install built wheel"
|
||||
if: matrix.target == 'x86_64'
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
|
||||
- name: "Upload wheels"
|
||||
ARCHIVE_FILE=ruff-${{ matrix.platform.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.platform.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
path: dist
|
||||
name: binaries
|
||||
path: |
|
||||
*.tar.gz
|
||||
*.sha256
|
||||
|
||||
release:
|
||||
name: Release
|
||||
@@ -280,7 +348,6 @@ jobs:
|
||||
- linux-cross
|
||||
- musllinux
|
||||
- musllinux-cross
|
||||
- pypy
|
||||
if: "startsWith(github.ref, 'refs/tags/')"
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
@@ -297,3 +364,11 @@ jobs:
|
||||
- name: "Update pre-commit mirror"
|
||||
run: |
|
||||
curl -X POST -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${{ secrets.RUFF_PRE_COMMIT_PAT }}" -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/charliermarsh/ruff-pre-commit/dispatches --data '{"event_type": "pypi_release"}'
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: binaries
|
||||
path: binaries
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: binaries/*
|
||||
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -1,10 +1,6 @@
|
||||
# Local cache
|
||||
.ruff_cache
|
||||
crates/ruff/resources/test/cpython
|
||||
docs/*
|
||||
!docs/rules
|
||||
!docs/assets
|
||||
!docs/requirements.txt
|
||||
mkdocs.yml
|
||||
.overrides
|
||||
|
||||
@@ -191,3 +187,7 @@ cython_debug/
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/
|
||||
|
||||
# VIM
|
||||
.*.sw?
|
||||
.sw?
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
fail_fast: true
|
||||
repos:
|
||||
- repo: https://github.com/abravalheri/validate-pyproject
|
||||
rev: v0.10.1
|
||||
@@ -12,6 +13,7 @@ repos:
|
||||
- --disable
|
||||
- MD013 # line-length
|
||||
- MD033 # no-inline-html
|
||||
- MD041 # first-line-h1
|
||||
- --
|
||||
|
||||
- repo: local
|
||||
@@ -28,11 +30,15 @@ repos:
|
||||
pass_filenames: false
|
||||
- id: ruff
|
||||
name: ruff
|
||||
entry: cargo run -- --no-cache --fix
|
||||
entry: cargo run -p ruff_cli -- check --no-cache --force-exclude --fix --exit-non-zero-on-fix
|
||||
language: rust
|
||||
types_or: [python, pyi]
|
||||
require_serial: true
|
||||
exclude: ^crates/ruff/resources
|
||||
exclude: |
|
||||
(?x)^(
|
||||
crates/ruff/resources/.*|
|
||||
crates/ruff_python_formatter/resources/.*
|
||||
)$
|
||||
- id: dev-generate-all
|
||||
name: dev-generate-all
|
||||
entry: cargo dev generate-all
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
- [Our Pledge](#our-pledge)
|
||||
- [Our Standards](#our-standards)
|
||||
- [Enforcement Responsibilities](#enforcement-responsibilities)
|
||||
- [Scope](#scope)
|
||||
- [Enforcement](#enforcement)
|
||||
- [Enforcement Guidelines](#enforcement-guidelines)
|
||||
- [1. Correction](#1-correction)
|
||||
- [2. Warning](#2-warning)
|
||||
- [3. Temporary Ban](#3-temporary-ban)
|
||||
- [4. Permanent Ban](#4-permanent-ban)
|
||||
- [Attribution](#attribution)
|
||||
* [Our Pledge](#our-pledge)
|
||||
* [Our Standards](#our-standards)
|
||||
* [Enforcement Responsibilities](#enforcement-responsibilities)
|
||||
* [Scope](#scope)
|
||||
* [Enforcement](#enforcement)
|
||||
* [Enforcement Guidelines](#enforcement-guidelines)
|
||||
* [1. Correction](#1-correction)
|
||||
* [2. Warning](#2-warning)
|
||||
* [3. Temporary Ban](#3-temporary-ban)
|
||||
* [4. Permanent Ban](#4-permanent-ban)
|
||||
* [Attribution](#attribution)
|
||||
|
||||
## Our Pledge
|
||||
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
|
||||
Welcome! We're happy to have you here. Thank you in advance for your contribution to Ruff.
|
||||
|
||||
- [The Basics](#the-basics)
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [Development](#development)
|
||||
- [Project Structure](#project-structure)
|
||||
- [Example: Adding a new lint rule](#example-adding-a-new-lint-rule)
|
||||
- [Rule naming convention](#rule-naming-convention)
|
||||
- [Example: Adding a new configuration option](#example-adding-a-new-configuration-option)
|
||||
- [MkDocs](#mkdocs)
|
||||
- [Release Process](#release-process)
|
||||
- [Benchmarks](#benchmarks)
|
||||
* [The Basics](#the-basics)
|
||||
* [Prerequisites](#prerequisites)
|
||||
* [Development](#development)
|
||||
* [Project Structure](#project-structure)
|
||||
* [Example: Adding a new lint rule](#example-adding-a-new-lint-rule)
|
||||
* [Rule naming convention](#rule-naming-convention)
|
||||
* [Example: Adding a new configuration option](#example-adding-a-new-configuration-option)
|
||||
* [MkDocs](#mkdocs)
|
||||
* [Release Process](#release-process)
|
||||
* [Benchmarks](#benchmarks)
|
||||
|
||||
## The Basics
|
||||
|
||||
@@ -29,13 +29,10 @@ If you're looking for a place to start, we recommend implementing a new lint rul
|
||||
pattern-match against the examples in the existing codebase. Many lint rules are inspired by
|
||||
existing Python plugins, which can be used as a reference implementation.
|
||||
|
||||
As a concrete example: consider taking on one of the rules from the [`tryceratops`](https://github.com/charliermarsh/ruff/issues/2056)
|
||||
plugin, and looking to the originating [Python source](https://github.com/guilatrova/tryceratops)
|
||||
As a concrete example: consider taking on one of the rules from the [`flake8-pyi`](https://github.com/charliermarsh/ruff/issues/848)
|
||||
plugin, and looking to the originating [Python source](https://github.com/PyCQA/flake8-pyi)
|
||||
for guidance.
|
||||
|
||||
Alternatively, we've started work on the [`flake8-pyi`](https://github.com/charliermarsh/ruff/issues/848)
|
||||
plugin (see the [Python source](https://github.com/PyCQA/flake8-pyi)) -- another good place to start.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Ruff is written in Rust. You'll need to install the
|
||||
@@ -52,7 +49,7 @@ cargo install cargo-insta
|
||||
After cloning the repository, run Ruff locally with:
|
||||
|
||||
```shell
|
||||
cargo run check /path/to/file.py --no-cache
|
||||
cargo run -p ruff_cli -- check /path/to/file.py --no-cache
|
||||
```
|
||||
|
||||
Prior to opening a pull request, ensure that your code has been auto-formatted,
|
||||
@@ -94,12 +91,12 @@ The vast majority of the code, including all lint rules, lives in the `ruff` cra
|
||||
|
||||
At time of writing, the repository includes the following crates:
|
||||
|
||||
- `crates/ruff`: library crate containing all lint rules and the core logic for running them.
|
||||
- `crates/ruff_cli`: binary crate containing Ruff's command-line interface.
|
||||
- `crates/ruff_dev`: binary crate containing utilities used in the development of Ruff itself (e.g., `cargo dev generate-all`).
|
||||
- `crates/ruff_macros`: library crate containing macros used by Ruff.
|
||||
- `crates/ruff_python`: library crate implementing Python-specific functionality (e.g., lists of standard library modules by versionb).
|
||||
- `crates/flake8_to_ruff`: binary crate for generating Ruff configuration from Flake8 configuration.
|
||||
* `crates/ruff`: library crate containing all lint rules and the core logic for running them.
|
||||
* `crates/ruff_cli`: binary crate containing Ruff's command-line interface.
|
||||
* `crates/ruff_dev`: binary crate containing utilities used in the development of Ruff itself (e.g., `cargo dev generate-all`).
|
||||
* `crates/ruff_macros`: library crate containing macros used by Ruff.
|
||||
* `crates/ruff_python`: library crate implementing Python-specific functionality (e.g., lists of standard library modules by versionb).
|
||||
* `crates/flake8_to_ruff`: binary crate for generating Ruff configuration from Flake8 configuration.
|
||||
|
||||
### Example: Adding a new lint rule
|
||||
|
||||
@@ -135,7 +132,7 @@ contain a variety of violations and non-violations designed to evaluate and demo
|
||||
of your lint rule.
|
||||
|
||||
Run `cargo dev generate-all` to generate the code for your new fixture. Then run Ruff
|
||||
locally with (e.g.) `cargo run check crates/ruff/resources/test/fixtures/pycodestyle/E402.py --no-cache --select E402`.
|
||||
locally with (e.g.) `cargo run -p ruff_cli -- check crates/ruff/resources/test/fixtures/pycodestyle/E402.py --no-cache --select E402`.
|
||||
|
||||
Once you're satisfied with the output, codify the behavior as a snapshot test by adding a new
|
||||
`test_case` macro in the relevant `crates/ruff/src/[linter]/mod.rs` file. Then, run `cargo test --all`.
|
||||
@@ -146,7 +143,7 @@ Finally, regenerate the documentation and generated code with `cargo dev generat
|
||||
|
||||
#### Rule naming convention
|
||||
|
||||
The rule name should make sense when read as "allow *rule-name*" or "allow *rule-name* items".
|
||||
The rule name should make sense when read as "allow _rule-name_" or "allow _rule-name_ items".
|
||||
|
||||
This implies that rule names:
|
||||
|
||||
@@ -186,14 +183,19 @@ Finally, regenerate the documentation and generated code with `cargo dev generat
|
||||
To preview any changes to the documentation locally:
|
||||
|
||||
1. Install MkDocs and Material for MkDocs with:
|
||||
|
||||
```shell
|
||||
pip install -r docs/requirements.txt
|
||||
```
|
||||
|
||||
2. Generate the MkDocs site with:
|
||||
|
||||
```shell
|
||||
python scripts/generate_mkdocs.py
|
||||
```
|
||||
|
||||
3. Run the development server with:
|
||||
|
||||
```shell
|
||||
mkdocs serve
|
||||
```
|
||||
|
||||
896
Cargo.lock
generated
896
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
21
Cargo.toml
21
Cargo.toml
@@ -1,11 +1,26 @@
|
||||
[workspace]
|
||||
members = ["crates/*"]
|
||||
default-members = ["crates/ruff", "crates/ruff_cli"]
|
||||
|
||||
[workspace.package]
|
||||
edition = "2021"
|
||||
rust-version = "1.65.0"
|
||||
|
||||
[workspace.dependencies]
|
||||
anyhow = { version = "1.0.66" }
|
||||
clap = { version = "4.0.1", features = ["derive"] }
|
||||
itertools = { version = "0.10.5" }
|
||||
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a8725d161fe8b3ed73a6758b21e177" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "61b48f108982d865524f86624a9d5bc2ae3bccef" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "61b48f108982d865524f86624a9d5bc2ae3bccef" }
|
||||
once_cell = { version = "1.16.0" }
|
||||
regex = { version = "1.6.0" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "ddf497623ae56d21aa4166ff1c0725a7db67e955" }
|
||||
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "ddf497623ae56d21aa4166ff1c0725a7db67e955" }
|
||||
schemars = { version = "0.8.11" }
|
||||
serde = { version = "1.0.147", features = ["derive"] }
|
||||
serde_json = { version = "1.0.87" }
|
||||
strum = { version = "0.24.1", features = ["strum_macros"] }
|
||||
strum_macros = { version = "0.24.3" }
|
||||
toml = { version = "0.6.0" }
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
|
||||
52
LICENSE
52
LICENSE
@@ -1062,3 +1062,55 @@ are:
|
||||
"""
|
||||
|
||||
- flake8-django, licensed under the GPL license.
|
||||
|
||||
- rust-analyzer/text-size, licensed under the MIT license:
|
||||
"""
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
"""
|
||||
|
||||
- rome/tools, licensed under the MIT license:
|
||||
"""
|
||||
MIT License
|
||||
|
||||
Copyright (c) Rome Tools, Inc. and its affiliates.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
@@ -1,2 +1,7 @@
|
||||
[files]
|
||||
extend-exclude = ["snapshots"]
|
||||
extend-exclude = ["snapshots", "black"]
|
||||
|
||||
[default.extend-words]
|
||||
trivias = "trivias"
|
||||
hel = "hel"
|
||||
whos = "whos"
|
||||
|
||||
7
clippy.toml
Normal file
7
clippy.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
doc-valid-idents = [
|
||||
"StackOverflow",
|
||||
"CodeQL",
|
||||
"IPython",
|
||||
"NumPy",
|
||||
"..",
|
||||
]
|
||||
@@ -1,19 +1,20 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.246"
|
||||
edition = "2021"
|
||||
version = "0.0.251"
|
||||
edition = { workspace = true }
|
||||
rust-version = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
anyhow = { version = "1.0.66" }
|
||||
clap = { version = "4.0.1", features = ["derive"] }
|
||||
anyhow = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
colored = { version = "2.0.0" }
|
||||
configparser = { version = "3.0.2" }
|
||||
once_cell = { version = "1.16.0" }
|
||||
regex = { version = "1.6.0" }
|
||||
once_cell = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
ruff = { path = "../ruff", default-features = false }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
serde = { version = "1.0.147", features = ["derive"] }
|
||||
serde_json = { version = "1.0.87" }
|
||||
strum = { version = "0.24.1", features = ["strum_macros"] }
|
||||
strum_macros = { version = "0.24.3" }
|
||||
toml = { version = "0.6.0" }
|
||||
rustc-hash = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
strum = { workspace = true }
|
||||
strum_macros = { workspace = true }
|
||||
toml = { workspace = true }
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.246"
|
||||
version = "0.0.251"
|
||||
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.65.0"
|
||||
edition = { workspace = true }
|
||||
rust-version = { workspace = true }
|
||||
documentation = "https://github.com/charliermarsh/ruff"
|
||||
homepage = "https://github.com/charliermarsh/ruff"
|
||||
repository = "https://github.com/charliermarsh/ruff"
|
||||
@@ -16,12 +16,12 @@ crate-type = ["cdylib", "rlib"]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
anyhow = { version = "1.0.66" }
|
||||
anyhow = { workspace = true }
|
||||
bisection = { version = "0.1.0" }
|
||||
bitflags = { version = "1.3.2" }
|
||||
cfg-if = { version = "1.0.0" }
|
||||
chrono = { version = "0.4.21", default-features = false, features = ["clock"] }
|
||||
clap = { version = "4.0.1", features = ["derive", "env"] }
|
||||
clap = { workspace = true, features = ["derive", "env", "string"] }
|
||||
colored = { version = "2.0.0" }
|
||||
dirs = { version = "4.0.0" }
|
||||
fern = { version = "0.6.1" }
|
||||
@@ -29,32 +29,32 @@ glob = { version = "0.3.0" }
|
||||
globset = { version = "0.4.9" }
|
||||
ignore = { version = "0.4.18" }
|
||||
imperative = { version = "1.0.3" }
|
||||
itertools = { version = "0.10.5" }
|
||||
itertools = { workspace = true }
|
||||
libcst = { workspace = true }
|
||||
log = { version = "0.4.17" }
|
||||
natord = { version = "1.0.9" }
|
||||
nohash-hasher = { version = "0.2.0" }
|
||||
num-bigint = { version = "0.4.3" }
|
||||
num-traits = "0.2.15"
|
||||
once_cell = { version = "1.16.0" }
|
||||
once_cell = { workspace = true }
|
||||
path-absolutize = { version = "3.0.14", features = ["once_cell_cache", "use_unix_paths_on_wasm"] }
|
||||
regex = { version = "1.6.0" }
|
||||
ruff_macros = { version = "0.0.246", path = "../ruff_macros" }
|
||||
ruff_python = { version = "0.0.246", path = "../ruff_python" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
regex = { workspace = true }
|
||||
ruff_macros = { path = "../ruff_macros" }
|
||||
ruff_python = { path = "../ruff_python" }
|
||||
rustc-hash = { workspace = true }
|
||||
rustpython-common = { workspace = true }
|
||||
rustpython-parser = { workspace = true }
|
||||
schemars = { version = "0.8.11" }
|
||||
schemars = { workspace = true }
|
||||
semver = { version = "1.0.16" }
|
||||
serde = { version = "1.0.147", features = ["derive"] }
|
||||
serde = { workspace = true }
|
||||
shellexpand = { version = "3.0.0" }
|
||||
smallvec = { version = "1.10.0" }
|
||||
strum = { version = "0.24.1", features = ["strum_macros"] }
|
||||
strum_macros = { version = "0.24.3" }
|
||||
strum = { workspace = true }
|
||||
strum_macros = { workspace = true }
|
||||
textwrap = { version = "0.16.0" }
|
||||
thiserror = { version = "1.0" }
|
||||
titlecase = { version = "2.2.1" }
|
||||
toml = { version = "0.6.0" }
|
||||
toml = { workspace = true }
|
||||
|
||||
# https://docs.rs/getrandom/0.2.7/getrandom/#webassembly-support
|
||||
# For (future) wasm-pack support
|
||||
|
||||
@@ -103,3 +103,7 @@ class Foo:
|
||||
@classmethod
|
||||
def foo(cls, a: int, b: int) -> int:
|
||||
pass
|
||||
|
||||
# ANN101
|
||||
def foo(self, /, a: int, b: int) -> int:
|
||||
pass
|
||||
|
||||
@@ -57,6 +57,8 @@ dict.fromkeys(("world",), True)
|
||||
{}.deploy(True, False)
|
||||
getattr(someobj, attrname, False)
|
||||
mylist.index(True)
|
||||
int(True)
|
||||
str(int(False))
|
||||
|
||||
|
||||
class Registry:
|
||||
@@ -66,3 +68,11 @@ class Registry:
|
||||
# FBT001: Boolean positional arg in function definition
|
||||
def __setitem__(self, switch: Switch, value: bool) -> None:
|
||||
self._switches[switch.value] = value
|
||||
|
||||
@foo.setter
|
||||
def foo(self, value: bool) -> None:
|
||||
pass
|
||||
|
||||
# FBT001: Boolean positional arg in function definition
|
||||
def foo(self, value: bool) -> None:
|
||||
pass
|
||||
|
||||
@@ -17,6 +17,11 @@ s.rstrip(".facebook.com") # warning
|
||||
s.rstrip("e") # no warning
|
||||
s.rstrip("\n\t ") # no warning
|
||||
s.rstrip(r"\n\t ") # warning
|
||||
s.strip("a") # no warning
|
||||
s.strip("あ") # no warning
|
||||
s.strip("ああ") # warning
|
||||
s.strip("\ufeff") # no warning
|
||||
s.strip("\u0074\u0065\u0073\u0074") # warning
|
||||
|
||||
from somewhere import other_type, strip
|
||||
|
||||
|
||||
@@ -105,3 +105,25 @@ while True:
|
||||
pass
|
||||
finally:
|
||||
break # warning
|
||||
|
||||
|
||||
while True:
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
match *0, 1, *2:
|
||||
case 0,:
|
||||
y = 0
|
||||
case 0, *x:
|
||||
break # warning
|
||||
|
||||
|
||||
while True:
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
match *0, 1, *2:
|
||||
case 0,:
|
||||
y = 0
|
||||
case 0, *x:
|
||||
pass # no warning
|
||||
|
||||
101
crates/ruff/resources/test/fixtures/flake8_bugbear/B027.pyi
vendored
Normal file
101
crates/ruff/resources/test/fixtures/flake8_bugbear/B027.pyi
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
"""
|
||||
Should emit:
|
||||
B027 - on lines 13, 16, 19, 23
|
||||
"""
|
||||
import abc
|
||||
from abc import ABC
|
||||
from abc import abstractmethod, abstractproperty
|
||||
from abc import abstractmethod as notabstract
|
||||
from abc import abstractproperty as notabstract_property
|
||||
|
||||
|
||||
class AbstractClass(ABC):
|
||||
def empty_1(self): # error
|
||||
...
|
||||
|
||||
def empty_2(self): # error
|
||||
pass
|
||||
|
||||
def empty_3(self): # error
|
||||
"""docstring"""
|
||||
...
|
||||
|
||||
def empty_4(self): # error
|
||||
"""multiple ellipsis/pass"""
|
||||
...
|
||||
pass
|
||||
...
|
||||
pass
|
||||
|
||||
@notabstract
|
||||
def abstract_0(self):
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def abstract_1(self):
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def abstract_2(self):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def abstract_3(self):
|
||||
...
|
||||
|
||||
@abc.abstractproperty
|
||||
def abstract_4(self):
|
||||
...
|
||||
|
||||
@abstractproperty
|
||||
def abstract_5(self):
|
||||
...
|
||||
|
||||
@notabstract_property
|
||||
def abstract_6(self):
|
||||
...
|
||||
|
||||
def body_1(self):
|
||||
print("foo")
|
||||
...
|
||||
|
||||
def body_2(self):
|
||||
self.body_1()
|
||||
|
||||
|
||||
class NonAbstractClass:
|
||||
def empty_1(self): # safe
|
||||
...
|
||||
|
||||
def empty_2(self): # safe
|
||||
pass
|
||||
|
||||
|
||||
# ignore @overload, fixes issue #304
|
||||
# ignore overload with other imports, fixes #308
|
||||
import typing
|
||||
import typing as t
|
||||
import typing as anything
|
||||
from typing import Union, overload
|
||||
|
||||
|
||||
class AbstractClass(ABC):
|
||||
@overload
|
||||
def empty_1(self, foo: str):
|
||||
...
|
||||
|
||||
@typing.overload
|
||||
def empty_1(self, foo: int):
|
||||
...
|
||||
|
||||
@t.overload
|
||||
def empty_1(self, foo: list):
|
||||
...
|
||||
|
||||
@anything.overload
|
||||
def empty_1(self, foo: float):
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def empty_1(self, foo: Union[str, int, list, float]):
|
||||
...
|
||||
14
crates/ruff/resources/test/fixtures/flake8_bugbear/B029.py
vendored
Normal file
14
crates/ruff/resources/test/fixtures/flake8_bugbear/B029.py
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
"""
|
||||
Should emit:
|
||||
B029 - on lines 8 and 13
|
||||
"""
|
||||
|
||||
try:
|
||||
pass
|
||||
except ():
|
||||
pass
|
||||
|
||||
try:
|
||||
pass
|
||||
except () as e:
|
||||
pass
|
||||
29
crates/ruff/resources/test/fixtures/flake8_bugbear/B032.py
vendored
Normal file
29
crates/ruff/resources/test/fixtures/flake8_bugbear/B032.py
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
"""
|
||||
Should emit:
|
||||
B032 - on lines 9, 10, 12, 13, 16-19
|
||||
"""
|
||||
|
||||
# Flag these
|
||||
dct = {"a": 1}
|
||||
|
||||
dct["b"]: 2
|
||||
dct.b: 2
|
||||
|
||||
dct["b"]: "test"
|
||||
dct.b: "test"
|
||||
|
||||
test = "test"
|
||||
dct["b"]: test
|
||||
dct["b"]: test.lower()
|
||||
dct.b: test
|
||||
dct.b: test.lower()
|
||||
|
||||
# Do not flag below
|
||||
typed_dct: dict[str, int] = {"a": 1}
|
||||
typed_dct["b"] = 2
|
||||
typed_dct.b = 2
|
||||
|
||||
|
||||
class TestClass:
|
||||
def test_self(self):
|
||||
self.test: int
|
||||
@@ -1,6 +1,6 @@
|
||||
"""
|
||||
Should emit:
|
||||
B904 - on lines 10, 11 and 16
|
||||
B904 - on lines 10, 11, 16, 62, and 64
|
||||
"""
|
||||
|
||||
try:
|
||||
@@ -53,3 +53,20 @@ except ImportError:
|
||||
raise ValueError
|
||||
except ValueError:
|
||||
raise
|
||||
|
||||
|
||||
try:
|
||||
...
|
||||
except Exception as e:
|
||||
if ...:
|
||||
raise RuntimeError("boom!")
|
||||
else:
|
||||
raise RuntimeError("bang!")
|
||||
|
||||
|
||||
try:
|
||||
...
|
||||
except Exception as e:
|
||||
match 0:
|
||||
case 0:
|
||||
raise RuntimeError("boom!")
|
||||
|
||||
@@ -12,7 +12,16 @@ class IncorrectModel(models.Model):
|
||||
urlfield = models.URLField(max_length=255, null=True)
|
||||
|
||||
|
||||
class IncorrectModelWithAliasedBase(DjangoModel):
|
||||
class IncorrectModelWithAlias(DjangoModel):
|
||||
charfield = DjangoModel.CharField(max_length=255, null=True)
|
||||
textfield = SmthCharField(max_length=255, null=True)
|
||||
slugfield = models.SlugField(max_length=255, null=True)
|
||||
emailfield = models.EmailField(max_length=255, null=True)
|
||||
filepathfield = models.FilePathField(max_length=255, null=True)
|
||||
urlfield = models.URLField(max_length=255, null=True)
|
||||
|
||||
|
||||
class IncorrectModelWithoutSuperclass:
|
||||
charfield = DjangoModel.CharField(max_length=255, null=True)
|
||||
textfield = SmthCharField(max_length=255, null=True)
|
||||
slugfield = models.SlugField(max_length=255, null=True)
|
||||
|
||||
@@ -15,3 +15,23 @@ def correct_pre_save_handler():
|
||||
@receiver(pre_save, sender=MyModel)
|
||||
def incorrect_pre_save_handler():
|
||||
pass
|
||||
|
||||
|
||||
@receiver(pre_save, sender=MyModel)
|
||||
@receiver(pre_save, sender=MyModel)
|
||||
@test_decorator
|
||||
def correct_multiple():
|
||||
pass
|
||||
|
||||
|
||||
@receiver(pre_save, sender=MyModel)
|
||||
@receiver(pre_save, sender=MyModel)
|
||||
def correct_multiple():
|
||||
pass
|
||||
|
||||
|
||||
@receiver(pre_save, sender=MyModel)
|
||||
@test_decorator
|
||||
@receiver(pre_save, sender=MyModel)
|
||||
def incorrect_multiple():
|
||||
pass
|
||||
|
||||
@@ -18,3 +18,10 @@ class Foo:
|
||||
|
||||
class FooTable(BaseTable):
|
||||
bar = fields.ListField(list)
|
||||
|
||||
|
||||
lambda *args, **kwargs: []
|
||||
|
||||
lambda *args: []
|
||||
|
||||
lambda **kwargs: []
|
||||
|
||||
@@ -17,6 +17,12 @@ class Test(unittest.TestCase):
|
||||
self.assertTrue(**{"expr": expr, "msg": msg}) # Error, unfixable
|
||||
self.assertTrue(msg=msg, expr=expr, unexpected_arg=False) # Error, unfixable
|
||||
self.assertTrue(msg=msg) # Error, unfixable
|
||||
(
|
||||
self.assertIsNotNone(value) # Error, unfixable
|
||||
if expect_condition
|
||||
else self.assertIsNone(value) # Error, unfixable
|
||||
)
|
||||
return self.assertEqual(True, False) # Error, unfixable
|
||||
|
||||
def test_assert_false(self):
|
||||
self.assertFalse(True) # Error
|
||||
|
||||
@@ -6,6 +6,8 @@ def test_ok():
|
||||
assert something or something_else
|
||||
assert something or something_else and something_third
|
||||
assert not (something and something_else)
|
||||
assert something, "something message"
|
||||
assert something or something_else and something_third, "another message"
|
||||
|
||||
|
||||
def test_error():
|
||||
@@ -13,6 +15,27 @@ def test_error():
|
||||
assert something and something_else and something_third
|
||||
assert something and not something_else
|
||||
assert something and (something_else or something_third)
|
||||
assert not something and something_else
|
||||
assert not (something or something_else)
|
||||
assert not (something or something_else or something_third)
|
||||
assert something and something_else == """error
|
||||
message
|
||||
"""
|
||||
|
||||
# recursive case
|
||||
assert not (a or not (b or c)) # note that we only reduce once here
|
||||
assert not (a or not (b and c))
|
||||
|
||||
# detected, but no autofix for messages
|
||||
assert something and something_else, "error message"
|
||||
assert not (something or something_else and something_third), "with message"
|
||||
# detected, but no autofix for mixed conditions (e.g. `a or b and c`)
|
||||
assert not (something or something_else and something_third)
|
||||
# detected, but no autofix for parenthesized conditions
|
||||
assert (
|
||||
something
|
||||
and something_else
|
||||
== """error
|
||||
message
|
||||
"""
|
||||
)
|
||||
|
||||
@@ -77,7 +77,7 @@ def x(y):
|
||||
|
||||
# last line in while loop
|
||||
def x(y):
|
||||
while True:
|
||||
while i > 0:
|
||||
if y > 0:
|
||||
return 1
|
||||
y += 1
|
||||
@@ -251,3 +251,27 @@ def noreturn_pytest_xfail_2():
|
||||
if x > 0:
|
||||
return 1
|
||||
py_xfail("oof")
|
||||
|
||||
|
||||
def nested(values):
|
||||
if not values:
|
||||
return False
|
||||
|
||||
for value in values:
|
||||
print(value)
|
||||
|
||||
|
||||
def while_true():
|
||||
while True:
|
||||
if y > 0:
|
||||
return 1
|
||||
y += 1
|
||||
|
||||
|
||||
# match
|
||||
def x(y):
|
||||
match y:
|
||||
case 0:
|
||||
return 1
|
||||
case 1:
|
||||
print() # error
|
||||
|
||||
@@ -136,3 +136,24 @@ def nested2(x, y, z):
|
||||
break
|
||||
else:
|
||||
a = z
|
||||
|
||||
|
||||
def elif1(x, y, w, z):
|
||||
for i in x:
|
||||
if i > y:
|
||||
a = z
|
||||
elif i > w:
|
||||
break
|
||||
else:
|
||||
a = z
|
||||
|
||||
|
||||
def elif2(x, y, w, z):
|
||||
for i in x:
|
||||
if i > y:
|
||||
a = z
|
||||
else:
|
||||
if i > w:
|
||||
break
|
||||
else:
|
||||
a = z
|
||||
|
||||
@@ -35,6 +35,13 @@ class Foo(metaclass=BazMeta):
|
||||
return None
|
||||
if self.bar()._private: # SLF001
|
||||
return None
|
||||
if Bar._private_thing: # SLF001
|
||||
return None
|
||||
if Foo._private_thing:
|
||||
return None
|
||||
Foo = Bar()
|
||||
if Foo._private_thing: # SLF001
|
||||
return None
|
||||
return self.bar
|
||||
|
||||
def public_func(self):
|
||||
@@ -49,15 +56,17 @@ class Foo(metaclass=BazMeta):
|
||||
|
||||
foo = Foo()
|
||||
|
||||
print(foo.public_thing)
|
||||
print(foo.public_func())
|
||||
print(foo.__dict__)
|
||||
print(foo.__str__())
|
||||
print(foo().__class__)
|
||||
|
||||
print(foo._private_thing) # SLF001
|
||||
print(foo.__really_private_thing) # SLF001
|
||||
print(foo._private_func()) # SLF001
|
||||
print(foo.__really_private_func(1)) # SLF001
|
||||
print(foo.bar._private) # SLF001
|
||||
print(foo()._private_thing) # SLF001
|
||||
print(foo()._private_thing__) # SLF001
|
||||
|
||||
print(foo.public_thing)
|
||||
print(foo.public_func())
|
||||
print(foo.__dict__)
|
||||
print(foo.__str__())
|
||||
print(foo().__class__)
|
||||
print(foo._asdict())
|
||||
|
||||
76
crates/ruff/resources/test/fixtures/flake8_simplify/SIM116.py
vendored
Normal file
76
crates/ruff/resources/test/fixtures/flake8_simplify/SIM116.py
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
# Errors
|
||||
a = "hello"
|
||||
|
||||
# SIM116
|
||||
if a == "foo":
|
||||
return "bar"
|
||||
elif a == "bar":
|
||||
return "baz"
|
||||
elif a == "boo":
|
||||
return "ooh"
|
||||
else:
|
||||
return 42
|
||||
|
||||
# SIM116
|
||||
if a == 1:
|
||||
return (1, 2, 3)
|
||||
elif a == 2:
|
||||
return (4, 5, 6)
|
||||
elif a == 3:
|
||||
return (7, 8, 9)
|
||||
else:
|
||||
return (10, 11, 12)
|
||||
|
||||
# SIM116
|
||||
if a == 1:
|
||||
return (1, 2, 3)
|
||||
elif a == 2:
|
||||
return (4, 5, 6)
|
||||
elif a == 3:
|
||||
return (7, 8, 9)
|
||||
|
||||
# SIM116
|
||||
if a == "hello 'sir'":
|
||||
return (1, 2, 3)
|
||||
elif a == 'goodbye "mam"':
|
||||
return (4, 5, 6)
|
||||
elif a == """Fairwell 'mister'""":
|
||||
return (7, 8, 9)
|
||||
else:
|
||||
return (10, 11, 12)
|
||||
|
||||
# SIM116
|
||||
if a == b"one":
|
||||
return 1
|
||||
elif a == b"two":
|
||||
return 2
|
||||
elif a == b"three":
|
||||
return 3
|
||||
|
||||
# SIM116
|
||||
if a == "hello 'sir'":
|
||||
return ("hello'", 'hi"', 3)
|
||||
elif a == 'goodbye "mam"':
|
||||
return (4, 5, 6)
|
||||
elif a == """Fairwell 'mister'""":
|
||||
return (7, 8, 9)
|
||||
else:
|
||||
return (10, 11, 12)
|
||||
|
||||
# OK
|
||||
if a == "foo":
|
||||
return "bar"
|
||||
elif a == "bar":
|
||||
return baz()
|
||||
elif a == "boo":
|
||||
return "ooh"
|
||||
else:
|
||||
return 42
|
||||
|
||||
# OK
|
||||
if a == b"one":
|
||||
return 1
|
||||
elif b == b"two":
|
||||
return 2
|
||||
elif a == b"three":
|
||||
return 3
|
||||
@@ -18,8 +18,6 @@ from \
|
||||
..parent\
|
||||
import \
|
||||
world_hello
|
||||
|
||||
# TID252 (without autofix; too many levels up)
|
||||
from ..... import ultragrantparent
|
||||
from ...... import ultragrantparent
|
||||
from ....... import ultragrantparent
|
||||
|
||||
0
crates/ruff/resources/test/fixtures/flake8_tidy_imports/TID252/my_package/__init__.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/flake8_tidy_imports/TID252/my_package/__init__.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/flake8_tidy_imports/TID252/my_package/sublib/__init__.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/flake8_tidy_imports/TID252/my_package/sublib/__init__.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
from typing import TYPE_CHECKING, Any, ClassVar
|
||||
|
||||
import attrs
|
||||
|
||||
from ..protocol import commands, definitions, responses
|
||||
from ..server import example
|
||||
from .. import server
|
||||
from . import logger, models
|
||||
10
crates/ruff/resources/test/fixtures/flake8_type_checking/TCH004_13.py
vendored
Normal file
10
crates/ruff/resources/test/fixtures/flake8_type_checking/TCH004_13.py
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Final, Literal, TypeAlias
|
||||
|
||||
RatingKey: TypeAlias = Literal["good", "fair", "poor"]
|
||||
|
||||
RATING_KEYS: Final[tuple[RatingKey, ...]] = ("good", "fair", "poor")
|
||||
23
crates/ruff/resources/test/fixtures/isort/force_to_top.py
vendored
Normal file
23
crates/ruff/resources/test/fixtures/isort/force_to_top.py
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
import lib6
|
||||
import lib2
|
||||
import lib5
|
||||
import lib1
|
||||
import lib3
|
||||
import lib4
|
||||
|
||||
import foo
|
||||
import z
|
||||
from foo import bar
|
||||
from lib1 import foo
|
||||
from lib2 import foo
|
||||
from lib1.lib2 import foo
|
||||
from foo.lib1.bar import baz
|
||||
from lib4 import lib1
|
||||
from lib5 import lib2
|
||||
from lib4 import lib2
|
||||
from lib5 import lib1
|
||||
|
||||
import lib3.lib4
|
||||
import lib3.lib4.lib5
|
||||
from lib3.lib4 import foo
|
||||
from lib3.lib4.lib5 import foo
|
||||
@@ -1,2 +1,2 @@
|
||||
from long_module_name import member_one, member_two, member_three, member_four, member_five
|
||||
from long_module_name import member_one, member_two, member_three, member_four, member_five
|
||||
|
||||
|
||||
@@ -5,3 +5,4 @@ line-length = 88
|
||||
lines-after-imports = 3
|
||||
lines-between-types = 2
|
||||
known-local-folder = ["ruff"]
|
||||
force-to-top = ["lib1", "lib3", "lib5", "lib3.lib4", "z"]
|
||||
|
||||
4
crates/ruff/resources/test/fixtures/isort/required_imports/multiline_docstring.py
vendored
Normal file
4
crates/ruff/resources/test/fixtures/isort/required_imports/multiline_docstring.py
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
"""a
|
||||
b"""
|
||||
# b
|
||||
import os
|
||||
20
crates/ruff/resources/test/fixtures/numpy/NPY001.py
vendored
Normal file
20
crates/ruff/resources/test/fixtures/numpy/NPY001.py
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
import numpy as npy
|
||||
import numpy as np
|
||||
import numpy
|
||||
|
||||
# Error
|
||||
npy.bool
|
||||
npy.int
|
||||
|
||||
if dtype == np.object:
|
||||
...
|
||||
|
||||
result = result.select_dtypes([np.byte, np.ubyte, np.short, np.ushort, np.int, np.long])
|
||||
|
||||
pdf = pd.DataFrame(
|
||||
data=[[1, 2, 3]],
|
||||
columns=["a", "b", "c"],
|
||||
dtype=numpy.object,
|
||||
)
|
||||
|
||||
_ = arr.astype(np.int)
|
||||
62
crates/ruff/resources/test/fixtures/numpy/NPY002.py
vendored
Normal file
62
crates/ruff/resources/test/fixtures/numpy/NPY002.py
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
# Do this (new version)
|
||||
from numpy.random import default_rng
|
||||
rng = default_rng()
|
||||
vals = rng.standard_normal(10)
|
||||
more_vals = rng.standard_normal(10)
|
||||
numbers = rng.integers(high, size=5)
|
||||
|
||||
# instead of this (legacy version)
|
||||
from numpy import random
|
||||
vals = random.standard_normal(10)
|
||||
more_vals = random.standard_normal(10)
|
||||
numbers = random.integers(high, size=5)
|
||||
|
||||
import numpy
|
||||
numpy.random.seed()
|
||||
numpy.random.get_state()
|
||||
numpy.random.set_state()
|
||||
numpy.random.rand()
|
||||
numpy.random.randn()
|
||||
numpy.random.randint()
|
||||
numpy.random.random_integers()
|
||||
numpy.random.random_sample()
|
||||
numpy.random.choice()
|
||||
numpy.random.bytes()
|
||||
numpy.random.shuffle()
|
||||
numpy.random.permutation()
|
||||
numpy.random.beta()
|
||||
numpy.random.binomial()
|
||||
numpy.random.chisquare()
|
||||
numpy.random.dirichlet()
|
||||
numpy.random.exponential()
|
||||
numpy.random.f()
|
||||
numpy.random.gamma()
|
||||
numpy.random.geometric()
|
||||
numpy.random.get_state()
|
||||
numpy.random.gumbel()
|
||||
numpy.random.hypergeometric()
|
||||
numpy.random.laplace()
|
||||
numpy.random.logistic()
|
||||
numpy.random.lognormal()
|
||||
numpy.random.logseries()
|
||||
numpy.random.multinomial()
|
||||
numpy.random.multivariate_normal()
|
||||
numpy.random.negative_binomial()
|
||||
numpy.random.noncentral_chisquare()
|
||||
numpy.random.noncentral_f()
|
||||
numpy.random.normal()
|
||||
numpy.random.pareto()
|
||||
numpy.random.poisson()
|
||||
numpy.random.power()
|
||||
numpy.random.rayleigh()
|
||||
numpy.random.standard_cauchy()
|
||||
numpy.random.standard_exponential()
|
||||
numpy.random.standard_gamma()
|
||||
numpy.random.standard_normal()
|
||||
numpy.random.standard_t()
|
||||
numpy.random.triangular()
|
||||
numpy.random.uniform()
|
||||
numpy.random.vonmises()
|
||||
numpy.random.wald()
|
||||
numpy.random.weibull()
|
||||
numpy.random.zipf()
|
||||
0
crates/ruff/resources/test/fixtures/pep8_naming/N999/module/MODULE/__init__.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/pep8_naming/N999/module/MODULE/__init__.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/pep8_naming/N999/module/MODULE/file.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/pep8_naming/N999/module/MODULE/file.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/pep8_naming/N999/module/flake9/__init__.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/pep8_naming/N999/module/flake9/__init__.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/pep8_naming/N999/module/mod with spaces/__init__.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/pep8_naming/N999/module/mod with spaces/__init__.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/pep8_naming/N999/module/mod with spaces/file.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/pep8_naming/N999/module/mod with spaces/file.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/pep8_naming/N999/module/mod with spaces/file2.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/pep8_naming/N999/module/mod with spaces/file2.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/pep8_naming/N999/module/mod-with-dashes/__init__.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/pep8_naming/N999/module/mod-with-dashes/__init__.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/pep8_naming/N999/module/no_module/test.txt
vendored
Normal file
0
crates/ruff/resources/test/fixtures/pep8_naming/N999/module/no_module/test.txt
vendored
Normal file
0
crates/ruff/resources/test/fixtures/pep8_naming/N999/module/valid_name/0001_initial.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/pep8_naming/N999/module/valid_name/0001_initial.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/pep8_naming/N999/module/valid_name/__init__.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/pep8_naming/N999/module/valid_name/__init__.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/pep8_naming/N999/module/valid_name/__main__.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/pep8_naming/N999/module/valid_name/__main__.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/pep8_naming/N999/module/valid_name/__setup__.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/pep8_naming/N999/module/valid_name/__setup__.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/pep8_naming/N999/module/valid_name/file-with-dashes.py
vendored
Normal file
0
crates/ruff/resources/test/fixtures/pep8_naming/N999/module/valid_name/file-with-dashes.py
vendored
Normal file
@@ -54,3 +54,6 @@ def f(): ...
|
||||
class C: ...; x = 1
|
||||
#: E701:1:8 E702:1:13
|
||||
class C: ...; ...
|
||||
#: E701:2:12
|
||||
match *0, 1, *2:
|
||||
case 0,: y = 0
|
||||
|
||||
@@ -9,6 +9,9 @@ while False:
|
||||
f = lambda: (yield 1)
|
||||
#: E731
|
||||
f = lambda: (yield from g())
|
||||
#: E731
|
||||
class F:
|
||||
f = lambda x: 2 * x
|
||||
|
||||
f = object()
|
||||
f.method = lambda: "Method"
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
#: W605:1:10
|
||||
regex = '\.png$'
|
||||
|
||||
#: W605:2:1
|
||||
regex = '''
|
||||
\.png$
|
||||
'''
|
||||
|
||||
#: W605:2:6
|
||||
f(
|
||||
'\_'
|
||||
)
|
||||
|
||||
#: W605:4:6
|
||||
"""
|
||||
multi-line
|
||||
literal
|
||||
with \_ somewhere
|
||||
in the middle
|
||||
"""
|
||||
|
||||
#: Okay
|
||||
regex = r'\.png$'
|
||||
regex = '\\.png$'
|
||||
regex = r'''
|
||||
\.png$
|
||||
'''
|
||||
regex = r'''
|
||||
\\.png$
|
||||
'''
|
||||
s = '\\'
|
||||
regex = '\w' # noqa
|
||||
regex = '''
|
||||
\w
|
||||
''' # noqa
|
||||
#: W605:1:10
|
||||
regex = '\.png$'
|
||||
|
||||
#: W605:2:1
|
||||
regex = '''
|
||||
\.png$
|
||||
'''
|
||||
|
||||
#: W605:2:6
|
||||
f(
|
||||
'\_'
|
||||
)
|
||||
|
||||
#: W605:4:6
|
||||
"""
|
||||
multi-line
|
||||
literal
|
||||
with \_ somewhere
|
||||
in the middle
|
||||
"""
|
||||
|
||||
#: Okay
|
||||
regex = r'\.png$'
|
||||
regex = '\\.png$'
|
||||
regex = r'''
|
||||
\.png$
|
||||
'''
|
||||
regex = r'''
|
||||
\\.png$
|
||||
'''
|
||||
s = '\\'
|
||||
regex = '\w' # noqa
|
||||
regex = '''
|
||||
\w
|
||||
''' # noqa
|
||||
|
||||
@@ -85,3 +85,10 @@ else:
|
||||
|
||||
|
||||
CustomInt: TypeAlias = "np.int8 | np.int16"
|
||||
|
||||
|
||||
# Test: match statements.
|
||||
match *0, 1, *2:
|
||||
case 0,:
|
||||
import x
|
||||
import y
|
||||
|
||||
@@ -4,3 +4,5 @@ from __future__ import absolute_import
|
||||
from collections import namedtuple
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import __future__
|
||||
|
||||
@@ -33,3 +33,11 @@ f"{f'{v:0.2f}'}"
|
||||
# Errors
|
||||
f"{v:{f'0.2f'}}"
|
||||
f"{f''}"
|
||||
f"{{test}}"
|
||||
f'{{ 40 }}'
|
||||
f"{{a {{x}}"
|
||||
f"{{{{x}}}}"
|
||||
|
||||
# To be fixed
|
||||
# Error: f-string: single '}' is not allowed at line 41 column 8
|
||||
# f"\{{x}}"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
Test that shadowing a global with a class attribute does not produce a
|
||||
warning.
|
||||
"""
|
||||
Test that shadowing a global with a class attribute does not produce a
|
||||
warning.
|
||||
"""
|
||||
|
||||
import fu
|
||||
|
||||
|
||||
26
crates/ruff/resources/test/fixtures/pyflakes/F821_9.py
vendored
Normal file
26
crates/ruff/resources/test/fixtures/pyflakes/F821_9.py
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
"""Test: match statements."""
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class Car:
|
||||
make: str
|
||||
model: str
|
||||
|
||||
|
||||
def f():
|
||||
match Car("Toyota", "Corolla"):
|
||||
case Car("Toyota", model):
|
||||
print(model)
|
||||
case Car(make, "Corolla"):
|
||||
print(make)
|
||||
|
||||
|
||||
def f(provided: int) -> int:
|
||||
match provided:
|
||||
case True:
|
||||
return captured # F821
|
||||
case [captured, *_]:
|
||||
return captured
|
||||
case captured:
|
||||
return captured
|
||||
@@ -71,7 +71,7 @@ def f():
|
||||
def connect():
|
||||
return None, None
|
||||
|
||||
with (connect() as (connection, cursor)):
|
||||
with connect() as (connection, cursor):
|
||||
cursor.execute("SELECT * FROM users")
|
||||
|
||||
|
||||
@@ -94,3 +94,30 @@ def f():
|
||||
(exponential := (exponential * base_multiplier) % 3): i + 1 for i in range(2)
|
||||
}
|
||||
return hash_map
|
||||
|
||||
|
||||
def f(x: int):
|
||||
msg1 = "Hello, world!"
|
||||
msg2 = "Hello, world!"
|
||||
msg3 = "Hello, world!"
|
||||
match x:
|
||||
case 1:
|
||||
print(msg1)
|
||||
case 2:
|
||||
print(msg2)
|
||||
|
||||
|
||||
def f(x: int):
|
||||
import enum
|
||||
|
||||
Foo = enum.Enum("Foo", "A B")
|
||||
Bar = enum.Enum("Bar", "A B")
|
||||
Baz = enum.Enum("Baz", "A B")
|
||||
|
||||
match x:
|
||||
case (Foo.A):
|
||||
print("A")
|
||||
case [Bar.A, *_]:
|
||||
print("A")
|
||||
case y:
|
||||
pass
|
||||
|
||||
41
crates/ruff/resources/test/fixtures/pylint/return_in_init.py
vendored
Normal file
41
crates/ruff/resources/test/fixtures/pylint/return_in_init.py
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
def a():
|
||||
return
|
||||
|
||||
def __init__():
|
||||
return
|
||||
|
||||
class A:
|
||||
def __init__(self):
|
||||
return
|
||||
|
||||
|
||||
class B:
|
||||
def __init__(self):
|
||||
return 3
|
||||
|
||||
def gen(self):
|
||||
return 5
|
||||
|
||||
class MyClass:
|
||||
|
||||
def __init__(self):
|
||||
return 1
|
||||
|
||||
class MyClass2:
|
||||
"""dummy class"""
|
||||
|
||||
def __init__(self):
|
||||
return
|
||||
|
||||
|
||||
class MyClass3:
|
||||
"""dummy class"""
|
||||
|
||||
def __init__(self):
|
||||
return None
|
||||
|
||||
class MyClass5:
|
||||
"""dummy class"""
|
||||
|
||||
def __init__(self):
|
||||
self.callable = lambda: (yield None)
|
||||
@@ -113,3 +113,14 @@ def test_break_in_if_orelse():
|
||||
else:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def test_break_in_with():
|
||||
"""no false positive for break in with"""
|
||||
for name in ["demo"]:
|
||||
with open(__file__) as f:
|
||||
if name in f.read():
|
||||
break
|
||||
else:
|
||||
return True
|
||||
return False
|
||||
|
||||
73
crates/ruff/resources/test/fixtures/ruff/RUF006.py
vendored
Normal file
73
crates/ruff/resources/test/fixtures/ruff/RUF006.py
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
import asyncio
|
||||
|
||||
|
||||
# Error
|
||||
def f():
|
||||
asyncio.create_task(coordinator.ws_connect()) # Error
|
||||
|
||||
|
||||
# Error
|
||||
def f():
|
||||
asyncio.ensure_future(coordinator.ws_connect()) # Error
|
||||
|
||||
|
||||
# OK
|
||||
def f():
|
||||
background_tasks = set()
|
||||
|
||||
for i in range(10):
|
||||
task = asyncio.create_task(some_coro(param=i))
|
||||
|
||||
# Add task to the set. This creates a strong reference.
|
||||
background_tasks.add(task)
|
||||
|
||||
# To prevent keeping references to finished tasks forever,
|
||||
# make each task remove its own reference from the set after
|
||||
# completion:
|
||||
task.add_done_callback(background_tasks.discard)
|
||||
|
||||
|
||||
# OK
|
||||
def f():
|
||||
background_tasks = set()
|
||||
|
||||
for i in range(10):
|
||||
task = asyncio.ensure_future(some_coro(param=i))
|
||||
|
||||
# Add task to the set. This creates a strong reference.
|
||||
background_tasks.add(task)
|
||||
|
||||
# To prevent keeping references to finished tasks forever,
|
||||
# make each task remove its own reference from the set after
|
||||
# completion:
|
||||
task.add_done_callback(background_tasks.discard)
|
||||
|
||||
|
||||
# OK
|
||||
def f():
|
||||
ctx.task = asyncio.create_task(make_request())
|
||||
|
||||
|
||||
# OK
|
||||
def f():
|
||||
tasks.append(asyncio.create_task(self._populate_collection(coll, coll_info)))
|
||||
|
||||
|
||||
# OK
|
||||
def f():
|
||||
asyncio.wait([asyncio.create_task(client.close()) for client in clients.values()])
|
||||
|
||||
|
||||
# OK
|
||||
def f():
|
||||
tasks = [asyncio.create_task(task) for task in tasks]
|
||||
|
||||
|
||||
# Ok (false negative)
|
||||
def f():
|
||||
task = asyncio.create_task(coordinator.ws_connect())
|
||||
|
||||
|
||||
# Ok (potential false negative)
|
||||
def f():
|
||||
do_nothing_with_the_task(asyncio.create_task(coordinator.ws_connect()))
|
||||
8
crates/ruff/resources/test/fixtures/ruff/ruff_targeted_noqa.py
vendored
Normal file
8
crates/ruff/resources/test/fixtures/ruff/ruff_targeted_noqa.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# ruff: noqa: F401
|
||||
|
||||
import os
|
||||
import foo
|
||||
|
||||
|
||||
def f():
|
||||
x = 1
|
||||
@@ -20,6 +20,24 @@ def bad():
|
||||
logger.exception("something failed")
|
||||
|
||||
|
||||
def bad():
|
||||
try:
|
||||
a = process()
|
||||
if not a:
|
||||
raise MyException(a)
|
||||
|
||||
raise MyException(a)
|
||||
|
||||
try:
|
||||
b = process()
|
||||
if not b:
|
||||
raise MyException(b)
|
||||
except* Exception:
|
||||
logger.exception("something failed")
|
||||
except* Exception:
|
||||
logger.exception("something failed")
|
||||
|
||||
|
||||
def good():
|
||||
try:
|
||||
a = process() # This throws the exception now
|
||||
|
||||
64
crates/ruff/resources/test/fixtures/tryceratops/TRY401.py
vendored
Normal file
64
crates/ruff/resources/test/fixtures/tryceratops/TRY401.py
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
# Errors
|
||||
def main_function():
|
||||
try:
|
||||
process()
|
||||
handle()
|
||||
finish()
|
||||
except Exception as ex:
|
||||
logger.exception(f"Found an error: {ex}") # TRY401
|
||||
|
||||
|
||||
def main_function():
|
||||
try:
|
||||
process()
|
||||
handle()
|
||||
finish()
|
||||
except ValueError as bad:
|
||||
if True is False:
|
||||
for i in range(10):
|
||||
logger.exception(f"Found an error: {bad} {good}") # TRY401
|
||||
except IndexError as bad:
|
||||
logger.exception(f"Found an error: {bad} {bad}") # TRY401
|
||||
except Exception as bad:
|
||||
logger.exception(f"Found an error: {bad}") # TRY401
|
||||
logger.exception(f"Found an error: {bad}") # TRY401
|
||||
|
||||
if True:
|
||||
logger.exception(f"Found an error: {bad}") # TRY401
|
||||
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def func_fstr():
|
||||
try:
|
||||
...
|
||||
except Exception as ex:
|
||||
logger.exception(f"Logging an error: {ex}") # TRY401
|
||||
|
||||
|
||||
def func_concat():
|
||||
try:
|
||||
...
|
||||
except Exception as ex:
|
||||
logger.exception("Logging an error: " + str(ex)) # TRY401
|
||||
|
||||
|
||||
def func_comma():
|
||||
try:
|
||||
...
|
||||
except Exception as ex:
|
||||
logger.exception("Logging an error:", ex) # TRY401
|
||||
|
||||
|
||||
# OK
|
||||
def main_function():
|
||||
try:
|
||||
process()
|
||||
handle()
|
||||
finish()
|
||||
except Exception as ex:
|
||||
logger.exception(f"Found an error: {er}")
|
||||
logger.exception(f"Found an error: {ex.field}")
|
||||
@@ -8,14 +8,14 @@ behaviors.
|
||||
Running from the repo root should pick up and enforce the appropriate settings for each package:
|
||||
|
||||
```console
|
||||
∴ cargo run resources/test/project/
|
||||
resources/test/project/examples/.dotfiles/script.py:1:1: I001 Import block is un-sorted or un-formatted
|
||||
resources/test/project/examples/.dotfiles/script.py:1:8: F401 `numpy` imported but unused
|
||||
resources/test/project/examples/.dotfiles/script.py:2:17: F401 `app.app_file` imported but unused
|
||||
resources/test/project/examples/docs/docs/file.py:1:1: I001 Import block is un-sorted or un-formatted
|
||||
resources/test/project/examples/docs/docs/file.py:8:5: F841 Local variable `x` is assigned to but never used
|
||||
resources/test/project/project/file.py:1:8: F401 `os` imported but unused
|
||||
resources/test/project/project/import_file.py:1:1: I001 Import block is un-sorted or un-formatted
|
||||
∴ cargo run -p ruff_cli -- check crates/ruff/resources/test/project/
|
||||
crates/ruff/resources/test/project/examples/.dotfiles/script.py:1:1: I001 Import block is un-sorted or un-formatted
|
||||
crates/ruff/resources/test/project/examples/.dotfiles/script.py:1:8: F401 `numpy` imported but unused
|
||||
crates/ruff/resources/test/project/examples/.dotfiles/script.py:2:17: F401 `app.app_file` imported but unused
|
||||
crates/ruff/resources/test/project/examples/docs/docs/file.py:1:1: I001 Import block is un-sorted or un-formatted
|
||||
crates/ruff/resources/test/project/examples/docs/docs/file.py:8:5: F841 Local variable `x` is assigned to but never used
|
||||
crates/ruff/resources/test/project/project/file.py:1:8: F401 `os` imported but unused
|
||||
crates/ruff/resources/test/project/project/import_file.py:1:1: I001 Import block is un-sorted or un-formatted
|
||||
Found 7 errors.
|
||||
7 potentially fixable with the --fix option.
|
||||
```
|
||||
@@ -23,7 +23,7 @@ Found 7 errors.
|
||||
Running from the project directory itself should exhibit the same behavior:
|
||||
|
||||
```console
|
||||
∴ (cd resources/test/project/ && cargo run .)
|
||||
∴ (cd crates/ruff/resources/test/project/ && cargo run -p ruff_cli -- check .)
|
||||
examples/.dotfiles/script.py:1:1: I001 Import block is un-sorted or un-formatted
|
||||
examples/.dotfiles/script.py:1:8: F401 `numpy` imported but unused
|
||||
examples/.dotfiles/script.py:2:17: F401 `app.app_file` imported but unused
|
||||
@@ -39,7 +39,7 @@ Running from the sub-package directory should exhibit the same behavior, but omi
|
||||
files:
|
||||
|
||||
```console
|
||||
∴ (cd resources/test/project/examples/docs && cargo run .)
|
||||
∴ (cd crates/ruff/resources/test/project/examples/docs && cargo run -p ruff_cli -- check .)
|
||||
docs/file.py:1:1: I001 Import block is un-sorted or un-formatted
|
||||
docs/file.py:8:5: F841 Local variable `x` is assigned to but never used
|
||||
Found 2 errors.
|
||||
@@ -50,16 +50,16 @@ Found 2 errors.
|
||||
file paths from the current working directory:
|
||||
|
||||
```console
|
||||
∴ (cargo run -- --config=resources/test/project/pyproject.toml resources/test/project/)
|
||||
resources/test/project/examples/.dotfiles/script.py:1:8: F401 `numpy` imported but unused
|
||||
resources/test/project/examples/.dotfiles/script.py:2:17: F401 `app.app_file` imported but unused
|
||||
resources/test/project/examples/docs/docs/concepts/file.py:1:8: F401 `os` imported but unused
|
||||
resources/test/project/examples/docs/docs/file.py:1:1: I001 Import block is un-sorted or un-formatted
|
||||
resources/test/project/examples/docs/docs/file.py:1:8: F401 `os` imported but unused
|
||||
resources/test/project/examples/docs/docs/file.py:3:8: F401 `numpy` imported but unused
|
||||
resources/test/project/examples/docs/docs/file.py:4:27: F401 `docs.concepts.file` imported but unused
|
||||
resources/test/project/examples/excluded/script.py:1:8: F401 `os` imported but unused
|
||||
resources/test/project/project/file.py:1:8: F401 `os` imported but unused
|
||||
∴ (cargo run -p ruff_cli -- check --config=crates/ruff/resources/test/project/pyproject.toml crates/ruff/resources/test/project/)
|
||||
crates/ruff/resources/test/project/examples/.dotfiles/script.py:1:8: F401 `numpy` imported but unused
|
||||
crates/ruff/resources/test/project/examples/.dotfiles/script.py:2:17: F401 `app.app_file` imported but unused
|
||||
crates/ruff/resources/test/project/examples/docs/docs/concepts/file.py:1:8: F401 `os` imported but unused
|
||||
crates/ruff/resources/test/project/examples/docs/docs/file.py:1:1: I001 Import block is un-sorted or un-formatted
|
||||
crates/ruff/resources/test/project/examples/docs/docs/file.py:1:8: F401 `os` imported but unused
|
||||
crates/ruff/resources/test/project/examples/docs/docs/file.py:3:8: F401 `numpy` imported but unused
|
||||
crates/ruff/resources/test/project/examples/docs/docs/file.py:4:27: F401 `docs.concepts.file` imported but unused
|
||||
crates/ruff/resources/test/project/examples/excluded/script.py:1:8: F401 `os` imported but unused
|
||||
crates/ruff/resources/test/project/project/file.py:1:8: F401 `os` imported but unused
|
||||
Found 9 errors.
|
||||
9 potentially fixable with the --fix option.
|
||||
```
|
||||
@@ -68,7 +68,7 @@ Running from a parent directory should "ignore" the `exclude` (hence, `concepts/
|
||||
included in the output):
|
||||
|
||||
```console
|
||||
∴ (cd resources/test/project/examples && cargo run -- --config=docs/ruff.toml .)
|
||||
∴ (cd crates/ruff/resources/test/project/examples && cargo run -p ruff_cli -- check --config=docs/ruff.toml .)
|
||||
docs/docs/concepts/file.py:5:5: F841 Local variable `x` is assigned to but never used
|
||||
docs/docs/file.py:1:1: I001 Import block is un-sorted or un-formatted
|
||||
docs/docs/file.py:8:5: F841 Local variable `x` is assigned to but never used
|
||||
@@ -80,8 +80,8 @@ Found 4 errors.
|
||||
Passing an excluded directory directly should report errors in the contained files:
|
||||
|
||||
```console
|
||||
∴ cargo run resources/test/project/examples/excluded/
|
||||
resources/test/project/examples/excluded/script.py:1:8: F401 `os` imported but unused
|
||||
∴ cargo run -p ruff_cli -- check crates/ruff/resources/test/project/examples/excluded/
|
||||
crates/ruff/resources/test/project/examples/excluded/script.py:1:8: F401 `os` imported but unused
|
||||
Found 1 error.
|
||||
1 potentially fixable with the --fix option.
|
||||
```
|
||||
@@ -89,8 +89,8 @@ Found 1 error.
|
||||
Unless we `--force-exclude`:
|
||||
|
||||
```console
|
||||
∴ cargo run resources/test/project/examples/excluded/ --force-exclude
|
||||
∴ cargo run -p ruff_cli -- check crates/ruff/resources/test/project/examples/excluded/ --force-exclude
|
||||
warning: No Python files found under the given path(s)
|
||||
∴ cargo run resources/test/project/examples/excluded/script.py --force-exclude
|
||||
∴ cargo run -p ruff_cli -- check crates/ruff/resources/test/project/examples/excluded/script.py --force-exclude
|
||||
warning: No Python files found under the given path(s)
|
||||
```
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
/// Platform-independent snapshot assertion
|
||||
#[macro_export]
|
||||
macro_rules! assert_yaml_snapshot {
|
||||
( $($args: expr),+) => {
|
||||
let line_sep = if cfg!(windows) { "\r\n" } else { "\n" };
|
||||
|
||||
// adjust snapshot file for platform
|
||||
let mut settings = insta::Settings::clone_current();
|
||||
settings.add_redaction("[].fix.content", insta::dynamic_redaction(move |value, _path| {
|
||||
insta::internals::Content::Seq(
|
||||
value.as_str().unwrap().split(line_sep).map(|line| line.into()).collect()
|
||||
)
|
||||
}));
|
||||
settings.bind(|| {
|
||||
insta::assert_yaml_snapshot!($($args),+);
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -59,6 +59,12 @@ fn alternatives<'a>(stmt: &'a RefEquality<'a, Stmt>) -> Vec<Vec<RefEquality<'a,
|
||||
handlers,
|
||||
orelse,
|
||||
..
|
||||
}
|
||||
| StmtKind::TryStar {
|
||||
body,
|
||||
handlers,
|
||||
orelse,
|
||||
..
|
||||
} => vec![body.iter().chain(orelse.iter()).map(RefEquality).collect()]
|
||||
.into_iter()
|
||||
.chain(handlers.iter().map(|handler| {
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
use num_bigint::BigInt;
|
||||
use rustpython_parser::ast::{
|
||||
Alias, Arg, Arguments, Boolop, Cmpop, Comprehension, Constant, Excepthandler,
|
||||
ExcepthandlerKind, Expr, ExprContext, ExprKind, Keyword, Operator, Stmt, StmtKind, Unaryop,
|
||||
Withitem,
|
||||
ExcepthandlerKind, Expr, ExprContext, ExprKind, Keyword, MatchCase, Operator, Pattern,
|
||||
PatternKind, Stmt, StmtKind, Unaryop, Withitem,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
@@ -157,6 +157,110 @@ impl<'a> From<&'a Withitem> for ComparableWithitem<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub enum ComparablePattern<'a> {
|
||||
MatchValue {
|
||||
value: ComparableExpr<'a>,
|
||||
},
|
||||
MatchSingleton {
|
||||
value: ComparableConstant<'a>,
|
||||
},
|
||||
MatchSequence {
|
||||
patterns: Vec<ComparablePattern<'a>>,
|
||||
},
|
||||
MatchMapping {
|
||||
keys: Vec<ComparableExpr<'a>>,
|
||||
patterns: Vec<ComparablePattern<'a>>,
|
||||
rest: Option<&'a str>,
|
||||
},
|
||||
MatchClass {
|
||||
cls: ComparableExpr<'a>,
|
||||
patterns: Vec<ComparablePattern<'a>>,
|
||||
kwd_attrs: Vec<&'a str>,
|
||||
kwd_patterns: Vec<ComparablePattern<'a>>,
|
||||
},
|
||||
MatchStar {
|
||||
name: Option<&'a str>,
|
||||
},
|
||||
MatchAs {
|
||||
pattern: Option<Box<ComparablePattern<'a>>>,
|
||||
name: Option<&'a str>,
|
||||
},
|
||||
MatchOr {
|
||||
patterns: Vec<ComparablePattern<'a>>,
|
||||
},
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Pattern> for ComparablePattern<'a> {
|
||||
fn from(pattern: &'a Pattern) -> Self {
|
||||
match &pattern.node {
|
||||
PatternKind::MatchValue { value } => Self::MatchValue {
|
||||
value: value.into(),
|
||||
},
|
||||
PatternKind::MatchSingleton { value } => Self::MatchSingleton {
|
||||
value: value.into(),
|
||||
},
|
||||
PatternKind::MatchSequence { patterns } => Self::MatchSequence {
|
||||
patterns: patterns.iter().map(Into::into).collect(),
|
||||
},
|
||||
PatternKind::MatchMapping {
|
||||
keys,
|
||||
patterns,
|
||||
rest,
|
||||
} => Self::MatchMapping {
|
||||
keys: keys.iter().map(Into::into).collect(),
|
||||
patterns: patterns.iter().map(Into::into).collect(),
|
||||
rest: rest.as_deref(),
|
||||
},
|
||||
PatternKind::MatchClass {
|
||||
cls,
|
||||
patterns,
|
||||
kwd_attrs,
|
||||
kwd_patterns,
|
||||
} => Self::MatchClass {
|
||||
cls: cls.into(),
|
||||
patterns: patterns.iter().map(Into::into).collect(),
|
||||
kwd_attrs: kwd_attrs.iter().map(String::as_str).collect(),
|
||||
kwd_patterns: kwd_patterns.iter().map(Into::into).collect(),
|
||||
},
|
||||
PatternKind::MatchStar { name } => Self::MatchStar {
|
||||
name: name.as_deref(),
|
||||
},
|
||||
PatternKind::MatchAs { pattern, name } => Self::MatchAs {
|
||||
pattern: pattern.as_ref().map(Into::into),
|
||||
name: name.as_deref(),
|
||||
},
|
||||
PatternKind::MatchOr { patterns } => Self::MatchOr {
|
||||
patterns: patterns.iter().map(Into::into).collect(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Box<Pattern>> for Box<ComparablePattern<'a>> {
|
||||
fn from(pattern: &'a Box<Pattern>) -> Self {
|
||||
Box::new((&**pattern).into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ComparableMatchCase<'a> {
|
||||
pub pattern: ComparablePattern<'a>,
|
||||
pub guard: Option<ComparableExpr<'a>>,
|
||||
pub body: Vec<ComparableStmt<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a MatchCase> for ComparableMatchCase<'a> {
|
||||
fn from(match_case: &'a MatchCase) -> Self {
|
||||
Self {
|
||||
pattern: (&match_case.pattern).into(),
|
||||
guard: match_case.guard.as_ref().map(Into::into),
|
||||
body: match_case.body.iter().map(Into::into).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub enum ComparableConstant<'a> {
|
||||
None,
|
||||
@@ -644,6 +748,10 @@ pub enum ComparableStmt<'a> {
|
||||
body: Vec<ComparableStmt<'a>>,
|
||||
type_comment: Option<&'a str>,
|
||||
},
|
||||
Match {
|
||||
subject: ComparableExpr<'a>,
|
||||
cases: Vec<ComparableMatchCase<'a>>,
|
||||
},
|
||||
Raise {
|
||||
exc: Option<ComparableExpr<'a>>,
|
||||
cause: Option<ComparableExpr<'a>>,
|
||||
@@ -654,6 +762,12 @@ pub enum ComparableStmt<'a> {
|
||||
orelse: Vec<ComparableStmt<'a>>,
|
||||
finalbody: Vec<ComparableStmt<'a>>,
|
||||
},
|
||||
TryStar {
|
||||
body: Vec<ComparableStmt<'a>>,
|
||||
handlers: Vec<ComparableExcepthandler<'a>>,
|
||||
orelse: Vec<ComparableStmt<'a>>,
|
||||
finalbody: Vec<ComparableStmt<'a>>,
|
||||
},
|
||||
Assert {
|
||||
test: ComparableExpr<'a>,
|
||||
msg: Option<ComparableExpr<'a>>,
|
||||
@@ -811,7 +925,10 @@ impl<'a> From<&'a Stmt> for ComparableStmt<'a> {
|
||||
body: body.iter().map(Into::into).collect(),
|
||||
type_comment: type_comment.as_ref().map(String::as_str),
|
||||
},
|
||||
StmtKind::Match { .. } => unreachable!("StmtKind::Match is not supported"),
|
||||
StmtKind::Match { subject, cases } => Self::Match {
|
||||
subject: subject.into(),
|
||||
cases: cases.iter().map(Into::into).collect(),
|
||||
},
|
||||
StmtKind::Raise { exc, cause } => Self::Raise {
|
||||
exc: exc.as_ref().map(Into::into),
|
||||
cause: cause.as_ref().map(Into::into),
|
||||
@@ -827,6 +944,17 @@ impl<'a> From<&'a Stmt> for ComparableStmt<'a> {
|
||||
orelse: orelse.iter().map(Into::into).collect(),
|
||||
finalbody: finalbody.iter().map(Into::into).collect(),
|
||||
},
|
||||
StmtKind::TryStar {
|
||||
body,
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
} => Self::TryStar {
|
||||
body: body.iter().map(Into::into).collect(),
|
||||
handlers: handlers.iter().map(Into::into).collect(),
|
||||
orelse: orelse.iter().map(Into::into).collect(),
|
||||
finalbody: finalbody.iter().map(Into::into).collect(),
|
||||
},
|
||||
StmtKind::Assert { test, msg } => Self::Assert {
|
||||
test: test.into(),
|
||||
msg: msg.as_ref().map(Into::into),
|
||||
|
||||
@@ -7,7 +7,7 @@ use regex::Regex;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use rustpython_parser::ast::{
|
||||
Arguments, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprKind, Keyword, KeywordData,
|
||||
Located, Location, Stmt, StmtKind,
|
||||
Located, Location, MatchCase, Pattern, PatternKind, Stmt, StmtKind,
|
||||
};
|
||||
use rustpython_parser::lexer;
|
||||
use rustpython_parser::lexer::Tok;
|
||||
@@ -249,6 +249,46 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn any_over_pattern<F>(pattern: &Pattern, func: &F) -> bool
|
||||
where
|
||||
F: Fn(&Expr) -> bool,
|
||||
{
|
||||
match &pattern.node {
|
||||
PatternKind::MatchValue { value } => any_over_expr(value, func),
|
||||
PatternKind::MatchSingleton { .. } => false,
|
||||
PatternKind::MatchSequence { patterns } => patterns
|
||||
.iter()
|
||||
.any(|pattern| any_over_pattern(pattern, func)),
|
||||
PatternKind::MatchMapping { keys, patterns, .. } => {
|
||||
keys.iter().any(|key| any_over_expr(key, func))
|
||||
|| patterns
|
||||
.iter()
|
||||
.any(|pattern| any_over_pattern(pattern, func))
|
||||
}
|
||||
PatternKind::MatchClass {
|
||||
cls,
|
||||
patterns,
|
||||
kwd_patterns,
|
||||
..
|
||||
} => {
|
||||
any_over_expr(cls, func)
|
||||
|| patterns
|
||||
.iter()
|
||||
.any(|pattern| any_over_pattern(pattern, func))
|
||||
|| kwd_patterns
|
||||
.iter()
|
||||
.any(|pattern| any_over_pattern(pattern, func))
|
||||
}
|
||||
PatternKind::MatchStar { .. } => false,
|
||||
PatternKind::MatchAs { pattern, .. } => pattern
|
||||
.as_ref()
|
||||
.map_or(false, |pattern| any_over_pattern(pattern, func)),
|
||||
PatternKind::MatchOr { patterns } => patterns
|
||||
.iter()
|
||||
.any(|pattern| any_over_pattern(pattern, func)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn any_over_stmt<F>(stmt: &Stmt, func: &F) -> bool
|
||||
where
|
||||
F: Fn(&Expr) -> bool,
|
||||
@@ -391,6 +431,12 @@ where
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
}
|
||||
| StmtKind::TryStar {
|
||||
body,
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
} => {
|
||||
any_over_body(body, func)
|
||||
|| handlers.iter().any(|handler| {
|
||||
@@ -409,8 +455,21 @@ where
|
||||
.as_ref()
|
||||
.map_or(false, |value| any_over_expr(value, func))
|
||||
}
|
||||
// TODO(charlie): Handle match statements.
|
||||
StmtKind::Match { .. } => false,
|
||||
StmtKind::Match { subject, cases } => {
|
||||
any_over_expr(subject, func)
|
||||
|| cases.iter().any(|case| {
|
||||
let MatchCase {
|
||||
pattern,
|
||||
guard,
|
||||
body,
|
||||
} = case;
|
||||
any_over_pattern(pattern, func)
|
||||
|| guard
|
||||
.as_ref()
|
||||
.map_or(false, |expr| any_over_expr(expr, func))
|
||||
|| any_over_body(body, func)
|
||||
})
|
||||
}
|
||||
StmtKind::Import { .. } => false,
|
||||
StmtKind::ImportFrom { .. } => false,
|
||||
StmtKind::Global { .. } => false,
|
||||
@@ -596,7 +655,7 @@ pub fn has_comments<T>(located: &Located<T>, locator: &Locator) -> bool {
|
||||
|
||||
/// Returns `true` if a [`Range`] includes at least one comment.
|
||||
pub fn has_comments_in(range: Range, locator: &Locator) -> bool {
|
||||
for tok in lexer::make_tokenizer(locator.slice_source_code_range(&range)) {
|
||||
for tok in lexer::make_tokenizer(locator.slice(&range)) {
|
||||
match tok {
|
||||
Ok((_, tok, _)) => {
|
||||
if matches!(tok, Tok::Comment(..)) {
|
||||
@@ -723,7 +782,7 @@ where
|
||||
StmtKind::FunctionDef { .. } | StmtKind::AsyncFunctionDef { .. } => {
|
||||
// Don't recurse.
|
||||
}
|
||||
StmtKind::Return { value } => self.returns.push(value.as_ref().map(|expr| &**expr)),
|
||||
StmtKind::Return { value } => self.returns.push(value.as_deref()),
|
||||
_ => visitor::walk_stmt(self, stmt),
|
||||
}
|
||||
}
|
||||
@@ -756,7 +815,7 @@ pub fn to_relative(absolute: Location, base: Location) -> Location {
|
||||
/// Return `true` if a [`Located`] has leading content.
|
||||
pub fn match_leading_content<T>(located: &Located<T>, locator: &Locator) -> bool {
|
||||
let range = Range::new(Location::new(located.location.row(), 0), located.location);
|
||||
let prefix = locator.slice_source_code_range(&range);
|
||||
let prefix = locator.slice(&range);
|
||||
prefix.chars().any(|char| !char.is_whitespace())
|
||||
}
|
||||
|
||||
@@ -766,7 +825,7 @@ pub fn match_trailing_content<T>(located: &Located<T>, locator: &Locator) -> boo
|
||||
located.end_location.unwrap(),
|
||||
Location::new(located.end_location.unwrap().row() + 1, 0),
|
||||
);
|
||||
let suffix = locator.slice_source_code_range(&range);
|
||||
let suffix = locator.slice(&range);
|
||||
for char in suffix.chars() {
|
||||
if char == '#' {
|
||||
return false;
|
||||
@@ -784,7 +843,7 @@ pub fn match_trailing_comment<T>(located: &Located<T>, locator: &Locator) -> Opt
|
||||
located.end_location.unwrap(),
|
||||
Location::new(located.end_location.unwrap().row() + 1, 0),
|
||||
);
|
||||
let suffix = locator.slice_source_code_range(&range);
|
||||
let suffix = locator.slice(&range);
|
||||
for (i, char) in suffix.chars().enumerate() {
|
||||
if char == '#' {
|
||||
return Some(i);
|
||||
@@ -798,8 +857,7 @@ pub fn match_trailing_comment<T>(located: &Located<T>, locator: &Locator) -> Opt
|
||||
|
||||
/// Return the number of trailing empty lines following a statement.
|
||||
pub fn count_trailing_lines(stmt: &Stmt, locator: &Locator) -> usize {
|
||||
let suffix =
|
||||
locator.slice_source_code_at(Location::new(stmt.end_location.unwrap().row() + 1, 0));
|
||||
let suffix = locator.skip(Location::new(stmt.end_location.unwrap().row() + 1, 0));
|
||||
suffix
|
||||
.lines()
|
||||
.take_while(|line| line.trim().is_empty())
|
||||
@@ -808,7 +866,7 @@ pub fn count_trailing_lines(stmt: &Stmt, locator: &Locator) -> usize {
|
||||
|
||||
/// Return the range of the first parenthesis pair after a given [`Location`].
|
||||
pub fn match_parens(start: Location, locator: &Locator) -> Option<Range> {
|
||||
let contents = locator.slice_source_code_at(start);
|
||||
let contents = locator.skip(start);
|
||||
let mut fix_start = None;
|
||||
let mut fix_end = None;
|
||||
let mut count: usize = 0;
|
||||
@@ -843,7 +901,7 @@ pub fn identifier_range(stmt: &Stmt, locator: &Locator) -> Range {
|
||||
| StmtKind::FunctionDef { .. }
|
||||
| StmtKind::AsyncFunctionDef { .. }
|
||||
) {
|
||||
let contents = locator.slice_source_code_range(&Range::from_located(stmt));
|
||||
let contents = locator.slice(&Range::from_located(stmt));
|
||||
for (start, tok, end) in lexer::make_tokenizer_located(contents, stmt.location).flatten() {
|
||||
if matches!(tok, Tok::Name { .. }) {
|
||||
return Range::new(start, end);
|
||||
@@ -871,7 +929,7 @@ pub fn binding_range(binding: &Binding, locator: &Locator) -> Range {
|
||||
|
||||
// Return the ranges of `Name` tokens within a specified node.
|
||||
pub fn find_names<T>(located: &Located<T>, locator: &Locator) -> Vec<Range> {
|
||||
let contents = locator.slice_source_code_range(&Range::from_located(located));
|
||||
let contents = locator.slice(&Range::from_located(located));
|
||||
lexer::make_tokenizer_located(contents, located.location)
|
||||
.flatten()
|
||||
.filter(|(_, tok, _)| matches!(tok, Tok::Name { .. }))
|
||||
@@ -890,8 +948,7 @@ pub fn excepthandler_name_range(handler: &Excepthandler, locator: &Locator) -> O
|
||||
match (name, type_) {
|
||||
(Some(_), Some(type_)) => {
|
||||
let type_end_location = type_.end_location.unwrap();
|
||||
let contents =
|
||||
locator.slice_source_code_range(&Range::new(type_end_location, body[0].location));
|
||||
let contents = locator.slice(&Range::new(type_end_location, body[0].location));
|
||||
let range = lexer::make_tokenizer_located(contents, type_end_location)
|
||||
.flatten()
|
||||
.tuple_windows()
|
||||
@@ -915,7 +972,7 @@ pub fn except_range(handler: &Excepthandler, locator: &Locator) -> Range {
|
||||
.expect("Expected body to be non-empty")
|
||||
.location
|
||||
};
|
||||
let contents = locator.slice_source_code_range(&Range {
|
||||
let contents = locator.slice(&Range {
|
||||
location: handler.location,
|
||||
end_location: end,
|
||||
});
|
||||
@@ -932,7 +989,7 @@ pub fn except_range(handler: &Excepthandler, locator: &Locator) -> Range {
|
||||
|
||||
/// Find f-strings that don't contain any formatted values in a `JoinedStr`.
|
||||
pub fn find_useless_f_strings(expr: &Expr, locator: &Locator) -> Vec<(Range, Range)> {
|
||||
let contents = locator.slice_source_code_range(&Range::from_located(expr));
|
||||
let contents = locator.slice(&Range::from_located(expr));
|
||||
lexer::make_tokenizer_located(contents, expr.location)
|
||||
.flatten()
|
||||
.filter_map(|(location, tok, end_location)| match tok {
|
||||
@@ -940,7 +997,7 @@ pub fn find_useless_f_strings(expr: &Expr, locator: &Locator) -> Vec<(Range, Ran
|
||||
kind: StringKind::FString | StringKind::RawFString,
|
||||
..
|
||||
} => {
|
||||
let first_char = locator.slice_source_code_range(&Range {
|
||||
let first_char = locator.slice(&Range {
|
||||
location,
|
||||
end_location: Location::new(location.row(), location.column() + 1),
|
||||
});
|
||||
@@ -980,7 +1037,7 @@ pub fn else_range(stmt: &Stmt, locator: &Locator) -> Option<Range> {
|
||||
.expect("Expected body to be non-empty")
|
||||
.end_location
|
||||
.unwrap();
|
||||
let contents = locator.slice_source_code_range(&Range {
|
||||
let contents = locator.slice(&Range {
|
||||
location: body_end,
|
||||
end_location: orelse
|
||||
.first()
|
||||
@@ -1002,7 +1059,7 @@ pub fn else_range(stmt: &Stmt, locator: &Locator) -> Option<Range> {
|
||||
|
||||
/// Return the `Range` of the first `Tok::Colon` token in a `Range`.
|
||||
pub fn first_colon_range(range: Range, locator: &Locator) -> Option<Range> {
|
||||
let contents = locator.slice_source_code_range(&range);
|
||||
let contents = locator.slice(&range);
|
||||
let range = lexer::make_tokenizer_located(contents, range.location)
|
||||
.flatten()
|
||||
.find(|(_, kind, _)| matches!(kind, Tok::Colon))
|
||||
@@ -1032,7 +1089,7 @@ pub fn elif_else_range(stmt: &Stmt, locator: &Locator) -> Option<Range> {
|
||||
[stmt, ..] => stmt.location,
|
||||
_ => return None,
|
||||
};
|
||||
let contents = locator.slice_source_code_range(&Range::new(start, end));
|
||||
let contents = locator.slice(&Range::new(start, end));
|
||||
let range = lexer::make_tokenizer_located(contents, start)
|
||||
.flatten()
|
||||
.find(|(_, kind, _)| matches!(kind, Tok::Elif | Tok::Else))
|
||||
|
||||
@@ -181,7 +181,10 @@ pub fn extract_globals(body: &[Stmt]) -> FxHashMap<&str, &Stmt> {
|
||||
/// Check if a node is parent of a conditional branch.
|
||||
pub fn on_conditional_branch<'a>(parents: &mut impl Iterator<Item = &'a Stmt>) -> bool {
|
||||
parents.any(|parent| {
|
||||
if matches!(parent.node, StmtKind::If { .. } | StmtKind::While { .. }) {
|
||||
if matches!(
|
||||
parent.node,
|
||||
StmtKind::If { .. } | StmtKind::While { .. } | StmtKind::Match { .. }
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if let StmtKind::Expr { value } = &parent.node {
|
||||
@@ -198,7 +201,10 @@ pub fn in_nested_block<'a>(mut parents: impl Iterator<Item = &'a Stmt>) -> bool
|
||||
parents.any(|parent| {
|
||||
matches!(
|
||||
parent.node,
|
||||
StmtKind::Try { .. } | StmtKind::If { .. } | StmtKind::With { .. }
|
||||
StmtKind::Try { .. }
|
||||
| StmtKind::TryStar { .. }
|
||||
| StmtKind::If { .. }
|
||||
| StmtKind::With { .. }
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -304,13 +310,14 @@ pub fn locate_cmpops(contents: &str) -> Vec<LocatedCmpop> {
|
||||
ops.push(LocatedCmpop::new(start, end, Cmpop::In));
|
||||
}
|
||||
Tok::Is => {
|
||||
if let Some((_, _, end)) =
|
||||
let op = if let Some((_, _, end)) =
|
||||
tok_iter.next_if(|(_, tok, _)| matches!(tok, Tok::Not))
|
||||
{
|
||||
ops.push(LocatedCmpop::new(start, end, Cmpop::IsNot));
|
||||
LocatedCmpop::new(start, end, Cmpop::IsNot)
|
||||
} else {
|
||||
ops.push(LocatedCmpop::new(start, end, Cmpop::Is));
|
||||
}
|
||||
LocatedCmpop::new(start, end, Cmpop::Is)
|
||||
};
|
||||
ops.push(op);
|
||||
}
|
||||
Tok::NotEqual => {
|
||||
ops.push(LocatedCmpop::new(start, end, Cmpop::NotEq));
|
||||
|
||||
@@ -205,7 +205,6 @@ pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) {
|
||||
visitor.visit_body(body);
|
||||
}
|
||||
StmtKind::Match { subject, cases } => {
|
||||
// TODO(charlie): Handle `cases`.
|
||||
visitor.visit_expr(subject);
|
||||
for match_case in cases {
|
||||
visitor.visit_match_case(match_case);
|
||||
@@ -232,6 +231,19 @@ pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) {
|
||||
visitor.visit_body(orelse);
|
||||
visitor.visit_body(finalbody);
|
||||
}
|
||||
StmtKind::TryStar {
|
||||
body,
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
} => {
|
||||
visitor.visit_body(body);
|
||||
for excepthandler in handlers {
|
||||
visitor.visit_excepthandler(excepthandler);
|
||||
}
|
||||
visitor.visit_body(orelse);
|
||||
visitor.visit_body(finalbody);
|
||||
}
|
||||
StmtKind::Assert { test, msg } => {
|
||||
visitor.visit_expr(test);
|
||||
if let Some(expr) = msg {
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::source_code::Locator;
|
||||
/// Extract the leading indentation from a line.
|
||||
pub fn indentation<'a, T>(locator: &'a Locator, located: &'a Located<T>) -> Option<&'a str> {
|
||||
let range = Range::from_located(located);
|
||||
let indentation = locator.slice_source_code_range(&Range::new(
|
||||
let indentation = locator.slice(&Range::new(
|
||||
Location::new(range.location.row(), 0),
|
||||
Location::new(range.location.row(), range.location.column()),
|
||||
));
|
||||
|
||||
@@ -53,6 +53,12 @@ fn is_lone_child(child: &Stmt, parent: &Stmt, deleted: &[&Stmt]) -> Result<bool>
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
}
|
||||
| StmtKind::TryStar {
|
||||
body,
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
} => {
|
||||
if body.iter().contains(child) {
|
||||
Ok(has_single_child(body, deleted))
|
||||
@@ -74,6 +80,19 @@ fn is_lone_child(child: &Stmt, parent: &Stmt, deleted: &[&Stmt]) -> Result<bool>
|
||||
bail!("Unable to find child in parent body")
|
||||
}
|
||||
}
|
||||
StmtKind::Match { cases, .. } => {
|
||||
if let Some(body) = cases.iter().find_map(|case| {
|
||||
if case.body.iter().contains(child) {
|
||||
Some(&case.body)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
Ok(has_single_child(body, deleted))
|
||||
} else {
|
||||
bail!("Unable to find child in parent body")
|
||||
}
|
||||
}
|
||||
_ => bail!("Unable to find child in parent body"),
|
||||
}
|
||||
}
|
||||
@@ -81,7 +100,7 @@ fn is_lone_child(child: &Stmt, parent: &Stmt, deleted: &[&Stmt]) -> Result<bool>
|
||||
/// Return the location of a trailing semicolon following a `Stmt`, if it's part
|
||||
/// of a multi-statement line.
|
||||
fn trailing_semicolon(stmt: &Stmt, locator: &Locator) -> Option<Location> {
|
||||
let contents = locator.slice_source_code_at(stmt.end_location.unwrap());
|
||||
let contents = locator.skip(stmt.end_location.unwrap());
|
||||
for (row, line) in LinesWithTrailingNewline::from(contents).enumerate() {
|
||||
let trimmed = line.trim();
|
||||
if trimmed.starts_with(';') {
|
||||
@@ -104,7 +123,7 @@ fn trailing_semicolon(stmt: &Stmt, locator: &Locator) -> Option<Location> {
|
||||
/// Find the next valid break for a `Stmt` after a semicolon.
|
||||
fn next_stmt_break(semicolon: Location, locator: &Locator) -> Location {
|
||||
let start_location = Location::new(semicolon.row(), semicolon.column() + 1);
|
||||
let contents = locator.slice_source_code_at(start_location);
|
||||
let contents = locator.skip(start_location);
|
||||
for (row, line) in LinesWithTrailingNewline::from(contents).enumerate() {
|
||||
let trimmed = line.trim();
|
||||
// Skip past any continuations.
|
||||
@@ -136,7 +155,7 @@ fn next_stmt_break(semicolon: Location, locator: &Locator) -> Location {
|
||||
|
||||
/// Return `true` if a `Stmt` occurs at the end of a file.
|
||||
fn is_end_of_file(stmt: &Stmt, locator: &Locator) -> bool {
|
||||
let contents = locator.slice_source_code_at(stmt.end_location.unwrap());
|
||||
let contents = locator.skip(stmt.end_location.unwrap());
|
||||
contents.is_empty()
|
||||
}
|
||||
|
||||
@@ -209,7 +228,7 @@ pub fn remove_unused_imports<'a>(
|
||||
indexer: &Indexer,
|
||||
stylist: &Stylist,
|
||||
) -> Result<Fix> {
|
||||
let module_text = locator.slice_source_code_range(&Range::from_located(stmt));
|
||||
let module_text = locator.slice(&Range::from_located(stmt));
|
||||
let mut tree = match_module(module_text)?;
|
||||
|
||||
let Some(Statement::Simple(body)) = tree.body.first_mut() else {
|
||||
@@ -339,7 +358,7 @@ pub fn remove_argument(
|
||||
remove_parentheses: bool,
|
||||
) -> Result<Fix> {
|
||||
// TODO(sbrugman): Preserve trailing comments.
|
||||
let contents = locator.slice_source_code_at(stmt_at);
|
||||
let contents = locator.skip(stmt_at);
|
||||
|
||||
let mut fix_start = None;
|
||||
let mut fix_end = None;
|
||||
|
||||
@@ -54,7 +54,7 @@ fn apply_fixes<'a>(
|
||||
}
|
||||
|
||||
// Add all contents from `last_pos` to `fix.location`.
|
||||
let slice = locator.slice_source_code_range(&Range::new(last_pos, fix.location));
|
||||
let slice = locator.slice(&Range::new(last_pos, fix.location));
|
||||
output.push_str(slice);
|
||||
|
||||
// Add the patch itself.
|
||||
@@ -67,7 +67,7 @@ fn apply_fixes<'a>(
|
||||
}
|
||||
|
||||
// Add the remaining content.
|
||||
let slice = locator.slice_source_code_at(last_pos);
|
||||
let slice = locator.skip(last_pos);
|
||||
output.push_str(slice);
|
||||
|
||||
(output, fixed)
|
||||
@@ -78,14 +78,14 @@ pub(crate) fn apply_fix(fix: &Fix, locator: &Locator) -> String {
|
||||
let mut output = String::with_capacity(locator.len());
|
||||
|
||||
// Add all contents from `last_pos` to `fix.location`.
|
||||
let slice = locator.slice_source_code_range(&Range::new(Location::new(1, 0), fix.location));
|
||||
let slice = locator.slice(&Range::new(Location::new(1, 0), fix.location));
|
||||
output.push_str(slice);
|
||||
|
||||
// Add the patch itself.
|
||||
output.push_str(&fix.content);
|
||||
|
||||
// Add the remaining content.
|
||||
let slice = locator.slice_source_code_at(fix.end_location);
|
||||
let slice = locator.skip(fix.end_location);
|
||||
output.push_str(slice);
|
||||
|
||||
output
|
||||
|
||||
@@ -10,7 +10,8 @@ use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use rustpython_common::cformat::{CFormatError, CFormatErrorType};
|
||||
use rustpython_parser::ast::{
|
||||
Arg, Arguments, Comprehension, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprContext,
|
||||
ExprKind, KeywordData, Located, Location, Operator, Stmt, StmtKind, Suite,
|
||||
ExprKind, KeywordData, Located, Location, Operator, Pattern, PatternKind, Stmt, StmtKind,
|
||||
Suite,
|
||||
};
|
||||
use rustpython_parser::parser;
|
||||
use smallvec::smallvec;
|
||||
@@ -28,18 +29,19 @@ use crate::ast::types::{
|
||||
RefEquality, Scope, ScopeKind,
|
||||
};
|
||||
use crate::ast::typing::{match_annotated_subscript, Callable, SubscriptKind};
|
||||
use crate::ast::visitor::{walk_excepthandler, Visitor};
|
||||
use crate::ast::visitor::{walk_excepthandler, walk_pattern, Visitor};
|
||||
use crate::ast::{branch_detection, cast, helpers, operations, typing, visitor};
|
||||
use crate::docstrings::definition::{Definition, DefinitionKind, Docstring, Documentable};
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::resolver::is_interface_definition_path;
|
||||
use crate::rules::{
|
||||
flake8_2020, flake8_annotations, flake8_bandit, flake8_blind_except, flake8_boolean_trap,
|
||||
flake8_bugbear, flake8_builtins, flake8_comprehensions, flake8_datetimez, flake8_debugger,
|
||||
flake8_django, flake8_errmsg, flake8_implicit_str_concat, flake8_import_conventions,
|
||||
flake8_logging_format, flake8_pie, flake8_print, flake8_pyi, flake8_pytest_style, flake8_raise,
|
||||
flake8_return, flake8_self, flake8_simplify, flake8_tidy_imports, flake8_type_checking,
|
||||
flake8_unused_arguments, flake8_use_pathlib, mccabe, pandas_vet, pep8_naming, pycodestyle,
|
||||
pydocstyle, pyflakes, pygrep_hooks, pylint, pyupgrade, ruff, tryceratops,
|
||||
flake8_unused_arguments, flake8_use_pathlib, mccabe, numpy, pandas_vet, pep8_naming,
|
||||
pycodestyle, pydocstyle, pyflakes, pygrep_hooks, pylint, pyupgrade, ruff, tryceratops,
|
||||
};
|
||||
use crate::settings::types::PythonVersion;
|
||||
use crate::settings::{flags, Settings};
|
||||
@@ -50,6 +52,7 @@ use crate::{autofix, docstrings, noqa, visibility};
|
||||
const GLOBAL_SCOPE_INDEX: usize = 0;
|
||||
|
||||
type DeferralContext<'a> = (Vec<usize>, Vec<RefEquality<'a, Stmt>>);
|
||||
type AnnotationContext = (bool, bool);
|
||||
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub struct Checker<'a> {
|
||||
@@ -57,6 +60,7 @@ pub struct Checker<'a> {
|
||||
pub(crate) path: &'a Path,
|
||||
module_path: Option<Vec<String>>,
|
||||
package: Option<&'a Path>,
|
||||
is_interface_definition: bool,
|
||||
autofix: flags::Autofix,
|
||||
noqa: flags::Noqa,
|
||||
pub(crate) settings: &'a Settings,
|
||||
@@ -84,8 +88,8 @@ pub struct Checker<'a> {
|
||||
pub(crate) scopes: Vec<Scope<'a>>,
|
||||
pub(crate) scope_stack: Vec<usize>,
|
||||
pub(crate) dead_scopes: Vec<(usize, Vec<usize>)>,
|
||||
deferred_string_type_definitions: Vec<(Range, &'a str, bool, DeferralContext<'a>)>,
|
||||
deferred_type_definitions: Vec<(&'a Expr, bool, DeferralContext<'a>)>,
|
||||
deferred_string_type_definitions: Vec<(Range, &'a str, AnnotationContext, DeferralContext<'a>)>,
|
||||
deferred_type_definitions: Vec<(&'a Expr, AnnotationContext, DeferralContext<'a>)>,
|
||||
deferred_functions: Vec<(&'a Stmt, DeferralContext<'a>, VisibleScope)>,
|
||||
deferred_lambdas: Vec<(&'a Expr, DeferralContext<'a>)>,
|
||||
deferred_for_loops: Vec<(&'a Stmt, DeferralContext<'a>)>,
|
||||
@@ -125,6 +129,7 @@ impl<'a> Checker<'a> {
|
||||
style: &'a Stylist,
|
||||
indexer: &'a Indexer,
|
||||
) -> Checker<'a> {
|
||||
let is_interface_definition = is_interface_definition_path(path);
|
||||
Checker {
|
||||
settings,
|
||||
noqa_line_for,
|
||||
@@ -133,6 +138,7 @@ impl<'a> Checker<'a> {
|
||||
path,
|
||||
package,
|
||||
module_path,
|
||||
is_interface_definition,
|
||||
locator,
|
||||
stylist: style,
|
||||
indexer,
|
||||
@@ -172,7 +178,7 @@ impl<'a> Checker<'a> {
|
||||
in_type_checking_block: false,
|
||||
seen_import_boundary: false,
|
||||
futures_allowed: true,
|
||||
annotations_future_enabled: path.extension().map_or(false, |ext| ext == "pyi"),
|
||||
annotations_future_enabled: is_interface_definition,
|
||||
except_handlers: vec![],
|
||||
// Check-specific state.
|
||||
flake8_bugbear_seen: vec![],
|
||||
@@ -234,51 +240,51 @@ impl<'a> Checker<'a> {
|
||||
'b: 'a,
|
||||
{
|
||||
let call_path = collect_call_path(value);
|
||||
if let Some(head) = call_path.first() {
|
||||
if let Some(binding) = self.find_binding(head) {
|
||||
match &binding.kind {
|
||||
BindingKind::Importation(.., name)
|
||||
| BindingKind::SubmoduleImportation(name, ..) => {
|
||||
return if name.starts_with('.') {
|
||||
if let Some(module) = &self.module_path {
|
||||
let mut source_path = from_relative_import(module, name);
|
||||
source_path.extend(call_path.into_iter().skip(1));
|
||||
Some(source_path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
let mut source_path: CallPath = name.split('.').collect();
|
||||
source_path.extend(call_path.into_iter().skip(1));
|
||||
Some(source_path)
|
||||
};
|
||||
let Some(head) = call_path.first() else {
|
||||
return None;
|
||||
};
|
||||
let Some(binding) = self.find_binding(head) else {
|
||||
return None;
|
||||
};
|
||||
match &binding.kind {
|
||||
BindingKind::Importation(.., name) | BindingKind::SubmoduleImportation(name, ..) => {
|
||||
if name.starts_with('.') {
|
||||
if let Some(module) = &self.module_path {
|
||||
let mut source_path = from_relative_import(module, name);
|
||||
source_path.extend(call_path.into_iter().skip(1));
|
||||
Some(source_path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
BindingKind::FromImportation(.., name) => {
|
||||
return if name.starts_with('.') {
|
||||
if let Some(module) = &self.module_path {
|
||||
let mut source_path = from_relative_import(module, name);
|
||||
source_path.extend(call_path.into_iter().skip(1));
|
||||
Some(source_path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
let mut source_path: CallPath = name.split('.').collect();
|
||||
source_path.extend(call_path.into_iter().skip(1));
|
||||
Some(source_path)
|
||||
};
|
||||
}
|
||||
BindingKind::Builtin => {
|
||||
let mut source_path: CallPath = smallvec![];
|
||||
source_path.push("");
|
||||
source_path.extend(call_path);
|
||||
return Some(source_path);
|
||||
}
|
||||
_ => {}
|
||||
} else {
|
||||
let mut source_path: CallPath = name.split('.').collect();
|
||||
source_path.extend(call_path.into_iter().skip(1));
|
||||
Some(source_path)
|
||||
}
|
||||
}
|
||||
BindingKind::FromImportation(.., name) => {
|
||||
if name.starts_with('.') {
|
||||
if let Some(module) = &self.module_path {
|
||||
let mut source_path = from_relative_import(module, name);
|
||||
source_path.extend(call_path.into_iter().skip(1));
|
||||
Some(source_path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
let mut source_path: CallPath = name.split('.').collect();
|
||||
source_path.extend(call_path.into_iter().skip(1));
|
||||
Some(source_path)
|
||||
}
|
||||
}
|
||||
BindingKind::Builtin => {
|
||||
let mut source_path: CallPath = smallvec![];
|
||||
source_path.push("");
|
||||
source_path.extend(call_path);
|
||||
Some(source_path)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Return `true` if a `Rule` is disabled by a `noqa` directive.
|
||||
@@ -464,14 +470,16 @@ where
|
||||
body,
|
||||
..
|
||||
} => {
|
||||
if self.settings.rules.enabled(&Rule::ReceiverDecoratorChecker) {
|
||||
if let Some(diagnostic) =
|
||||
flake8_django::rules::receiver_decorator_checker(decorator_list, |expr| {
|
||||
self.resolve_call_path(expr)
|
||||
})
|
||||
{
|
||||
self.diagnostics.push(diagnostic);
|
||||
}
|
||||
if self
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::NonLeadingReceiverDecorator)
|
||||
{
|
||||
self.diagnostics
|
||||
.extend(flake8_django::rules::non_leading_receiver_decorator(
|
||||
decorator_list,
|
||||
|expr| self.resolve_call_path(expr),
|
||||
));
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::AmbiguousFunctionName) {
|
||||
if let Some(diagnostic) =
|
||||
@@ -699,7 +707,12 @@ where
|
||||
.rules
|
||||
.enabled(&Rule::BooleanPositionalArgInFunctionDefinition)
|
||||
{
|
||||
flake8_boolean_trap::rules::check_positional_boolean_in_def(self, name, args);
|
||||
flake8_boolean_trap::rules::check_positional_boolean_in_def(
|
||||
self,
|
||||
name,
|
||||
decorator_list,
|
||||
args,
|
||||
);
|
||||
}
|
||||
|
||||
if self
|
||||
@@ -708,7 +721,10 @@ where
|
||||
.enabled(&Rule::BooleanDefaultValueInFunctionDefinition)
|
||||
{
|
||||
flake8_boolean_trap::rules::check_boolean_default_value_in_function_definition(
|
||||
self, name, args,
|
||||
self,
|
||||
name,
|
||||
decorator_list,
|
||||
args,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -772,6 +788,9 @@ where
|
||||
if self.settings.rules.enabled(&Rule::ReturnOutsideFunction) {
|
||||
pyflakes::rules::return_outside_function(self, stmt);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::ReturnInInit) {
|
||||
pylint::rules::return_in_init(self, stmt);
|
||||
}
|
||||
}
|
||||
StmtKind::ClassDef {
|
||||
name,
|
||||
@@ -780,15 +799,15 @@ where
|
||||
decorator_list,
|
||||
body,
|
||||
} => {
|
||||
if self.settings.rules.enabled(&Rule::ModelStringFieldNullable) {
|
||||
if self.settings.rules.enabled(&Rule::NullableModelStringField) {
|
||||
self.diagnostics
|
||||
.extend(flake8_django::rules::model_string_field_nullable(
|
||||
self, bases, body,
|
||||
.extend(flake8_django::rules::nullable_model_string_field(
|
||||
self, body,
|
||||
));
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::ModelDunderStr) {
|
||||
if self.settings.rules.enabled(&Rule::ModelWithoutDunderStr) {
|
||||
if let Some(diagnostic) =
|
||||
flake8_django::rules::model_dunder_str(self, bases, body, stmt)
|
||||
flake8_django::rules::model_without_dunder_str(self, bases, body, stmt)
|
||||
{
|
||||
self.diagnostics.push(diagnostic);
|
||||
}
|
||||
@@ -832,18 +851,20 @@ where
|
||||
flake8_bugbear::rules::useless_expression(self, body);
|
||||
}
|
||||
|
||||
if self
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::AbstractBaseClassWithoutAbstractMethod)
|
||||
|| self
|
||||
if !self.is_interface_definition {
|
||||
if self
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::EmptyMethodWithoutAbstractDecorator)
|
||||
{
|
||||
flake8_bugbear::rules::abstract_base_class(
|
||||
self, stmt, name, bases, keywords, body,
|
||||
);
|
||||
.enabled(&Rule::AbstractBaseClassWithoutAbstractMethod)
|
||||
|| self
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::EmptyMethodWithoutAbstractDecorator)
|
||||
{
|
||||
flake8_bugbear::rules::abstract_base_class(
|
||||
self, stmt, name, bases, keywords, body,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if self
|
||||
@@ -897,7 +918,38 @@ where
|
||||
}
|
||||
|
||||
for alias in names {
|
||||
if alias.node.name.contains('.') && alias.node.asname.is_none() {
|
||||
if alias.node.name == "__future__" {
|
||||
let name = alias.node.asname.as_ref().unwrap_or(&alias.node.name);
|
||||
self.add_binding(
|
||||
name,
|
||||
Binding {
|
||||
kind: BindingKind::FutureImportation,
|
||||
runtime_usage: None,
|
||||
// Always mark `__future__` imports as used.
|
||||
synthetic_usage: Some((
|
||||
self.scopes[*(self
|
||||
.scope_stack
|
||||
.last()
|
||||
.expect("No current scope found"))]
|
||||
.id,
|
||||
Range::from_located(alias),
|
||||
)),
|
||||
typing_usage: None,
|
||||
range: Range::from_located(alias),
|
||||
source: Some(self.current_stmt().clone()),
|
||||
context: self.execution_context(),
|
||||
},
|
||||
);
|
||||
|
||||
if self.settings.rules.enabled(&Rule::LateFutureImport)
|
||||
&& !self.futures_allowed
|
||||
{
|
||||
self.diagnostics.push(Diagnostic::new(
|
||||
pyflakes::rules::LateFutureImport,
|
||||
Range::from_located(stmt),
|
||||
));
|
||||
}
|
||||
} else if alias.node.name.contains('.') && alias.node.asname.is_none() {
|
||||
// Given `import foo.bar`, `name` would be "foo", and `full_name` would be
|
||||
// "foo.bar".
|
||||
let name = alias.node.name.split('.').next().unwrap();
|
||||
@@ -915,10 +967,6 @@ where
|
||||
},
|
||||
);
|
||||
} else {
|
||||
if let Some(asname) = &alias.node.asname {
|
||||
self.check_builtin_shadowing(asname, stmt, false);
|
||||
}
|
||||
|
||||
// Given `import foo`, `name` and `full_name` would both be `foo`.
|
||||
// Given `import foo as bar`, `name` would be `bar` and `full_name` would
|
||||
// be `foo`.
|
||||
@@ -954,6 +1002,10 @@ where
|
||||
context: self.execution_context(),
|
||||
},
|
||||
);
|
||||
|
||||
if let Some(asname) = &alias.node.asname {
|
||||
self.check_builtin_shadowing(asname, stmt, false);
|
||||
}
|
||||
}
|
||||
|
||||
// flake8-debugger
|
||||
@@ -1317,8 +1369,8 @@ where
|
||||
stmt,
|
||||
level.as_ref(),
|
||||
module.as_deref(),
|
||||
self.module_path.as_ref(),
|
||||
&self.settings.flake8_tidy_imports.ban_relative_imports,
|
||||
self.path,
|
||||
)
|
||||
{
|
||||
self.diagnostics.push(diagnostic);
|
||||
@@ -1522,6 +1574,9 @@ where
|
||||
if self.settings.rules.enabled(&Rule::NeedlessBool) {
|
||||
flake8_simplify::rules::return_bool_condition_directly(self, stmt);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::ManualDictLookup) {
|
||||
flake8_simplify::rules::manual_dict_lookup(self, stmt, test, body, orelse);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::UseTernaryOperator) {
|
||||
flake8_simplify::rules::use_ternary_operator(
|
||||
self,
|
||||
@@ -1557,12 +1612,7 @@ where
|
||||
pyflakes::rules::assert_tuple(self, stmt, test);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::AssertFalse) {
|
||||
flake8_bugbear::rules::assert_false(
|
||||
self,
|
||||
stmt,
|
||||
test,
|
||||
msg.as_ref().map(|expr| &**expr),
|
||||
);
|
||||
flake8_bugbear::rules::assert_false(self, stmt, test, msg.as_deref());
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::Assert) {
|
||||
self.diagnostics
|
||||
@@ -1574,11 +1624,12 @@ where
|
||||
}
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::CompositeAssertion) {
|
||||
if let Some(diagnostic) =
|
||||
flake8_pytest_style::rules::composite_condition(stmt, test)
|
||||
{
|
||||
self.diagnostics.push(diagnostic);
|
||||
}
|
||||
flake8_pytest_style::rules::composite_condition(
|
||||
self,
|
||||
stmt,
|
||||
test,
|
||||
msg.as_deref(),
|
||||
);
|
||||
}
|
||||
}
|
||||
StmtKind::With { items, body, .. } => {
|
||||
@@ -1645,9 +1696,7 @@ where
|
||||
pylint::rules::useless_else_on_loop(self, stmt, body, orelse);
|
||||
}
|
||||
if matches!(stmt.node, StmtKind::For { .. }) {
|
||||
if self.settings.rules.enabled(&Rule::ConvertLoopToAny)
|
||||
|| self.settings.rules.enabled(&Rule::ConvertLoopToAll)
|
||||
{
|
||||
if self.settings.rules.enabled(&Rule::ReimplementedBuiltin) {
|
||||
flake8_simplify::rules::convert_for_loop_to_any_all(
|
||||
self,
|
||||
stmt,
|
||||
@@ -1665,6 +1714,13 @@ where
|
||||
orelse,
|
||||
finalbody,
|
||||
..
|
||||
}
|
||||
| StmtKind::TryStar {
|
||||
body,
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
..
|
||||
} => {
|
||||
if self.settings.rules.enabled(&Rule::DefaultExceptNotLast) {
|
||||
if let Some(diagnostic) =
|
||||
@@ -1715,6 +1771,9 @@ where
|
||||
if self.settings.rules.enabled(&Rule::VerboseRaise) {
|
||||
tryceratops::rules::verbose_raise(self, handlers);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::VerboseLogMessage) {
|
||||
tryceratops::rules::verbose_log_message(self, handlers);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::RaiseWithinTry) {
|
||||
tryceratops::rules::raise_within_try(self, body);
|
||||
}
|
||||
@@ -1741,8 +1800,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
if self.settings.rules.enabled(&Rule::PrefixTypeParams) {
|
||||
if self.path.extension().map_or(false, |ext| ext == "pyi") {
|
||||
if self.is_interface_definition {
|
||||
if self.settings.rules.enabled(&Rule::PrefixTypeParams) {
|
||||
flake8_pyi::rules::prefix_type_params(self, value, targets);
|
||||
}
|
||||
}
|
||||
@@ -1784,6 +1843,18 @@ where
|
||||
pycodestyle::rules::lambda_assignment(self, target, value, stmt);
|
||||
}
|
||||
}
|
||||
if self
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::UnintentionalTypeAnnotation)
|
||||
{
|
||||
flake8_bugbear::rules::unintentional_type_annotation(
|
||||
self,
|
||||
target,
|
||||
value.as_deref(),
|
||||
stmt,
|
||||
);
|
||||
}
|
||||
}
|
||||
StmtKind::Delete { .. } => {}
|
||||
StmtKind::Expr { value, .. } => {
|
||||
@@ -1797,6 +1868,13 @@ where
|
||||
{
|
||||
flake8_simplify::rules::use_capital_environment_variables(self, value);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::AsyncioDanglingTask) {
|
||||
if let Some(diagnostic) = ruff::rules::asyncio_dangling_task(value, |expr| {
|
||||
self.resolve_call_path(expr)
|
||||
}) {
|
||||
self.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -1942,6 +2020,12 @@ where
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
}
|
||||
| StmtKind::TryStar {
|
||||
body,
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
} => {
|
||||
// TODO(charlie): The use of `smallvec` here leads to a lifetime issue.
|
||||
let handler_names = extract_handler_names(handlers)
|
||||
@@ -2067,13 +2151,13 @@ where
|
||||
self.deferred_string_type_definitions.push((
|
||||
Range::from_located(expr),
|
||||
value,
|
||||
self.in_annotation,
|
||||
(self.in_annotation, self.in_type_checking_block),
|
||||
(self.scope_stack.clone(), self.parents.clone()),
|
||||
));
|
||||
} else {
|
||||
self.deferred_type_definitions.push((
|
||||
expr,
|
||||
self.in_annotation,
|
||||
(self.in_annotation, self.in_type_checking_block),
|
||||
(self.scope_stack.clone(), self.parents.clone()),
|
||||
));
|
||||
}
|
||||
@@ -2142,6 +2226,9 @@ where
|
||||
if self.settings.rules.enabled(&Rule::TypingTextStrAlias) {
|
||||
pyupgrade::rules::typing_text_str_alias(self, expr);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::NumpyDeprecatedTypeAlias) {
|
||||
numpy::rules::deprecated_type_alias(self, expr);
|
||||
}
|
||||
|
||||
// Ex) List[...]
|
||||
if !self.in_deferred_string_type_definition
|
||||
@@ -2208,6 +2295,9 @@ where
|
||||
if self.settings.rules.enabled(&Rule::TypingTextStrAlias) {
|
||||
pyupgrade::rules::typing_text_str_alias(self, expr);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::NumpyDeprecatedTypeAlias) {
|
||||
numpy::rules::deprecated_type_alias(self, expr);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::RewriteMockImport) {
|
||||
pyupgrade::rules::rewrite_mock_attribute(self, expr);
|
||||
}
|
||||
@@ -2515,7 +2605,12 @@ where
|
||||
.enabled(&Rule::UnnecessaryCollectionCall)
|
||||
{
|
||||
flake8_comprehensions::rules::unnecessary_collection_call(
|
||||
self, expr, func, args, keywords,
|
||||
self,
|
||||
expr,
|
||||
func,
|
||||
args,
|
||||
keywords,
|
||||
&self.settings.flake8_comprehensions,
|
||||
);
|
||||
}
|
||||
if self
|
||||
@@ -2800,6 +2895,11 @@ where
|
||||
flake8_use_pathlib::helpers::replaceable_by_pathlib(self, func);
|
||||
}
|
||||
|
||||
// numpy
|
||||
if self.settings.rules.enabled(&Rule::NumpyLegacyRandom) {
|
||||
numpy::rules::numpy_legacy_random(self, func);
|
||||
}
|
||||
|
||||
// flake8-logging-format
|
||||
if self.settings.rules.enabled(&Rule::LoggingStringFormat)
|
||||
|| self.settings.rules.enabled(&Rule::LoggingPercentFormat)
|
||||
@@ -3163,13 +3263,13 @@ where
|
||||
flake8_simplify::rules::yoda_conditions(self, expr, left, ops, comparators);
|
||||
}
|
||||
|
||||
if self
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::UnrecognizedPlatformCheck)
|
||||
|| self.settings.rules.enabled(&Rule::UnrecognizedPlatformName)
|
||||
{
|
||||
if self.path.extension().map_or(false, |ext| ext == "pyi") {
|
||||
if self.is_interface_definition {
|
||||
if self
|
||||
.settings
|
||||
.rules
|
||||
.enabled(&Rule::UnrecognizedPlatformCheck)
|
||||
|| self.settings.rules.enabled(&Rule::UnrecognizedPlatformName)
|
||||
{
|
||||
flake8_pyi::rules::unrecognized_platform(
|
||||
self,
|
||||
expr,
|
||||
@@ -3188,7 +3288,7 @@ where
|
||||
self.deferred_string_type_definitions.push((
|
||||
Range::from_located(expr),
|
||||
value,
|
||||
self.in_annotation,
|
||||
(self.in_annotation, self.in_type_checking_block),
|
||||
(self.scope_stack.clone(), self.parents.clone()),
|
||||
));
|
||||
}
|
||||
@@ -3642,6 +3742,9 @@ where
|
||||
self.settings.flake8_bandit.check_typed_exception,
|
||||
);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::ExceptWithEmptyTuple) {
|
||||
flake8_bugbear::rules::except_with_empty_tuple(self, excepthandler);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::ReraiseNoCause) {
|
||||
tryceratops::rules::reraise_no_cause(self, body);
|
||||
}
|
||||
@@ -3738,6 +3841,28 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_pattern(&mut self, pattern: &'b Pattern) {
|
||||
if let PatternKind::MatchAs {
|
||||
name: Some(name), ..
|
||||
} = &pattern.node
|
||||
{
|
||||
self.add_binding(
|
||||
name,
|
||||
Binding {
|
||||
kind: BindingKind::Assignment,
|
||||
runtime_usage: None,
|
||||
synthetic_usage: None,
|
||||
typing_usage: None,
|
||||
range: Range::from_located(pattern),
|
||||
source: Some(self.current_stmt().clone()),
|
||||
context: self.execution_context(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
walk_pattern(self, pattern);
|
||||
}
|
||||
|
||||
fn visit_format_spec(&mut self, format_spec: &'b Expr) {
|
||||
match &format_spec.node {
|
||||
ExprKind::JoinedStr { values } => {
|
||||
@@ -4109,146 +4234,147 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
|
||||
fn handle_node_load(&mut self, expr: &Expr) {
|
||||
if let ExprKind::Name { id, .. } = &expr.node {
|
||||
let scope_id = self.current_scope().id;
|
||||
let ExprKind::Name { id, .. } = &expr.node else {
|
||||
return;
|
||||
};
|
||||
let scope_id = self.current_scope().id;
|
||||
|
||||
let mut first_iter = true;
|
||||
let mut in_generator = false;
|
||||
let mut import_starred = false;
|
||||
let mut first_iter = true;
|
||||
let mut in_generator = false;
|
||||
let mut import_starred = false;
|
||||
|
||||
for scope_index in self.scope_stack.iter().rev() {
|
||||
let scope = &self.scopes[*scope_index];
|
||||
|
||||
if matches!(scope.kind, ScopeKind::Class(_)) {
|
||||
if id == "__class__" {
|
||||
return;
|
||||
} else if !first_iter && !in_generator {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(index) = scope.bindings.get(&id.as_str()) {
|
||||
// Mark the binding as used.
|
||||
let context = self.execution_context();
|
||||
self.bindings[*index].mark_used(scope_id, Range::from_located(expr), context);
|
||||
|
||||
if matches!(self.bindings[*index].kind, BindingKind::Annotation)
|
||||
&& !self.in_deferred_string_type_definition
|
||||
&& !self.in_deferred_type_definition
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the name of the sub-importation is the same as an alias of another
|
||||
// importation and the alias is used, that sub-importation should be
|
||||
// marked as used too.
|
||||
//
|
||||
// This handles code like:
|
||||
// import pyarrow as pa
|
||||
// import pyarrow.csv
|
||||
// print(pa.csv.read_csv("test.csv"))
|
||||
match &self.bindings[*index].kind {
|
||||
BindingKind::Importation(name, full_name)
|
||||
| BindingKind::SubmoduleImportation(name, full_name) => {
|
||||
let has_alias = full_name
|
||||
.split('.')
|
||||
.last()
|
||||
.map(|segment| &segment != name)
|
||||
.unwrap_or_default();
|
||||
if has_alias {
|
||||
// Mark the sub-importation as used.
|
||||
if let Some(index) = scope.bindings.get(full_name) {
|
||||
self.bindings[*index].mark_used(
|
||||
scope_id,
|
||||
Range::from_located(expr),
|
||||
context,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
BindingKind::FromImportation(name, full_name) => {
|
||||
let has_alias = full_name
|
||||
.split('.')
|
||||
.last()
|
||||
.map(|segment| &segment != name)
|
||||
.unwrap_or_default();
|
||||
if has_alias {
|
||||
// Mark the sub-importation as used.
|
||||
if let Some(index) = scope.bindings.get(full_name.as_str()) {
|
||||
self.bindings[*index].mark_used(
|
||||
scope_id,
|
||||
Range::from_located(expr),
|
||||
context,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
for scope_index in self.scope_stack.iter().rev() {
|
||||
let scope = &self.scopes[*scope_index];
|
||||
|
||||
if matches!(scope.kind, ScopeKind::Class(_)) {
|
||||
if id == "__class__" {
|
||||
return;
|
||||
} else if !first_iter && !in_generator {
|
||||
continue;
|
||||
}
|
||||
|
||||
first_iter = false;
|
||||
in_generator = matches!(scope.kind, ScopeKind::Generator);
|
||||
import_starred = import_starred || scope.import_starred;
|
||||
}
|
||||
|
||||
if import_starred {
|
||||
if self.settings.rules.enabled(&Rule::ImportStarUsage) {
|
||||
let mut from_list = vec![];
|
||||
for scope_index in self.scope_stack.iter().rev() {
|
||||
let scope = &self.scopes[*scope_index];
|
||||
for binding in scope.bindings.values().map(|index| &self.bindings[*index]) {
|
||||
if let BindingKind::StarImportation(level, module) = &binding.kind {
|
||||
from_list.push(helpers::format_import_from(
|
||||
level.as_ref(),
|
||||
module.as_deref(),
|
||||
));
|
||||
if let Some(index) = scope.bindings.get(&id.as_str()) {
|
||||
// Mark the binding as used.
|
||||
let context = self.execution_context();
|
||||
self.bindings[*index].mark_used(scope_id, Range::from_located(expr), context);
|
||||
|
||||
if matches!(self.bindings[*index].kind, BindingKind::Annotation)
|
||||
&& !self.in_deferred_string_type_definition
|
||||
&& !self.in_deferred_type_definition
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the name of the sub-importation is the same as an alias of another
|
||||
// importation and the alias is used, that sub-importation should be
|
||||
// marked as used too.
|
||||
//
|
||||
// This handles code like:
|
||||
// import pyarrow as pa
|
||||
// import pyarrow.csv
|
||||
// print(pa.csv.read_csv("test.csv"))
|
||||
match &self.bindings[*index].kind {
|
||||
BindingKind::Importation(name, full_name)
|
||||
| BindingKind::SubmoduleImportation(name, full_name) => {
|
||||
let has_alias = full_name
|
||||
.split('.')
|
||||
.last()
|
||||
.map(|segment| &segment != name)
|
||||
.unwrap_or_default();
|
||||
if has_alias {
|
||||
// Mark the sub-importation as used.
|
||||
if let Some(index) = scope.bindings.get(full_name) {
|
||||
self.bindings[*index].mark_used(
|
||||
scope_id,
|
||||
Range::from_located(expr),
|
||||
context,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
from_list.sort();
|
||||
|
||||
self.diagnostics.push(Diagnostic::new(
|
||||
pyflakes::rules::ImportStarUsage {
|
||||
name: id.to_string(),
|
||||
sources: from_list,
|
||||
},
|
||||
Range::from_located(expr),
|
||||
));
|
||||
BindingKind::FromImportation(name, full_name) => {
|
||||
let has_alias = full_name
|
||||
.split('.')
|
||||
.last()
|
||||
.map(|segment| &segment != name)
|
||||
.unwrap_or_default();
|
||||
if has_alias {
|
||||
// Mark the sub-importation as used.
|
||||
if let Some(index) = scope.bindings.get(full_name.as_str()) {
|
||||
self.bindings[*index].mark_used(
|
||||
scope_id,
|
||||
Range::from_located(expr),
|
||||
context,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if self.settings.rules.enabled(&Rule::UndefinedName) {
|
||||
// Allow __path__.
|
||||
if self.path.ends_with("__init__.py") && id == "__path__" {
|
||||
return;
|
||||
}
|
||||
first_iter = false;
|
||||
in_generator = matches!(scope.kind, ScopeKind::Generator);
|
||||
import_starred = import_starred || scope.import_starred;
|
||||
}
|
||||
|
||||
// Allow "__module__" and "__qualname__" in class scopes.
|
||||
if (id == "__module__" || id == "__qualname__")
|
||||
&& matches!(self.current_scope().kind, ScopeKind::Class(..))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Avoid flagging if NameError is handled.
|
||||
if let Some(handler_names) = self.except_handlers.last() {
|
||||
if handler_names
|
||||
.iter()
|
||||
.any(|call_path| call_path.as_slice() == ["NameError"])
|
||||
{
|
||||
return;
|
||||
if import_starred {
|
||||
if self.settings.rules.enabled(&Rule::ImportStarUsage) {
|
||||
let mut from_list = vec![];
|
||||
for scope_index in self.scope_stack.iter().rev() {
|
||||
let scope = &self.scopes[*scope_index];
|
||||
for binding in scope.bindings.values().map(|index| &self.bindings[*index]) {
|
||||
if let BindingKind::StarImportation(level, module) = &binding.kind {
|
||||
from_list.push(helpers::format_import_from(
|
||||
level.as_ref(),
|
||||
module.as_deref(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
from_list.sort();
|
||||
|
||||
self.diagnostics.push(Diagnostic::new(
|
||||
pyflakes::rules::UndefinedName { name: id.clone() },
|
||||
pyflakes::rules::ImportStarUsage {
|
||||
name: id.to_string(),
|
||||
sources: from_list,
|
||||
},
|
||||
Range::from_located(expr),
|
||||
));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if self.settings.rules.enabled(&Rule::UndefinedName) {
|
||||
// Allow __path__.
|
||||
if self.path.ends_with("__init__.py") && id == "__path__" {
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow "__module__" and "__qualname__" in class scopes.
|
||||
if (id == "__module__" || id == "__qualname__")
|
||||
&& matches!(self.current_scope().kind, ScopeKind::Class(..))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Avoid flagging if NameError is handled.
|
||||
if let Some(handler_names) = self.except_handlers.last() {
|
||||
if handler_names
|
||||
.iter()
|
||||
.any(|call_path| call_path.as_slice() == ["NameError"])
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self.diagnostics.push(Diagnostic::new(
|
||||
pyflakes::rules::UndefinedName { name: id.clone() },
|
||||
Range::from_located(expr),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4446,26 +4572,29 @@ impl<'a> Checker<'a> {
|
||||
where
|
||||
'b: 'a,
|
||||
{
|
||||
if let ExprKind::Name { id, .. } = &expr.node {
|
||||
if operations::on_conditional_branch(
|
||||
&mut self.parents.iter().rev().map(std::convert::Into::into),
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
let scope =
|
||||
&mut self.scopes[*(self.scope_stack.last().expect("No current scope found"))];
|
||||
if scope.bindings.remove(&id.as_str()).is_none()
|
||||
&& self.settings.rules.enabled(&Rule::UndefinedName)
|
||||
{
|
||||
self.diagnostics.push(Diagnostic::new(
|
||||
pyflakes::rules::UndefinedName {
|
||||
name: id.to_string(),
|
||||
},
|
||||
Range::from_located(expr),
|
||||
));
|
||||
}
|
||||
let ExprKind::Name { id, .. } = &expr.node else {
|
||||
return;
|
||||
};
|
||||
if operations::on_conditional_branch(
|
||||
&mut self.parents.iter().rev().map(std::convert::Into::into),
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
let scope = &mut self.scopes[*(self.scope_stack.last().expect("No current scope found"))];
|
||||
if scope.bindings.remove(&id.as_str()).is_some() {
|
||||
return;
|
||||
}
|
||||
if !self.settings.rules.enabled(&Rule::UndefinedName) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.diagnostics.push(Diagnostic::new(
|
||||
pyflakes::rules::UndefinedName {
|
||||
name: id.to_string(),
|
||||
},
|
||||
Range::from_located(expr),
|
||||
));
|
||||
}
|
||||
|
||||
fn visit_docstring<'b>(&mut self, python_ast: &'b Suite) -> bool
|
||||
@@ -4493,12 +4622,13 @@ impl<'a> Checker<'a> {
|
||||
|
||||
fn check_deferred_type_definitions(&mut self) {
|
||||
self.deferred_type_definitions.reverse();
|
||||
while let Some((expr, in_annotation, (scopes, parents))) =
|
||||
while let Some((expr, (in_annotation, in_type_checking_block), (scopes, parents))) =
|
||||
self.deferred_type_definitions.pop()
|
||||
{
|
||||
self.scope_stack = scopes;
|
||||
self.parents = parents;
|
||||
self.in_annotation = in_annotation;
|
||||
self.in_type_checking_block = in_type_checking_block;
|
||||
self.in_type_definition = true;
|
||||
self.in_deferred_type_definition = true;
|
||||
self.visit_expr(expr);
|
||||
@@ -4513,7 +4643,7 @@ impl<'a> Checker<'a> {
|
||||
{
|
||||
let mut stacks = vec![];
|
||||
self.deferred_string_type_definitions.reverse();
|
||||
while let Some((range, expression, in_annotation, context)) =
|
||||
while let Some((range, expression, (in_annotation, in_type_checking_block), deferral)) =
|
||||
self.deferred_string_type_definitions.pop()
|
||||
{
|
||||
if let Ok(mut expr) = parser::parse_expression(expression, "<filename>") {
|
||||
@@ -4524,7 +4654,7 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
relocate_expr(&mut expr, range);
|
||||
allocator.push(expr);
|
||||
stacks.push((in_annotation, context));
|
||||
stacks.push(((in_annotation, in_type_checking_block), deferral));
|
||||
} else {
|
||||
if self
|
||||
.settings
|
||||
@@ -4540,10 +4670,13 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
for (expr, (in_annotation, (scopes, parents))) in allocator.iter().zip(stacks) {
|
||||
for (expr, ((in_annotation, in_type_checking_block), (scopes, parents))) in
|
||||
allocator.iter().zip(stacks)
|
||||
{
|
||||
self.scope_stack = scopes;
|
||||
self.parents = parents;
|
||||
self.in_annotation = in_annotation;
|
||||
self.in_type_checking_block = in_type_checking_block;
|
||||
self.in_type_definition = true;
|
||||
self.in_deferred_string_type_definition = true;
|
||||
self.visit_expr(expr);
|
||||
@@ -5213,10 +5346,8 @@ impl<'a> Checker<'a> {
|
||||
|
||||
// Extract a `Docstring` from a `Definition`.
|
||||
let expr = definition.docstring.unwrap();
|
||||
let contents = self
|
||||
.locator
|
||||
.slice_source_code_range(&Range::from_located(expr));
|
||||
let indentation = self.locator.slice_source_code_range(&Range::new(
|
||||
let contents = self.locator.slice(&Range::from_located(expr));
|
||||
let indentation = self.locator.slice(&Range::new(
|
||||
Location::new(expr.location.row(), 0),
|
||||
Location::new(expr.location.row(), expr.location.column()),
|
||||
));
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::path::Path;
|
||||
|
||||
use crate::registry::{Diagnostic, Rule};
|
||||
use crate::rules::flake8_no_pep420::rules::implicit_namespace_package;
|
||||
use crate::rules::pep8_naming::rules::invalid_module_name;
|
||||
use crate::settings::Settings;
|
||||
|
||||
pub fn check_file_path(
|
||||
@@ -20,5 +21,12 @@ pub fn check_file_path(
|
||||
}
|
||||
}
|
||||
|
||||
// pep8-naming
|
||||
if settings.rules.enabled(&Rule::InvalidModuleName) {
|
||||
if let Some(diagnostic) = invalid_module_name(path, package) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
diagnostics
|
||||
}
|
||||
|
||||
@@ -52,8 +52,7 @@ pub fn check_logical_lines(
|
||||
|
||||
// Extract the indentation level.
|
||||
let start_loc = line.mapping[0].1;
|
||||
let start_line = locator
|
||||
.slice_source_code_range(&Range::new(Location::new(start_loc.row(), 0), start_loc));
|
||||
let start_line = locator.slice(&Range::new(Location::new(start_loc.row(), 0), start_loc));
|
||||
let indent_level = expand_indent(start_line);
|
||||
let indent_size = 4;
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
//! `NoQA` enforcement and validation.
|
||||
|
||||
use log::warn;
|
||||
use nohash_hasher::IntMap;
|
||||
use rustpython_parser::ast::Location;
|
||||
|
||||
use crate::ast::types::Range;
|
||||
use crate::codes::NoqaCode;
|
||||
use crate::fix::Fix;
|
||||
use crate::noqa;
|
||||
use crate::noqa::{is_file_exempt, Directive};
|
||||
use crate::noqa::{extract_file_exemption, Directive, Exemption};
|
||||
use crate::registry::{Diagnostic, DiagnosticKind, Rule};
|
||||
use crate::rule_redirects::get_redirect_target;
|
||||
use crate::rules::ruff::rules::{UnusedCodes, UnusedNOQA};
|
||||
@@ -20,17 +22,38 @@ pub fn check_noqa(
|
||||
settings: &Settings,
|
||||
autofix: flags::Autofix,
|
||||
) {
|
||||
let mut noqa_directives: IntMap<usize, (Directive, Vec<&str>)> = IntMap::default();
|
||||
let mut ignored = vec![];
|
||||
|
||||
let enforce_noqa = settings.rules.enabled(&Rule::UnusedNOQA);
|
||||
|
||||
// Whether the file is exempted from all checks.
|
||||
let mut file_exempted = false;
|
||||
|
||||
// Codes that are globally exempted (within the current file).
|
||||
let mut file_exemptions: Vec<NoqaCode> = vec![];
|
||||
|
||||
// Map from line number to `noqa` directive on that line, along with any codes
|
||||
// that were matched by the directive.
|
||||
let mut noqa_directives: IntMap<usize, (Directive, Vec<NoqaCode>)> = IntMap::default();
|
||||
|
||||
// Indices of diagnostics that were ignored by a `noqa` directive.
|
||||
let mut ignored_diagnostics = vec![];
|
||||
|
||||
let lines: Vec<&str> = contents.lines().collect();
|
||||
for lineno in commented_lines {
|
||||
// If we hit an exemption for the entire file, bail.
|
||||
if is_file_exempt(lines[lineno - 1]) {
|
||||
diagnostics.drain(..);
|
||||
return;
|
||||
match extract_file_exemption(lines[lineno - 1]) {
|
||||
Exemption::All => {
|
||||
file_exempted = true;
|
||||
}
|
||||
Exemption::Codes(codes) => {
|
||||
file_exemptions.extend(codes.into_iter().filter_map(|code| {
|
||||
if let Ok(rule) = Rule::from_code(get_redirect_target(code).unwrap_or(code)) {
|
||||
Some(rule.noqa_code())
|
||||
} else {
|
||||
warn!("Invalid code provided to `# ruff: noqa`: {}", code);
|
||||
None
|
||||
}
|
||||
}));
|
||||
}
|
||||
Exemption::None => {}
|
||||
}
|
||||
|
||||
if enforce_noqa {
|
||||
@@ -46,6 +69,20 @@ pub fn check_noqa(
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the file is exempted, ignore all diagnostics.
|
||||
if file_exempted {
|
||||
ignored_diagnostics.push(index);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the diagnostic is ignored by a global exemption, ignore it.
|
||||
if !file_exemptions.is_empty() {
|
||||
if file_exemptions.contains(&diagnostic.kind.rule().noqa_code()) {
|
||||
ignored_diagnostics.push(index);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Is the violation ignored by a `noqa` directive on the parent line?
|
||||
if let Some(parent_lineno) = diagnostic.parent.map(|location| location.row()) {
|
||||
let noqa_lineno = noqa_line_for.get(&parent_lineno).unwrap_or(&parent_lineno);
|
||||
@@ -55,14 +92,14 @@ pub fn check_noqa(
|
||||
});
|
||||
match noqa {
|
||||
(Directive::All(..), matches) => {
|
||||
matches.push(diagnostic.kind.rule().code());
|
||||
ignored.push(index);
|
||||
matches.push(diagnostic.kind.rule().noqa_code());
|
||||
ignored_diagnostics.push(index);
|
||||
continue;
|
||||
}
|
||||
(Directive::Codes(.., codes), matches) => {
|
||||
if noqa::includes(diagnostic.kind.rule(), codes) {
|
||||
matches.push(diagnostic.kind.rule().code());
|
||||
ignored.push(index);
|
||||
matches.push(diagnostic.kind.rule().noqa_code());
|
||||
ignored_diagnostics.push(index);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -82,13 +119,15 @@ pub fn check_noqa(
|
||||
.or_insert_with(|| (noqa::extract_noqa_directive(lines[noqa_lineno - 1]), vec![]));
|
||||
match noqa {
|
||||
(Directive::All(..), matches) => {
|
||||
matches.push(diagnostic.kind.rule().code());
|
||||
ignored.push(index);
|
||||
matches.push(diagnostic.kind.rule().noqa_code());
|
||||
ignored_diagnostics.push(index);
|
||||
continue;
|
||||
}
|
||||
(Directive::Codes(.., codes), matches) => {
|
||||
if noqa::includes(diagnostic.kind.rule(), codes) {
|
||||
matches.push(diagnostic.kind.rule().code());
|
||||
ignored.push(index);
|
||||
matches.push(diagnostic.kind.rule().noqa_code());
|
||||
ignored_diagnostics.push(index);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
(Directive::None, ..) => {}
|
||||
@@ -128,12 +167,12 @@ pub fn check_noqa(
|
||||
let mut self_ignore = false;
|
||||
for code in codes {
|
||||
let code = get_redirect_target(code).unwrap_or(code);
|
||||
if code == Rule::UnusedNOQA.code() {
|
||||
if Rule::UnusedNOQA.noqa_code() == code {
|
||||
self_ignore = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if matches.contains(&code) || settings.external.contains(code) {
|
||||
if matches.iter().any(|m| *m == code) || settings.external.contains(code) {
|
||||
valid_codes.push(code);
|
||||
} else {
|
||||
if let Ok(rule) = Rule::from_code(code) {
|
||||
@@ -202,8 +241,8 @@ pub fn check_noqa(
|
||||
}
|
||||
}
|
||||
|
||||
ignored.sort_unstable();
|
||||
for index in ignored.iter().rev() {
|
||||
ignored_diagnostics.sort_unstable();
|
||||
for index in ignored_diagnostics.iter().rev() {
|
||||
diagnostics.swap_remove(*index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ pub fn check_tokens(
|
||||
// E701, E702, E703
|
||||
if enforce_compound_statements {
|
||||
diagnostics.extend(
|
||||
pycodestyle::rules::compound_statements(tokens)
|
||||
pycodestyle::rules::compound_statements(tokens, settings, autofix)
|
||||
.into_iter()
|
||||
.filter(|diagnostic| settings.rules.enabled(diagnostic.kind.rule())),
|
||||
);
|
||||
|
||||
616
crates/ruff/src/codes.rs
Normal file
616
crates/ruff/src/codes.rs
Normal file
@@ -0,0 +1,616 @@
|
||||
use crate::registry::{Linter, Rule};
|
||||
|
||||
#[ruff_macros::map_codes]
|
||||
pub fn code_to_rule(linter: Linter, code: &str) -> Option<Rule> {
|
||||
#[allow(clippy::enum_glob_use)]
|
||||
use Linter::*;
|
||||
|
||||
Some(match (linter, code) {
|
||||
// pycodestyle errors
|
||||
(Pycodestyle, "E101") => Rule::MixedSpacesAndTabs,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E111") => Rule::IndentationWithInvalidMultiple,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E112") => Rule::NoIndentedBlock,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E113") => Rule::UnexpectedIndentation,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E114") => Rule::IndentationWithInvalidMultipleComment,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E115") => Rule::NoIndentedBlockComment,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E116") => Rule::UnexpectedIndentationComment,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E117") => Rule::OverIndented,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E201") => Rule::WhitespaceAfterOpenBracket,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E202") => Rule::WhitespaceBeforeCloseBracket,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E203") => Rule::WhitespaceBeforePunctuation,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E221") => Rule::MultipleSpacesBeforeOperator,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E222") => Rule::MultipleSpacesAfterOperator,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E223") => Rule::TabBeforeOperator,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E224") => Rule::TabAfterOperator,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E261") => Rule::TooFewSpacesBeforeInlineComment,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E262") => Rule::NoSpaceAfterInlineComment,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E265") => Rule::NoSpaceAfterBlockComment,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E266") => Rule::MultipleLeadingHashesForBlockComment,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E271") => Rule::MultipleSpacesAfterKeyword,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E272") => Rule::MultipleSpacesBeforeKeyword,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E273") => Rule::TabAfterKeyword,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
(Pycodestyle, "E274") => Rule::TabBeforeKeyword,
|
||||
(Pycodestyle, "E401") => Rule::MultipleImportsOnOneLine,
|
||||
(Pycodestyle, "E402") => Rule::ModuleImportNotAtTopOfFile,
|
||||
(Pycodestyle, "E501") => Rule::LineTooLong,
|
||||
(Pycodestyle, "E701") => Rule::MultipleStatementsOnOneLineColon,
|
||||
(Pycodestyle, "E702") => Rule::MultipleStatementsOnOneLineSemicolon,
|
||||
(Pycodestyle, "E703") => Rule::UselessSemicolon,
|
||||
(Pycodestyle, "E711") => Rule::NoneComparison,
|
||||
(Pycodestyle, "E712") => Rule::TrueFalseComparison,
|
||||
(Pycodestyle, "E713") => Rule::NotInTest,
|
||||
(Pycodestyle, "E714") => Rule::NotIsTest,
|
||||
(Pycodestyle, "E721") => Rule::TypeComparison,
|
||||
(Pycodestyle, "E722") => Rule::BareExcept,
|
||||
(Pycodestyle, "E731") => Rule::LambdaAssignment,
|
||||
(Pycodestyle, "E741") => Rule::AmbiguousVariableName,
|
||||
(Pycodestyle, "E742") => Rule::AmbiguousClassName,
|
||||
(Pycodestyle, "E743") => Rule::AmbiguousFunctionName,
|
||||
(Pycodestyle, "E902") => Rule::IOError,
|
||||
(Pycodestyle, "E999") => Rule::SyntaxError,
|
||||
|
||||
// pycodestyle warnings
|
||||
(Pycodestyle, "W292") => Rule::NoNewLineAtEndOfFile,
|
||||
(Pycodestyle, "W505") => Rule::DocLineTooLong,
|
||||
(Pycodestyle, "W605") => Rule::InvalidEscapeSequence,
|
||||
|
||||
// pyflakes
|
||||
(Pyflakes, "401") => Rule::UnusedImport,
|
||||
(Pyflakes, "402") => Rule::ImportShadowedByLoopVar,
|
||||
(Pyflakes, "403") => Rule::ImportStar,
|
||||
(Pyflakes, "404") => Rule::LateFutureImport,
|
||||
(Pyflakes, "405") => Rule::ImportStarUsage,
|
||||
(Pyflakes, "406") => Rule::ImportStarNotPermitted,
|
||||
(Pyflakes, "407") => Rule::FutureFeatureNotDefined,
|
||||
(Pyflakes, "501") => Rule::PercentFormatInvalidFormat,
|
||||
(Pyflakes, "502") => Rule::PercentFormatExpectedMapping,
|
||||
(Pyflakes, "503") => Rule::PercentFormatExpectedSequence,
|
||||
(Pyflakes, "504") => Rule::PercentFormatExtraNamedArguments,
|
||||
(Pyflakes, "505") => Rule::PercentFormatMissingArgument,
|
||||
(Pyflakes, "506") => Rule::PercentFormatMixedPositionalAndNamed,
|
||||
(Pyflakes, "507") => Rule::PercentFormatPositionalCountMismatch,
|
||||
(Pyflakes, "508") => Rule::PercentFormatStarRequiresSequence,
|
||||
(Pyflakes, "509") => Rule::PercentFormatUnsupportedFormatCharacter,
|
||||
(Pyflakes, "521") => Rule::StringDotFormatInvalidFormat,
|
||||
(Pyflakes, "522") => Rule::StringDotFormatExtraNamedArguments,
|
||||
(Pyflakes, "523") => Rule::StringDotFormatExtraPositionalArguments,
|
||||
(Pyflakes, "524") => Rule::StringDotFormatMissingArguments,
|
||||
(Pyflakes, "525") => Rule::StringDotFormatMixingAutomatic,
|
||||
(Pyflakes, "541") => Rule::FStringMissingPlaceholders,
|
||||
(Pyflakes, "601") => Rule::MultiValueRepeatedKeyLiteral,
|
||||
(Pyflakes, "602") => Rule::MultiValueRepeatedKeyVariable,
|
||||
(Pyflakes, "621") => Rule::ExpressionsInStarAssignment,
|
||||
(Pyflakes, "622") => Rule::TwoStarredExpressions,
|
||||
(Pyflakes, "631") => Rule::AssertTuple,
|
||||
(Pyflakes, "632") => Rule::IsLiteral,
|
||||
(Pyflakes, "633") => Rule::InvalidPrintSyntax,
|
||||
(Pyflakes, "634") => Rule::IfTuple,
|
||||
(Pyflakes, "701") => Rule::BreakOutsideLoop,
|
||||
(Pyflakes, "702") => Rule::ContinueOutsideLoop,
|
||||
(Pyflakes, "704") => Rule::YieldOutsideFunction,
|
||||
(Pyflakes, "706") => Rule::ReturnOutsideFunction,
|
||||
(Pyflakes, "707") => Rule::DefaultExceptNotLast,
|
||||
(Pyflakes, "722") => Rule::ForwardAnnotationSyntaxError,
|
||||
(Pyflakes, "811") => Rule::RedefinedWhileUnused,
|
||||
(Pyflakes, "821") => Rule::UndefinedName,
|
||||
(Pyflakes, "822") => Rule::UndefinedExport,
|
||||
(Pyflakes, "823") => Rule::UndefinedLocal,
|
||||
(Pyflakes, "841") => Rule::UnusedVariable,
|
||||
(Pyflakes, "842") => Rule::UnusedAnnotation,
|
||||
(Pyflakes, "901") => Rule::RaiseNotImplemented,
|
||||
|
||||
// pylint
|
||||
(Pylint, "E0100") => Rule::YieldInInit,
|
||||
(Pylint, "E0101") => Rule::ReturnInInit,
|
||||
(Pylint, "E0604") => Rule::InvalidAllObject,
|
||||
(Pylint, "E0605") => Rule::InvalidAllFormat,
|
||||
(Pylint, "E1307") => Rule::BadStringFormatType,
|
||||
(Pylint, "E2502") => Rule::BidirectionalUnicode,
|
||||
(Pylint, "E1310") => Rule::BadStrStripCall,
|
||||
(Pylint, "C0414") => Rule::UselessImportAlias,
|
||||
(Pylint, "C3002") => Rule::UnnecessaryDirectLambdaCall,
|
||||
(Pylint, "E0117") => Rule::NonlocalWithoutBinding,
|
||||
(Pylint, "E0118") => Rule::UsedPriorGlobalDeclaration,
|
||||
(Pylint, "E1142") => Rule::AwaitOutsideAsync,
|
||||
(Pylint, "R0206") => Rule::PropertyWithParameters,
|
||||
(Pylint, "R0402") => Rule::ConsiderUsingFromImport,
|
||||
(Pylint, "R0133") => Rule::ComparisonOfConstant,
|
||||
(Pylint, "R1701") => Rule::ConsiderMergingIsinstance,
|
||||
(Pylint, "R1722") => Rule::ConsiderUsingSysExit,
|
||||
(Pylint, "R2004") => Rule::MagicValueComparison,
|
||||
(Pylint, "W0120") => Rule::UselessElseOnLoop,
|
||||
(Pylint, "W0602") => Rule::GlobalVariableNotAssigned,
|
||||
(Pylint, "R0911") => Rule::TooManyReturnStatements,
|
||||
(Pylint, "R0913") => Rule::TooManyArguments,
|
||||
(Pylint, "R0912") => Rule::TooManyBranches,
|
||||
(Pylint, "R0915") => Rule::TooManyStatements,
|
||||
|
||||
// flake8-builtins
|
||||
(Flake8Builtins, "001") => Rule::BuiltinVariableShadowing,
|
||||
(Flake8Builtins, "002") => Rule::BuiltinArgumentShadowing,
|
||||
(Flake8Builtins, "003") => Rule::BuiltinAttributeShadowing,
|
||||
|
||||
// flake8-bugbear
|
||||
(Flake8Bugbear, "002") => Rule::UnaryPrefixIncrement,
|
||||
(Flake8Bugbear, "003") => Rule::AssignmentToOsEnviron,
|
||||
(Flake8Bugbear, "004") => Rule::UnreliableCallableCheck,
|
||||
(Flake8Bugbear, "005") => Rule::StripWithMultiCharacters,
|
||||
(Flake8Bugbear, "006") => Rule::MutableArgumentDefault,
|
||||
(Flake8Bugbear, "007") => Rule::UnusedLoopControlVariable,
|
||||
(Flake8Bugbear, "008") => Rule::FunctionCallArgumentDefault,
|
||||
(Flake8Bugbear, "009") => Rule::GetAttrWithConstant,
|
||||
(Flake8Bugbear, "010") => Rule::SetAttrWithConstant,
|
||||
(Flake8Bugbear, "011") => Rule::AssertFalse,
|
||||
(Flake8Bugbear, "012") => Rule::JumpStatementInFinally,
|
||||
(Flake8Bugbear, "013") => Rule::RedundantTupleInExceptionHandler,
|
||||
(Flake8Bugbear, "014") => Rule::DuplicateHandlerException,
|
||||
(Flake8Bugbear, "015") => Rule::UselessComparison,
|
||||
(Flake8Bugbear, "016") => Rule::CannotRaiseLiteral,
|
||||
(Flake8Bugbear, "017") => Rule::AssertRaisesException,
|
||||
(Flake8Bugbear, "018") => Rule::UselessExpression,
|
||||
(Flake8Bugbear, "019") => Rule::CachedInstanceMethod,
|
||||
(Flake8Bugbear, "020") => Rule::LoopVariableOverridesIterator,
|
||||
(Flake8Bugbear, "021") => Rule::FStringDocstring,
|
||||
(Flake8Bugbear, "022") => Rule::UselessContextlibSuppress,
|
||||
(Flake8Bugbear, "023") => Rule::FunctionUsesLoopVariable,
|
||||
(Flake8Bugbear, "024") => Rule::AbstractBaseClassWithoutAbstractMethod,
|
||||
(Flake8Bugbear, "025") => Rule::DuplicateTryBlockException,
|
||||
(Flake8Bugbear, "026") => Rule::StarArgUnpackingAfterKeywordArg,
|
||||
(Flake8Bugbear, "027") => Rule::EmptyMethodWithoutAbstractDecorator,
|
||||
(Flake8Bugbear, "029") => Rule::ExceptWithEmptyTuple,
|
||||
(Flake8Bugbear, "032") => Rule::UnintentionalTypeAnnotation,
|
||||
(Flake8Bugbear, "904") => Rule::RaiseWithoutFromInsideExcept,
|
||||
(Flake8Bugbear, "905") => Rule::ZipWithoutExplicitStrict,
|
||||
|
||||
// flake8-blind-except
|
||||
(Flake8BlindExcept, "001") => Rule::BlindExcept,
|
||||
|
||||
// flake8-comprehensions
|
||||
(Flake8Comprehensions, "00") => Rule::UnnecessaryGeneratorList,
|
||||
(Flake8Comprehensions, "01") => Rule::UnnecessaryGeneratorSet,
|
||||
(Flake8Comprehensions, "02") => Rule::UnnecessaryGeneratorDict,
|
||||
(Flake8Comprehensions, "03") => Rule::UnnecessaryListComprehensionSet,
|
||||
(Flake8Comprehensions, "04") => Rule::UnnecessaryListComprehensionDict,
|
||||
(Flake8Comprehensions, "05") => Rule::UnnecessaryLiteralSet,
|
||||
(Flake8Comprehensions, "06") => Rule::UnnecessaryLiteralDict,
|
||||
(Flake8Comprehensions, "08") => Rule::UnnecessaryCollectionCall,
|
||||
(Flake8Comprehensions, "09") => Rule::UnnecessaryLiteralWithinTupleCall,
|
||||
(Flake8Comprehensions, "10") => Rule::UnnecessaryLiteralWithinListCall,
|
||||
(Flake8Comprehensions, "11") => Rule::UnnecessaryListCall,
|
||||
(Flake8Comprehensions, "13") => Rule::UnnecessaryCallAroundSorted,
|
||||
(Flake8Comprehensions, "14") => Rule::UnnecessaryDoubleCastOrProcess,
|
||||
(Flake8Comprehensions, "15") => Rule::UnnecessarySubscriptReversal,
|
||||
(Flake8Comprehensions, "16") => Rule::UnnecessaryComprehension,
|
||||
(Flake8Comprehensions, "17") => Rule::UnnecessaryMap,
|
||||
|
||||
// flake8-debugger
|
||||
(Flake8Debugger, "0") => Rule::Debugger,
|
||||
|
||||
// mccabe
|
||||
(McCabe, "1") => Rule::ComplexStructure,
|
||||
|
||||
// flake8-tidy-imports
|
||||
(Flake8TidyImports, "251") => Rule::BannedApi,
|
||||
(Flake8TidyImports, "252") => Rule::RelativeImports,
|
||||
|
||||
// flake8-return
|
||||
(Flake8Return, "501") => Rule::UnnecessaryReturnNone,
|
||||
(Flake8Return, "502") => Rule::ImplicitReturnValue,
|
||||
(Flake8Return, "503") => Rule::ImplicitReturn,
|
||||
(Flake8Return, "504") => Rule::UnnecessaryAssign,
|
||||
(Flake8Return, "505") => Rule::SuperfluousElseReturn,
|
||||
(Flake8Return, "506") => Rule::SuperfluousElseRaise,
|
||||
(Flake8Return, "507") => Rule::SuperfluousElseContinue,
|
||||
(Flake8Return, "508") => Rule::SuperfluousElseBreak,
|
||||
|
||||
// flake8-implicit-str-concat
|
||||
(Flake8ImplicitStrConcat, "001") => Rule::SingleLineImplicitStringConcatenation,
|
||||
(Flake8ImplicitStrConcat, "002") => Rule::MultiLineImplicitStringConcatenation,
|
||||
(Flake8ImplicitStrConcat, "003") => Rule::ExplicitStringConcatenation,
|
||||
|
||||
// flake8-print
|
||||
(Flake8Print, "1") => Rule::PrintFound,
|
||||
(Flake8Print, "3") => Rule::PPrintFound,
|
||||
|
||||
// flake8-quotes
|
||||
(Flake8Quotes, "000") => Rule::BadQuotesInlineString,
|
||||
(Flake8Quotes, "001") => Rule::BadQuotesMultilineString,
|
||||
(Flake8Quotes, "002") => Rule::BadQuotesDocstring,
|
||||
(Flake8Quotes, "003") => Rule::AvoidableEscapedQuote,
|
||||
|
||||
// flake8-annotations
|
||||
(Flake8Annotations, "001") => Rule::MissingTypeFunctionArgument,
|
||||
(Flake8Annotations, "002") => Rule::MissingTypeArgs,
|
||||
(Flake8Annotations, "003") => Rule::MissingTypeKwargs,
|
||||
(Flake8Annotations, "101") => Rule::MissingTypeSelf,
|
||||
(Flake8Annotations, "102") => Rule::MissingTypeCls,
|
||||
(Flake8Annotations, "201") => Rule::MissingReturnTypePublicFunction,
|
||||
(Flake8Annotations, "202") => Rule::MissingReturnTypePrivateFunction,
|
||||
(Flake8Annotations, "204") => Rule::MissingReturnTypeSpecialMethod,
|
||||
(Flake8Annotations, "205") => Rule::MissingReturnTypeStaticMethod,
|
||||
(Flake8Annotations, "206") => Rule::MissingReturnTypeClassMethod,
|
||||
(Flake8Annotations, "401") => Rule::AnyType,
|
||||
|
||||
// flake8-2020
|
||||
(Flake82020, "101") => Rule::SysVersionSlice3Referenced,
|
||||
(Flake82020, "102") => Rule::SysVersion2Referenced,
|
||||
(Flake82020, "103") => Rule::SysVersionCmpStr3,
|
||||
(Flake82020, "201") => Rule::SysVersionInfo0Eq3Referenced,
|
||||
(Flake82020, "202") => Rule::SixPY3Referenced,
|
||||
(Flake82020, "203") => Rule::SysVersionInfo1CmpInt,
|
||||
(Flake82020, "204") => Rule::SysVersionInfoMinorCmpInt,
|
||||
(Flake82020, "301") => Rule::SysVersion0Referenced,
|
||||
(Flake82020, "302") => Rule::SysVersionCmpStr10,
|
||||
(Flake82020, "303") => Rule::SysVersionSlice1Referenced,
|
||||
|
||||
// flake8-simplify
|
||||
(Flake8Simplify, "101") => Rule::DuplicateIsinstanceCall,
|
||||
(Flake8Simplify, "102") => Rule::CollapsibleIf,
|
||||
(Flake8Simplify, "103") => Rule::NeedlessBool,
|
||||
(Flake8Simplify, "105") => Rule::UseContextlibSuppress,
|
||||
(Flake8Simplify, "107") => Rule::ReturnInTryExceptFinally,
|
||||
(Flake8Simplify, "108") => Rule::UseTernaryOperator,
|
||||
(Flake8Simplify, "109") => Rule::CompareWithTuple,
|
||||
(Flake8Simplify, "110") => Rule::ReimplementedBuiltin,
|
||||
// (Flake8Simplify, "111") => Rule::ReimplementedBuiltin,
|
||||
(Flake8Simplify, "112") => Rule::UseCapitalEnvironmentVariables,
|
||||
(Flake8Simplify, "114") => Rule::IfWithSameArms,
|
||||
(Flake8Simplify, "115") => Rule::OpenFileWithContextHandler,
|
||||
(Flake8Simplify, "116") => Rule::ManualDictLookup,
|
||||
(Flake8Simplify, "117") => Rule::MultipleWithStatements,
|
||||
(Flake8Simplify, "118") => Rule::KeyInDict,
|
||||
(Flake8Simplify, "201") => Rule::NegateEqualOp,
|
||||
(Flake8Simplify, "202") => Rule::NegateNotEqualOp,
|
||||
(Flake8Simplify, "208") => Rule::DoubleNegation,
|
||||
(Flake8Simplify, "210") => Rule::IfExprWithTrueFalse,
|
||||
(Flake8Simplify, "211") => Rule::IfExprWithFalseTrue,
|
||||
(Flake8Simplify, "212") => Rule::IfExprWithTwistedArms,
|
||||
(Flake8Simplify, "220") => Rule::AAndNotA,
|
||||
(Flake8Simplify, "221") => Rule::AOrNotA,
|
||||
(Flake8Simplify, "222") => Rule::OrTrue,
|
||||
(Flake8Simplify, "223") => Rule::AndFalse,
|
||||
(Flake8Simplify, "300") => Rule::YodaConditions,
|
||||
(Flake8Simplify, "401") => Rule::DictGetWithDefault,
|
||||
|
||||
// pyupgrade
|
||||
(Pyupgrade, "001") => Rule::UselessMetaclassType,
|
||||
(Pyupgrade, "003") => Rule::TypeOfPrimitive,
|
||||
(Pyupgrade, "004") => Rule::UselessObjectInheritance,
|
||||
(Pyupgrade, "005") => Rule::DeprecatedUnittestAlias,
|
||||
(Pyupgrade, "006") => Rule::DeprecatedCollectionType,
|
||||
(Pyupgrade, "007") => Rule::TypingUnion,
|
||||
(Pyupgrade, "008") => Rule::SuperCallWithParameters,
|
||||
(Pyupgrade, "009") => Rule::UTF8EncodingDeclaration,
|
||||
(Pyupgrade, "010") => Rule::UnnecessaryFutureImport,
|
||||
(Pyupgrade, "011") => Rule::LRUCacheWithoutParameters,
|
||||
(Pyupgrade, "012") => Rule::UnnecessaryEncodeUTF8,
|
||||
(Pyupgrade, "013") => Rule::ConvertTypedDictFunctionalToClass,
|
||||
(Pyupgrade, "014") => Rule::ConvertNamedTupleFunctionalToClass,
|
||||
(Pyupgrade, "015") => Rule::RedundantOpenModes,
|
||||
(Pyupgrade, "017") => Rule::DatetimeTimezoneUTC,
|
||||
(Pyupgrade, "018") => Rule::NativeLiterals,
|
||||
(Pyupgrade, "019") => Rule::TypingTextStrAlias,
|
||||
(Pyupgrade, "020") => Rule::OpenAlias,
|
||||
(Pyupgrade, "021") => Rule::ReplaceUniversalNewlines,
|
||||
(Pyupgrade, "022") => Rule::ReplaceStdoutStderr,
|
||||
(Pyupgrade, "023") => Rule::RewriteCElementTree,
|
||||
(Pyupgrade, "024") => Rule::OSErrorAlias,
|
||||
(Pyupgrade, "025") => Rule::RewriteUnicodeLiteral,
|
||||
(Pyupgrade, "026") => Rule::RewriteMockImport,
|
||||
(Pyupgrade, "027") => Rule::RewriteListComprehension,
|
||||
(Pyupgrade, "028") => Rule::RewriteYieldFrom,
|
||||
(Pyupgrade, "029") => Rule::UnnecessaryBuiltinImport,
|
||||
(Pyupgrade, "030") => Rule::FormatLiterals,
|
||||
(Pyupgrade, "031") => Rule::PrintfStringFormatting,
|
||||
(Pyupgrade, "032") => Rule::FString,
|
||||
(Pyupgrade, "033") => Rule::FunctoolsCache,
|
||||
(Pyupgrade, "034") => Rule::ExtraneousParentheses,
|
||||
(Pyupgrade, "035") => Rule::ImportReplacements,
|
||||
(Pyupgrade, "036") => Rule::OutdatedVersionBlock,
|
||||
(Pyupgrade, "037") => Rule::QuotedAnnotation,
|
||||
|
||||
// pydocstyle
|
||||
(Pydocstyle, "100") => Rule::PublicModule,
|
||||
(Pydocstyle, "101") => Rule::PublicClass,
|
||||
(Pydocstyle, "102") => Rule::PublicMethod,
|
||||
(Pydocstyle, "103") => Rule::PublicFunction,
|
||||
(Pydocstyle, "104") => Rule::PublicPackage,
|
||||
(Pydocstyle, "105") => Rule::MagicMethod,
|
||||
(Pydocstyle, "106") => Rule::PublicNestedClass,
|
||||
(Pydocstyle, "107") => Rule::PublicInit,
|
||||
(Pydocstyle, "200") => Rule::FitsOnOneLine,
|
||||
(Pydocstyle, "201") => Rule::NoBlankLineBeforeFunction,
|
||||
(Pydocstyle, "202") => Rule::NoBlankLineAfterFunction,
|
||||
(Pydocstyle, "203") => Rule::OneBlankLineBeforeClass,
|
||||
(Pydocstyle, "204") => Rule::OneBlankLineAfterClass,
|
||||
(Pydocstyle, "205") => Rule::BlankLineAfterSummary,
|
||||
(Pydocstyle, "206") => Rule::IndentWithSpaces,
|
||||
(Pydocstyle, "207") => Rule::NoUnderIndentation,
|
||||
(Pydocstyle, "208") => Rule::NoOverIndentation,
|
||||
(Pydocstyle, "209") => Rule::NewLineAfterLastParagraph,
|
||||
(Pydocstyle, "210") => Rule::NoSurroundingWhitespace,
|
||||
(Pydocstyle, "211") => Rule::NoBlankLineBeforeClass,
|
||||
(Pydocstyle, "212") => Rule::MultiLineSummaryFirstLine,
|
||||
(Pydocstyle, "213") => Rule::MultiLineSummarySecondLine,
|
||||
(Pydocstyle, "214") => Rule::SectionNotOverIndented,
|
||||
(Pydocstyle, "215") => Rule::SectionUnderlineNotOverIndented,
|
||||
(Pydocstyle, "300") => Rule::TripleSingleQuotes,
|
||||
(Pydocstyle, "301") => Rule::EscapeSequenceInDocstring,
|
||||
(Pydocstyle, "400") => Rule::EndsInPeriod,
|
||||
(Pydocstyle, "401") => Rule::NonImperativeMood,
|
||||
(Pydocstyle, "402") => Rule::NoSignature,
|
||||
(Pydocstyle, "403") => Rule::FirstLineCapitalized,
|
||||
(Pydocstyle, "404") => Rule::DocstringStartsWithThis,
|
||||
(Pydocstyle, "405") => Rule::CapitalizeSectionName,
|
||||
(Pydocstyle, "406") => Rule::NewLineAfterSectionName,
|
||||
(Pydocstyle, "407") => Rule::DashedUnderlineAfterSection,
|
||||
(Pydocstyle, "408") => Rule::SectionUnderlineAfterName,
|
||||
(Pydocstyle, "409") => Rule::SectionUnderlineMatchesSectionLength,
|
||||
(Pydocstyle, "410") => Rule::BlankLineAfterSection,
|
||||
(Pydocstyle, "411") => Rule::BlankLineBeforeSection,
|
||||
(Pydocstyle, "412") => Rule::NoBlankLinesBetweenHeaderAndContent,
|
||||
(Pydocstyle, "413") => Rule::BlankLineAfterLastSection,
|
||||
(Pydocstyle, "414") => Rule::EmptyDocstringSection,
|
||||
(Pydocstyle, "415") => Rule::EndsInPunctuation,
|
||||
(Pydocstyle, "416") => Rule::SectionNameEndsInColon,
|
||||
(Pydocstyle, "417") => Rule::UndocumentedParam,
|
||||
(Pydocstyle, "418") => Rule::OverloadWithDocstring,
|
||||
(Pydocstyle, "419") => Rule::EmptyDocstring,
|
||||
|
||||
// pep8-naming
|
||||
(PEP8Naming, "801") => Rule::InvalidClassName,
|
||||
(PEP8Naming, "802") => Rule::InvalidFunctionName,
|
||||
(PEP8Naming, "803") => Rule::InvalidArgumentName,
|
||||
(PEP8Naming, "804") => Rule::InvalidFirstArgumentNameForClassMethod,
|
||||
(PEP8Naming, "805") => Rule::InvalidFirstArgumentNameForMethod,
|
||||
(PEP8Naming, "806") => Rule::NonLowercaseVariableInFunction,
|
||||
(PEP8Naming, "807") => Rule::DunderFunctionName,
|
||||
(PEP8Naming, "811") => Rule::ConstantImportedAsNonConstant,
|
||||
(PEP8Naming, "812") => Rule::LowercaseImportedAsNonLowercase,
|
||||
(PEP8Naming, "813") => Rule::CamelcaseImportedAsLowercase,
|
||||
(PEP8Naming, "814") => Rule::CamelcaseImportedAsConstant,
|
||||
(PEP8Naming, "815") => Rule::MixedCaseVariableInClassScope,
|
||||
(PEP8Naming, "816") => Rule::MixedCaseVariableInGlobalScope,
|
||||
(PEP8Naming, "817") => Rule::CamelcaseImportedAsAcronym,
|
||||
(PEP8Naming, "818") => Rule::ErrorSuffixOnExceptionName,
|
||||
(PEP8Naming, "999") => Rule::InvalidModuleName,
|
||||
|
||||
// isort
|
||||
(Isort, "001") => Rule::UnsortedImports,
|
||||
(Isort, "002") => Rule::MissingRequiredImport,
|
||||
|
||||
// eradicate
|
||||
(Eradicate, "001") => Rule::CommentedOutCode,
|
||||
|
||||
// flake8-bandit
|
||||
(Flake8Bandit, "101") => Rule::Assert,
|
||||
(Flake8Bandit, "102") => Rule::ExecBuiltin,
|
||||
(Flake8Bandit, "103") => Rule::BadFilePermissions,
|
||||
(Flake8Bandit, "104") => Rule::HardcodedBindAllInterfaces,
|
||||
(Flake8Bandit, "105") => Rule::HardcodedPasswordString,
|
||||
(Flake8Bandit, "106") => Rule::HardcodedPasswordFuncArg,
|
||||
(Flake8Bandit, "107") => Rule::HardcodedPasswordDefault,
|
||||
(Flake8Bandit, "608") => Rule::HardcodedSQLExpression,
|
||||
(Flake8Bandit, "108") => Rule::HardcodedTempFile,
|
||||
(Flake8Bandit, "110") => Rule::TryExceptPass,
|
||||
(Flake8Bandit, "112") => Rule::TryExceptContinue,
|
||||
(Flake8Bandit, "113") => Rule::RequestWithoutTimeout,
|
||||
(Flake8Bandit, "324") => Rule::HashlibInsecureHashFunction,
|
||||
(Flake8Bandit, "501") => Rule::RequestWithNoCertValidation,
|
||||
(Flake8Bandit, "506") => Rule::UnsafeYAMLLoad,
|
||||
(Flake8Bandit, "508") => Rule::SnmpInsecureVersion,
|
||||
(Flake8Bandit, "509") => Rule::SnmpWeakCryptography,
|
||||
(Flake8Bandit, "612") => Rule::LoggingConfigInsecureListen,
|
||||
(Flake8Bandit, "701") => Rule::Jinja2AutoescapeFalse,
|
||||
|
||||
// flake8-boolean-trap
|
||||
(Flake8BooleanTrap, "001") => Rule::BooleanPositionalArgInFunctionDefinition,
|
||||
(Flake8BooleanTrap, "002") => Rule::BooleanDefaultValueInFunctionDefinition,
|
||||
(Flake8BooleanTrap, "003") => Rule::BooleanPositionalValueInFunctionCall,
|
||||
|
||||
// flake8-unused-arguments
|
||||
(Flake8UnusedArguments, "001") => Rule::UnusedFunctionArgument,
|
||||
(Flake8UnusedArguments, "002") => Rule::UnusedMethodArgument,
|
||||
(Flake8UnusedArguments, "003") => Rule::UnusedClassMethodArgument,
|
||||
(Flake8UnusedArguments, "004") => Rule::UnusedStaticMethodArgument,
|
||||
(Flake8UnusedArguments, "005") => Rule::UnusedLambdaArgument,
|
||||
|
||||
// flake8-import-conventions
|
||||
(Flake8ImportConventions, "001") => Rule::UnconventionalImportAlias,
|
||||
|
||||
// flake8-datetimez
|
||||
(Flake8Datetimez, "001") => Rule::CallDatetimeWithoutTzinfo,
|
||||
(Flake8Datetimez, "002") => Rule::CallDatetimeToday,
|
||||
(Flake8Datetimez, "003") => Rule::CallDatetimeUtcnow,
|
||||
(Flake8Datetimez, "004") => Rule::CallDatetimeUtcfromtimestamp,
|
||||
(Flake8Datetimez, "005") => Rule::CallDatetimeNowWithoutTzinfo,
|
||||
(Flake8Datetimez, "006") => Rule::CallDatetimeFromtimestamp,
|
||||
(Flake8Datetimez, "007") => Rule::CallDatetimeStrptimeWithoutZone,
|
||||
(Flake8Datetimez, "011") => Rule::CallDateToday,
|
||||
(Flake8Datetimez, "012") => Rule::CallDateFromtimestamp,
|
||||
|
||||
// pygrep-hooks
|
||||
(PygrepHooks, "001") => Rule::NoEval,
|
||||
(PygrepHooks, "002") => Rule::DeprecatedLogWarn,
|
||||
(PygrepHooks, "003") => Rule::BlanketTypeIgnore,
|
||||
(PygrepHooks, "004") => Rule::BlanketNOQA,
|
||||
|
||||
// pandas-vet
|
||||
(PandasVet, "002") => Rule::UseOfInplaceArgument,
|
||||
(PandasVet, "003") => Rule::UseOfDotIsNull,
|
||||
(PandasVet, "004") => Rule::UseOfDotNotNull,
|
||||
(PandasVet, "007") => Rule::UseOfDotIx,
|
||||
(PandasVet, "008") => Rule::UseOfDotAt,
|
||||
(PandasVet, "009") => Rule::UseOfDotIat,
|
||||
(PandasVet, "010") => Rule::UseOfDotPivotOrUnstack,
|
||||
(PandasVet, "011") => Rule::UseOfDotValues,
|
||||
(PandasVet, "012") => Rule::UseOfDotReadTable,
|
||||
(PandasVet, "013") => Rule::UseOfDotStack,
|
||||
(PandasVet, "015") => Rule::UseOfPdMerge,
|
||||
(PandasVet, "901") => Rule::DfIsABadVariableName,
|
||||
|
||||
// flake8-errmsg
|
||||
(Flake8ErrMsg, "101") => Rule::RawStringInException,
|
||||
(Flake8ErrMsg, "102") => Rule::FStringInException,
|
||||
(Flake8ErrMsg, "103") => Rule::DotFormatInException,
|
||||
|
||||
// flake8-pyi
|
||||
(Flake8Pyi, "001") => Rule::PrefixTypeParams,
|
||||
(Flake8Pyi, "007") => Rule::UnrecognizedPlatformCheck,
|
||||
(Flake8Pyi, "008") => Rule::UnrecognizedPlatformName,
|
||||
|
||||
// flake8-pytest-style
|
||||
(Flake8PytestStyle, "001") => Rule::IncorrectFixtureParenthesesStyle,
|
||||
(Flake8PytestStyle, "002") => Rule::FixturePositionalArgs,
|
||||
(Flake8PytestStyle, "003") => Rule::ExtraneousScopeFunction,
|
||||
(Flake8PytestStyle, "004") => Rule::MissingFixtureNameUnderscore,
|
||||
(Flake8PytestStyle, "005") => Rule::IncorrectFixtureNameUnderscore,
|
||||
(Flake8PytestStyle, "006") => Rule::ParametrizeNamesWrongType,
|
||||
(Flake8PytestStyle, "007") => Rule::ParametrizeValuesWrongType,
|
||||
(Flake8PytestStyle, "008") => Rule::PatchWithLambda,
|
||||
(Flake8PytestStyle, "009") => Rule::UnittestAssertion,
|
||||
(Flake8PytestStyle, "010") => Rule::RaisesWithoutException,
|
||||
(Flake8PytestStyle, "011") => Rule::RaisesTooBroad,
|
||||
(Flake8PytestStyle, "012") => Rule::RaisesWithMultipleStatements,
|
||||
(Flake8PytestStyle, "013") => Rule::IncorrectPytestImport,
|
||||
(Flake8PytestStyle, "015") => Rule::AssertAlwaysFalse,
|
||||
(Flake8PytestStyle, "016") => Rule::FailWithoutMessage,
|
||||
(Flake8PytestStyle, "017") => Rule::AssertInExcept,
|
||||
(Flake8PytestStyle, "018") => Rule::CompositeAssertion,
|
||||
(Flake8PytestStyle, "019") => Rule::FixtureParamWithoutValue,
|
||||
(Flake8PytestStyle, "020") => Rule::DeprecatedYieldFixture,
|
||||
(Flake8PytestStyle, "021") => Rule::FixtureFinalizerCallback,
|
||||
(Flake8PytestStyle, "022") => Rule::UselessYieldFixture,
|
||||
(Flake8PytestStyle, "023") => Rule::IncorrectMarkParenthesesStyle,
|
||||
(Flake8PytestStyle, "024") => Rule::UnnecessaryAsyncioMarkOnFixture,
|
||||
(Flake8PytestStyle, "025") => Rule::ErroneousUseFixturesOnFixture,
|
||||
(Flake8PytestStyle, "026") => Rule::UseFixturesWithoutParameters,
|
||||
|
||||
// flake8-pie
|
||||
(Flake8Pie, "790") => Rule::UnnecessaryPass,
|
||||
(Flake8Pie, "794") => Rule::DupeClassFieldDefinitions,
|
||||
(Flake8Pie, "796") => Rule::PreferUniqueEnums,
|
||||
(Flake8Pie, "800") => Rule::UnnecessarySpread,
|
||||
(Flake8Pie, "804") => Rule::UnnecessaryDictKwargs,
|
||||
(Flake8Pie, "807") => Rule::PreferListBuiltin,
|
||||
(Flake8Pie, "810") => Rule::SingleStartsEndsWith,
|
||||
|
||||
// flake8-commas
|
||||
(Flake8Commas, "812") => Rule::TrailingCommaMissing,
|
||||
(Flake8Commas, "818") => Rule::TrailingCommaOnBareTupleProhibited,
|
||||
(Flake8Commas, "819") => Rule::TrailingCommaProhibited,
|
||||
|
||||
// flake8-no-pep420
|
||||
(Flake8NoPep420, "001") => Rule::ImplicitNamespacePackage,
|
||||
|
||||
// flake8-executable
|
||||
(Flake8Executable, "001") => Rule::ShebangNotExecutable,
|
||||
(Flake8Executable, "002") => Rule::ShebangMissingExecutableFile,
|
||||
(Flake8Executable, "003") => Rule::ShebangPython,
|
||||
(Flake8Executable, "004") => Rule::ShebangWhitespace,
|
||||
(Flake8Executable, "005") => Rule::ShebangNewline,
|
||||
|
||||
// flake8-type-checking
|
||||
(Flake8TypeChecking, "001") => Rule::TypingOnlyFirstPartyImport,
|
||||
(Flake8TypeChecking, "002") => Rule::TypingOnlyThirdPartyImport,
|
||||
(Flake8TypeChecking, "003") => Rule::TypingOnlyStandardLibraryImport,
|
||||
(Flake8TypeChecking, "004") => Rule::RuntimeImportInTypeCheckingBlock,
|
||||
(Flake8TypeChecking, "005") => Rule::EmptyTypeCheckingBlock,
|
||||
|
||||
// tryceratops
|
||||
(Tryceratops, "002") => Rule::RaiseVanillaClass,
|
||||
(Tryceratops, "003") => Rule::RaiseVanillaArgs,
|
||||
(Tryceratops, "004") => Rule::PreferTypeError,
|
||||
(Tryceratops, "200") => Rule::ReraiseNoCause,
|
||||
(Tryceratops, "201") => Rule::VerboseRaise,
|
||||
(Tryceratops, "300") => Rule::TryConsiderElse,
|
||||
(Tryceratops, "301") => Rule::RaiseWithinTry,
|
||||
(Tryceratops, "400") => Rule::ErrorInsteadOfException,
|
||||
(Tryceratops, "401") => Rule::VerboseLogMessage,
|
||||
|
||||
// flake8-use-pathlib
|
||||
(Flake8UsePathlib, "100") => Rule::PathlibAbspath,
|
||||
(Flake8UsePathlib, "101") => Rule::PathlibChmod,
|
||||
(Flake8UsePathlib, "102") => Rule::PathlibMkdir,
|
||||
(Flake8UsePathlib, "103") => Rule::PathlibMakedirs,
|
||||
(Flake8UsePathlib, "104") => Rule::PathlibRename,
|
||||
(Flake8UsePathlib, "105") => Rule::PathlibReplace,
|
||||
(Flake8UsePathlib, "106") => Rule::PathlibRmdir,
|
||||
(Flake8UsePathlib, "107") => Rule::PathlibRemove,
|
||||
(Flake8UsePathlib, "108") => Rule::PathlibUnlink,
|
||||
(Flake8UsePathlib, "109") => Rule::PathlibGetcwd,
|
||||
(Flake8UsePathlib, "110") => Rule::PathlibExists,
|
||||
(Flake8UsePathlib, "111") => Rule::PathlibExpanduser,
|
||||
(Flake8UsePathlib, "112") => Rule::PathlibIsDir,
|
||||
(Flake8UsePathlib, "113") => Rule::PathlibIsFile,
|
||||
(Flake8UsePathlib, "114") => Rule::PathlibIsLink,
|
||||
(Flake8UsePathlib, "115") => Rule::PathlibReadlink,
|
||||
(Flake8UsePathlib, "116") => Rule::PathlibStat,
|
||||
(Flake8UsePathlib, "117") => Rule::PathlibIsAbs,
|
||||
(Flake8UsePathlib, "118") => Rule::PathlibJoin,
|
||||
(Flake8UsePathlib, "119") => Rule::PathlibBasename,
|
||||
(Flake8UsePathlib, "120") => Rule::PathlibDirname,
|
||||
(Flake8UsePathlib, "121") => Rule::PathlibSamefile,
|
||||
(Flake8UsePathlib, "122") => Rule::PathlibSplitext,
|
||||
(Flake8UsePathlib, "123") => Rule::PathlibOpen,
|
||||
(Flake8UsePathlib, "124") => Rule::PathlibPyPath,
|
||||
|
||||
// flake8-logging-format
|
||||
(Flake8LoggingFormat, "001") => Rule::LoggingStringFormat,
|
||||
(Flake8LoggingFormat, "002") => Rule::LoggingPercentFormat,
|
||||
(Flake8LoggingFormat, "003") => Rule::LoggingStringConcat,
|
||||
(Flake8LoggingFormat, "004") => Rule::LoggingFString,
|
||||
(Flake8LoggingFormat, "010") => Rule::LoggingWarn,
|
||||
(Flake8LoggingFormat, "101") => Rule::LoggingExtraAttrClash,
|
||||
(Flake8LoggingFormat, "201") => Rule::LoggingExcInfo,
|
||||
(Flake8LoggingFormat, "202") => Rule::LoggingRedundantExcInfo,
|
||||
|
||||
// flake8-raise
|
||||
(Flake8Raise, "102") => Rule::UnnecessaryParenOnRaiseException,
|
||||
|
||||
// flake8-self
|
||||
(Flake8Self, "001") => Rule::PrivateMemberAccess,
|
||||
|
||||
// numpy
|
||||
(Numpy, "001") => Rule::NumpyDeprecatedTypeAlias,
|
||||
(Numpy, "002") => Rule::NumpyLegacyRandom,
|
||||
|
||||
// ruff
|
||||
(Ruff, "001") => Rule::AmbiguousUnicodeCharacterString,
|
||||
(Ruff, "002") => Rule::AmbiguousUnicodeCharacterDocstring,
|
||||
(Ruff, "003") => Rule::AmbiguousUnicodeCharacterComment,
|
||||
(Ruff, "004") => Rule::KeywordArgumentBeforeStarArgument,
|
||||
(Ruff, "005") => Rule::UnpackInsteadOfConcatenatingToCollectionLiteral,
|
||||
(Ruff, "006") => Rule::AsyncioDanglingTask,
|
||||
(Ruff, "100") => Rule::UnusedNOQA,
|
||||
|
||||
// flake8-django
|
||||
(Flake8Django, "001") => Rule::NullableModelStringField,
|
||||
(Flake8Django, "008") => Rule::ModelWithoutDunderStr,
|
||||
(Flake8Django, "013") => Rule::NonLeadingReceiverDecorator,
|
||||
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
@@ -6,8 +6,8 @@ use itertools::Itertools;
|
||||
use super::external_config::ExternalConfig;
|
||||
use super::plugin::Plugin;
|
||||
use super::{parser, plugin};
|
||||
use crate::registry::RuleCodePrefix;
|
||||
use crate::rule_selector::{prefix_to_selector, RuleSelector};
|
||||
use crate::registry::Linter;
|
||||
use crate::rule_selector::RuleSelector;
|
||||
use crate::rules::flake8_pytest_style::types::{
|
||||
ParametrizeNameType, ParametrizeValuesRowType, ParametrizeValuesType,
|
||||
};
|
||||
@@ -23,9 +23,8 @@ use crate::settings::pyproject::Pyproject;
|
||||
use crate::warn_user;
|
||||
|
||||
const DEFAULT_SELECTORS: &[RuleSelector] = &[
|
||||
prefix_to_selector(RuleCodePrefix::F),
|
||||
prefix_to_selector(RuleCodePrefix::E),
|
||||
prefix_to_selector(RuleCodePrefix::W),
|
||||
RuleSelector::Linter(Linter::Pyflakes),
|
||||
RuleSelector::Linter(Linter::Pycodestyle),
|
||||
];
|
||||
|
||||
pub fn convert(
|
||||
@@ -359,13 +358,13 @@ pub fn convert(
|
||||
options.select = Some(
|
||||
select
|
||||
.into_iter()
|
||||
.sorted_by_key(RuleSelector::short_code)
|
||||
.sorted_by_key(RuleSelector::prefix_and_code)
|
||||
.collect(),
|
||||
);
|
||||
options.ignore = Some(
|
||||
ignore
|
||||
.into_iter()
|
||||
.sorted_by_key(RuleSelector::short_code)
|
||||
.sorted_by_key(RuleSelector::prefix_and_code)
|
||||
.collect(),
|
||||
);
|
||||
if flake8_annotations != flake8_annotations::settings::Options::default() {
|
||||
@@ -433,7 +432,7 @@ pub fn convert(
|
||||
/// plugins.
|
||||
fn resolve_select(plugins: &[Plugin]) -> HashSet<RuleSelector> {
|
||||
let mut select: HashSet<_> = DEFAULT_SELECTORS.iter().cloned().collect();
|
||||
select.extend(plugins.iter().map(Plugin::selector));
|
||||
select.extend(plugins.iter().map(|p| Linter::from(p).into()));
|
||||
select
|
||||
}
|
||||
|
||||
@@ -448,7 +447,7 @@ mod tests {
|
||||
use super::convert;
|
||||
use crate::flake8_to_ruff::converter::DEFAULT_SELECTORS;
|
||||
use crate::flake8_to_ruff::ExternalConfig;
|
||||
use crate::registry::RuleCodePrefix;
|
||||
use crate::registry::Linter;
|
||||
use crate::rule_selector::RuleSelector;
|
||||
use crate::rules::pydocstyle::settings::Convention;
|
||||
use crate::rules::{flake8_quotes, pydocstyle};
|
||||
@@ -463,7 +462,7 @@ mod tests {
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(plugins)
|
||||
.sorted_by_key(RuleSelector::short_code)
|
||||
.sorted_by_key(RuleSelector::prefix_and_code)
|
||||
.collect(),
|
||||
),
|
||||
..Options::default()
|
||||
@@ -578,7 +577,7 @@ mod tests {
|
||||
pydocstyle: Some(pydocstyle::settings::Options {
|
||||
convention: Some(Convention::Numpy),
|
||||
}),
|
||||
..default_options([RuleCodePrefix::D.into()])
|
||||
..default_options([Linter::Pydocstyle.into()])
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
@@ -602,7 +601,7 @@ mod tests {
|
||||
docstring_quotes: None,
|
||||
avoid_escape: None,
|
||||
}),
|
||||
..default_options([RuleCodePrefix::Q.into()])
|
||||
..default_options([Linter::Flake8Quotes.into()])
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
|
||||
@@ -196,7 +196,8 @@ mod tests {
|
||||
use anyhow::Result;
|
||||
|
||||
use super::{parse_files_to_codes_mapping, parse_prefix_codes, parse_strings};
|
||||
use crate::registry::RuleCodePrefix;
|
||||
use crate::codes;
|
||||
use crate::registry::Linter;
|
||||
use crate::rule_selector::RuleSelector;
|
||||
use crate::settings::types::PatternPrefixPair;
|
||||
|
||||
@@ -211,19 +212,25 @@ mod tests {
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let actual = parse_prefix_codes("F401");
|
||||
let expected = vec![RuleCodePrefix::F401.into()];
|
||||
let expected = vec![codes::Pyflakes::_401.into()];
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let actual = parse_prefix_codes("F401,");
|
||||
let expected = vec![RuleCodePrefix::F401.into()];
|
||||
let expected = vec![codes::Pyflakes::_401.into()];
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let actual = parse_prefix_codes("F401,E501");
|
||||
let expected = vec![RuleCodePrefix::F401.into(), RuleCodePrefix::E501.into()];
|
||||
let expected = vec![
|
||||
codes::Pyflakes::_401.into(),
|
||||
codes::Pycodestyle::E501.into(),
|
||||
];
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
let actual = parse_prefix_codes("F401, E501");
|
||||
let expected = vec![RuleCodePrefix::F401.into(), RuleCodePrefix::E501.into()];
|
||||
let expected = vec![
|
||||
codes::Pyflakes::_401.into(),
|
||||
codes::Pycodestyle::E501.into(),
|
||||
];
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
@@ -276,11 +283,11 @@ mod tests {
|
||||
let expected: Vec<PatternPrefixPair> = vec![
|
||||
PatternPrefixPair {
|
||||
pattern: "locust/test/*".to_string(),
|
||||
prefix: RuleCodePrefix::F841.into(),
|
||||
prefix: codes::Pyflakes::_841.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "examples/*".to_string(),
|
||||
prefix: RuleCodePrefix::F841.into(),
|
||||
prefix: codes::Pyflakes::_841.into(),
|
||||
},
|
||||
];
|
||||
assert_eq!(actual, expected);
|
||||
@@ -296,23 +303,23 @@ mod tests {
|
||||
let expected: Vec<PatternPrefixPair> = vec![
|
||||
PatternPrefixPair {
|
||||
pattern: "t/*".to_string(),
|
||||
prefix: RuleCodePrefix::D.into(),
|
||||
prefix: Linter::Pydocstyle.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "setup.py".to_string(),
|
||||
prefix: RuleCodePrefix::D.into(),
|
||||
prefix: Linter::Pydocstyle.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "examples/*".to_string(),
|
||||
prefix: RuleCodePrefix::D.into(),
|
||||
prefix: Linter::Pydocstyle.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "docs/*".to_string(),
|
||||
prefix: RuleCodePrefix::D.into(),
|
||||
prefix: Linter::Pydocstyle.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "extra/*".to_string(),
|
||||
prefix: RuleCodePrefix::D.into(),
|
||||
prefix: Linter::Pydocstyle.into(),
|
||||
},
|
||||
];
|
||||
assert_eq!(actual, expected);
|
||||
@@ -334,47 +341,47 @@ mod tests {
|
||||
let expected: Vec<PatternPrefixPair> = vec![
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/__init__.py".to_string(),
|
||||
prefix: RuleCodePrefix::E402.into(),
|
||||
prefix: codes::Pycodestyle::E402.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/core/downloader/handlers/http.py".to_string(),
|
||||
prefix: RuleCodePrefix::F401.into(),
|
||||
prefix: codes::Pyflakes::_401.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/http/__init__.py".to_string(),
|
||||
prefix: RuleCodePrefix::F401.into(),
|
||||
prefix: codes::Pyflakes::_401.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/linkextractors/__init__.py".to_string(),
|
||||
prefix: RuleCodePrefix::E402.into(),
|
||||
prefix: codes::Pycodestyle::E402.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/linkextractors/__init__.py".to_string(),
|
||||
prefix: RuleCodePrefix::F401.into(),
|
||||
prefix: codes::Pyflakes::_401.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/selector/__init__.py".to_string(),
|
||||
prefix: RuleCodePrefix::F401.into(),
|
||||
prefix: codes::Pyflakes::_401.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/spiders/__init__.py".to_string(),
|
||||
prefix: RuleCodePrefix::E402.into(),
|
||||
prefix: codes::Pycodestyle::E402.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/spiders/__init__.py".to_string(),
|
||||
prefix: RuleCodePrefix::F401.into(),
|
||||
prefix: codes::Pyflakes::_401.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/utils/url.py".to_string(),
|
||||
prefix: RuleCodePrefix::F403.into(),
|
||||
prefix: codes::Pyflakes::_403.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "scrapy/utils/url.py".to_string(),
|
||||
prefix: RuleCodePrefix::F405.into(),
|
||||
prefix: codes::Pyflakes::_405.into(),
|
||||
},
|
||||
PatternPrefixPair {
|
||||
pattern: "tests/test_loader.py".to_string(),
|
||||
prefix: RuleCodePrefix::E741.into(),
|
||||
prefix: codes::Pycodestyle::E741.into(),
|
||||
},
|
||||
];
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::str::FromStr;
|
||||
|
||||
use anyhow::anyhow;
|
||||
|
||||
use crate::registry::RuleCodePrefix;
|
||||
use crate::registry::Linter;
|
||||
use crate::rule_selector::RuleSelector;
|
||||
|
||||
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||
@@ -131,43 +131,42 @@ impl fmt::Debug for Plugin {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(martin): Convert into `impl From<Plugin> for Linter`
|
||||
impl Plugin {
|
||||
pub fn selector(&self) -> RuleSelector {
|
||||
match self {
|
||||
Plugin::Flake82020 => RuleCodePrefix::YTT.into(),
|
||||
Plugin::Flake8Annotations => RuleCodePrefix::ANN.into(),
|
||||
Plugin::Flake8Bandit => RuleCodePrefix::S.into(),
|
||||
Plugin::Flake8BlindExcept => RuleCodePrefix::BLE.into(),
|
||||
Plugin::Flake8BooleanTrap => RuleCodePrefix::FBT.into(),
|
||||
Plugin::Flake8Bugbear => RuleCodePrefix::B.into(),
|
||||
Plugin::Flake8Builtins => RuleCodePrefix::A.into(),
|
||||
Plugin::Flake8Commas => RuleCodePrefix::COM.into(),
|
||||
Plugin::Flake8Comprehensions => RuleCodePrefix::C4.into(),
|
||||
Plugin::Flake8Datetimez => RuleCodePrefix::DTZ.into(),
|
||||
Plugin::Flake8Debugger => RuleCodePrefix::T1.into(),
|
||||
Plugin::Flake8Docstrings => RuleCodePrefix::D.into(),
|
||||
Plugin::Flake8Eradicate => RuleCodePrefix::ERA.into(),
|
||||
Plugin::Flake8ErrMsg => RuleCodePrefix::EM.into(),
|
||||
Plugin::Flake8Executable => RuleCodePrefix::EXE.into(),
|
||||
Plugin::Flake8ImplicitStrConcat => RuleCodePrefix::ISC.into(),
|
||||
Plugin::Flake8ImportConventions => RuleCodePrefix::ICN.into(),
|
||||
Plugin::Flake8NoPep420 => RuleCodePrefix::INP.into(),
|
||||
Plugin::Flake8Pie => RuleCodePrefix::PIE.into(),
|
||||
Plugin::Flake8Print => RuleCodePrefix::T2.into(),
|
||||
Plugin::Flake8PytestStyle => RuleCodePrefix::PT.into(),
|
||||
Plugin::Flake8Quotes => RuleCodePrefix::Q.into(),
|
||||
Plugin::Flake8Return => RuleCodePrefix::RET.into(),
|
||||
Plugin::Flake8Simplify => RuleCodePrefix::SIM.into(),
|
||||
Plugin::Flake8TidyImports => RuleCodePrefix::TID.into(),
|
||||
Plugin::Flake8TypeChecking => RuleCodePrefix::TCH.into(),
|
||||
Plugin::Flake8UnusedArguments => RuleCodePrefix::ARG.into(),
|
||||
Plugin::Flake8UsePathlib => RuleCodePrefix::PTH.into(),
|
||||
Plugin::McCabe => RuleCodePrefix::C9.into(),
|
||||
Plugin::PEP8Naming => RuleCodePrefix::N.into(),
|
||||
Plugin::PandasVet => RuleCodePrefix::PD.into(),
|
||||
Plugin::Pyupgrade => RuleCodePrefix::UP.into(),
|
||||
Plugin::Tryceratops => RuleCodePrefix::TRY.into(),
|
||||
impl From<&Plugin> for Linter {
|
||||
fn from(plugin: &Plugin) -> Self {
|
||||
match plugin {
|
||||
Plugin::Flake82020 => Linter::Flake82020,
|
||||
Plugin::Flake8Annotations => Linter::Flake8Annotations,
|
||||
Plugin::Flake8Bandit => Linter::Flake8Bandit,
|
||||
Plugin::Flake8BlindExcept => Linter::Flake8BlindExcept,
|
||||
Plugin::Flake8BooleanTrap => Linter::Flake8BooleanTrap,
|
||||
Plugin::Flake8Bugbear => Linter::Flake8Bugbear,
|
||||
Plugin::Flake8Builtins => Linter::Flake8Builtins,
|
||||
Plugin::Flake8Commas => Linter::Flake8Commas,
|
||||
Plugin::Flake8Comprehensions => Linter::Flake8Comprehensions,
|
||||
Plugin::Flake8Datetimez => Linter::Flake8Datetimez,
|
||||
Plugin::Flake8Debugger => Linter::Flake8Debugger,
|
||||
Plugin::Flake8Docstrings => Linter::Pydocstyle,
|
||||
Plugin::Flake8Eradicate => Linter::Eradicate,
|
||||
Plugin::Flake8ErrMsg => Linter::Flake8ErrMsg,
|
||||
Plugin::Flake8Executable => Linter::Flake8Executable,
|
||||
Plugin::Flake8ImplicitStrConcat => Linter::Flake8ImplicitStrConcat,
|
||||
Plugin::Flake8ImportConventions => Linter::Flake8ImportConventions,
|
||||
Plugin::Flake8NoPep420 => Linter::Flake8NoPep420,
|
||||
Plugin::Flake8Pie => Linter::Flake8Pie,
|
||||
Plugin::Flake8Print => Linter::Flake8Print,
|
||||
Plugin::Flake8PytestStyle => Linter::Flake8PytestStyle,
|
||||
Plugin::Flake8Quotes => Linter::Flake8Quotes,
|
||||
Plugin::Flake8Return => Linter::Flake8Return,
|
||||
Plugin::Flake8Simplify => Linter::Flake8Simplify,
|
||||
Plugin::Flake8TidyImports => Linter::Flake8TidyImports,
|
||||
Plugin::Flake8TypeChecking => Linter::Flake8TypeChecking,
|
||||
Plugin::Flake8UnusedArguments => Linter::Flake8UnusedArguments,
|
||||
Plugin::Flake8UsePathlib => Linter::Flake8UsePathlib,
|
||||
Plugin::McCabe => Linter::McCabe,
|
||||
Plugin::PEP8Naming => Linter::PEP8Naming,
|
||||
Plugin::PandasVet => Linter::PandasVet,
|
||||
Plugin::Pyupgrade => Linter::Pyupgrade,
|
||||
Plugin::Tryceratops => Linter::Tryceratops,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -334,7 +333,7 @@ pub fn infer_plugins_from_codes(selectors: &HashSet<RuleSelector>) -> Vec<Plugin
|
||||
for selector in selectors {
|
||||
if selector
|
||||
.into_iter()
|
||||
.any(|rule| plugin.selector().into_iter().any(|r| r == rule))
|
||||
.any(|rule| Linter::from(plugin).into_iter().any(|r| r == rule))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user