Compare commits
129 Commits
ibraheem/s
...
alex/known
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ea95e9f24 | ||
|
|
040bb0f932 | ||
|
|
0150ac964d | ||
|
|
1dcdf7f41d | ||
|
|
d00697621e | ||
|
|
76619b96e5 | ||
|
|
6e25cfba2b | ||
|
|
76387295a5 | ||
|
|
d04e63a6d9 | ||
|
|
86fd9b634e | ||
|
|
c0beb3412f | ||
|
|
b01003f81d | ||
|
|
2362263d5e | ||
|
|
170ccd80b4 | ||
|
|
4a5715b97a | ||
|
|
c77e72ea1a | ||
|
|
5d546c600a | ||
|
|
8b22992988 | ||
|
|
f6def1c86d | ||
|
|
5aab49880a | ||
|
|
7783cea14f | ||
|
|
c1fed55d51 | ||
|
|
689797a984 | ||
|
|
66dbea90f1 | ||
|
|
d2684a00c6 | ||
|
|
2a0c5669f2 | ||
|
|
cb152b4725 | ||
|
|
90f47e9b7b | ||
|
|
62975b3ab2 | ||
|
|
eee5a5a3d6 | ||
|
|
66f50fb04b | ||
|
|
919af9628d | ||
|
|
9d8cba4e8b | ||
|
|
d89f75f9cc | ||
|
|
e44c489273 | ||
|
|
3220242dec | ||
|
|
6abafcb565 | ||
|
|
cef1a522dc | ||
|
|
40731f0589 | ||
|
|
1461137407 | ||
|
|
47653ca88a | ||
|
|
02ae8e1210 | ||
|
|
0edbd6c390 | ||
|
|
833be2e66a | ||
|
|
0194452928 | ||
|
|
2c4c015f74 | ||
|
|
66fc7c8fc0 | ||
|
|
237a5821ba | ||
|
|
27eee5a1a8 | ||
|
|
fd2cc37f90 | ||
|
|
ca7933804e | ||
|
|
e474f36473 | ||
|
|
da16e00751 | ||
|
|
885dc9091f | ||
|
|
d01e0faee3 | ||
|
|
ef8281b695 | ||
|
|
a77db3da3f | ||
|
|
9e9c4fe17b | ||
|
|
f4c6ff3f68 | ||
|
|
ca8ed35275 | ||
|
|
315fb0f3da | ||
|
|
bbc26b2f11 | ||
|
|
ec07a0f885 | ||
|
|
861dff1dd8 | ||
|
|
8be205df99 | ||
|
|
34fd44eb55 | ||
|
|
b64307f65e | ||
|
|
291413b126 | ||
|
|
7ec7853cec | ||
|
|
907c291877 | ||
|
|
21303d1a02 | ||
|
|
528ae8083b | ||
|
|
0ce022e64e | ||
|
|
659ecba477 | ||
|
|
96660d93ca | ||
|
|
c111517f1b | ||
|
|
9f29551fb2 | ||
|
|
cc659c6988 | ||
|
|
c8e03a0449 | ||
|
|
a5494839b1 | ||
|
|
835b37818c | ||
|
|
f88fbc3952 | ||
|
|
9570d39f9b | ||
|
|
06a78d0bd0 | ||
|
|
cfec89e8c3 | ||
|
|
9089493263 | ||
|
|
089f5152f6 | ||
|
|
f24e650dfd | ||
|
|
f32ae94bc3 | ||
|
|
da6cbeee60 | ||
|
|
cccbd0286e | ||
|
|
a6ad8fb342 | ||
|
|
ea812d0813 | ||
|
|
d9266284df | ||
|
|
2d224e6096 | ||
|
|
ef785d2e74 | ||
|
|
e36611c4d8 | ||
|
|
49763a7f7c | ||
|
|
2d25aaeaa2 | ||
|
|
2910988b06 | ||
|
|
8cff77c82e | ||
|
|
ffb09c84f2 | ||
|
|
dc160c4a49 | ||
|
|
073a71ca9a | ||
|
|
23a3b6ef23 | ||
|
|
5b3a501fae | ||
|
|
e66f182045 | ||
|
|
f544026b81 | ||
|
|
234d248730 | ||
|
|
0ef324b2dd | ||
|
|
7982edac90 | ||
|
|
e180975226 | ||
|
|
97819f8a37 | ||
|
|
22177e6915 | ||
|
|
20d73dd41c | ||
|
|
50bf3fa45a | ||
|
|
440635cbe6 | ||
|
|
ce0a32aadb | ||
|
|
10a1d9f01e | ||
|
|
4e83db4d40 | ||
|
|
136443b71b | ||
|
|
f7a741a99e | ||
|
|
4c8d612120 | ||
|
|
65b288b45b | ||
|
|
06da2c808f | ||
|
|
55a2ff91c7 | ||
|
|
d6705f4700 | ||
|
|
f67ff33177 | ||
|
|
dcf0a8d4d7 |
16
.github/workflows/build-binaries.yml
vendored
16
.github/workflows/build-binaries.yml
vendored
@@ -49,7 +49,7 @@ jobs:
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build sdist"
|
||||
uses: PyO3/maturin-action@aef21716ff3dcae8a1c301d23ec3e4446972a6e3 # v1.49.1
|
||||
uses: PyO3/maturin-action@35be3186fc8e037e329f06b68dcd807d83dcc6dc # v1.49.2
|
||||
with:
|
||||
command: sdist
|
||||
args: --out dist
|
||||
@@ -79,7 +79,7 @@ jobs:
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels - x86_64"
|
||||
uses: PyO3/maturin-action@aef21716ff3dcae8a1c301d23ec3e4446972a6e3 # v1.49.1
|
||||
uses: PyO3/maturin-action@35be3186fc8e037e329f06b68dcd807d83dcc6dc # v1.49.2
|
||||
with:
|
||||
target: x86_64
|
||||
args: --release --locked --out dist
|
||||
@@ -121,7 +121,7 @@ jobs:
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels - aarch64"
|
||||
uses: PyO3/maturin-action@aef21716ff3dcae8a1c301d23ec3e4446972a6e3 # v1.49.1
|
||||
uses: PyO3/maturin-action@35be3186fc8e037e329f06b68dcd807d83dcc6dc # v1.49.2
|
||||
with:
|
||||
target: aarch64
|
||||
args: --release --locked --out dist
|
||||
@@ -177,7 +177,7 @@ jobs:
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels"
|
||||
uses: PyO3/maturin-action@aef21716ff3dcae8a1c301d23ec3e4446972a6e3 # v1.49.1
|
||||
uses: PyO3/maturin-action@35be3186fc8e037e329f06b68dcd807d83dcc6dc # v1.49.2
|
||||
with:
|
||||
target: ${{ matrix.platform.target }}
|
||||
args: --release --locked --out dist
|
||||
@@ -230,7 +230,7 @@ jobs:
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels"
|
||||
uses: PyO3/maturin-action@aef21716ff3dcae8a1c301d23ec3e4446972a6e3 # v1.49.1
|
||||
uses: PyO3/maturin-action@35be3186fc8e037e329f06b68dcd807d83dcc6dc # v1.49.2
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: auto
|
||||
@@ -304,7 +304,7 @@ jobs:
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels"
|
||||
uses: PyO3/maturin-action@aef21716ff3dcae8a1c301d23ec3e4446972a6e3 # v1.49.1
|
||||
uses: PyO3/maturin-action@35be3186fc8e037e329f06b68dcd807d83dcc6dc # v1.49.2
|
||||
with:
|
||||
target: ${{ matrix.platform.target }}
|
||||
manylinux: auto
|
||||
@@ -370,7 +370,7 @@ jobs:
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels"
|
||||
uses: PyO3/maturin-action@aef21716ff3dcae8a1c301d23ec3e4446972a6e3 # v1.49.1
|
||||
uses: PyO3/maturin-action@35be3186fc8e037e329f06b68dcd807d83dcc6dc # v1.49.2
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: musllinux_1_2
|
||||
@@ -435,7 +435,7 @@ jobs:
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels"
|
||||
uses: PyO3/maturin-action@aef21716ff3dcae8a1c301d23ec3e4446972a6e3 # v1.49.1
|
||||
uses: PyO3/maturin-action@35be3186fc8e037e329f06b68dcd807d83dcc6dc # v1.49.2
|
||||
with:
|
||||
target: ${{ matrix.platform.target }}
|
||||
manylinux: musllinux_1_2
|
||||
|
||||
8
.github/workflows/build-docker.yml
vendored
8
.github/workflows/build-docker.yml
vendored
@@ -38,7 +38,7 @@ jobs:
|
||||
submodules: recursive
|
||||
persist-credentials: false
|
||||
|
||||
- uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
||||
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
|
||||
- uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
with:
|
||||
@@ -119,7 +119,7 @@ jobs:
|
||||
pattern: digests-*
|
||||
merge-multiple: true
|
||||
|
||||
- uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
||||
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
@@ -167,7 +167,7 @@ jobs:
|
||||
- debian:bookworm-slim,bookworm-slim,debian-slim
|
||||
- buildpack-deps:bookworm,bookworm,debian
|
||||
steps:
|
||||
- uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
||||
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
|
||||
- uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
with:
|
||||
@@ -262,7 +262,7 @@ jobs:
|
||||
pattern: digests-*
|
||||
merge-multiple: true
|
||||
|
||||
- uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
||||
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
|
||||
44
.github/workflows/ci.yaml
vendored
44
.github/workflows/ci.yaml
vendored
@@ -238,13 +238,13 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@b3958095189f34b95d402a680b6e96b7f194f7b9 # v1
|
||||
uses: rui314/setup-mold@85c79d00377f0d32cdbae595a46de6f7c2fa6599 # v1
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@735e5933943122c5ac182670a935f54a949265c1 # v2.52.4
|
||||
uses: taiki-e/install-action@d12e869b89167df346dd0ff65da342d1fb1202fb # v2.53.2
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Install cargo insta"
|
||||
uses: taiki-e/install-action@735e5933943122c5ac182670a935f54a949265c1 # v2.52.4
|
||||
uses: taiki-e/install-action@d12e869b89167df346dd0ff65da342d1fb1202fb # v2.53.2
|
||||
with:
|
||||
tool: cargo-insta
|
||||
- name: ty mdtests (GitHub annotations)
|
||||
@@ -296,13 +296,13 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@b3958095189f34b95d402a680b6e96b7f194f7b9 # v1
|
||||
uses: rui314/setup-mold@85c79d00377f0d32cdbae595a46de6f7c2fa6599 # v1
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@735e5933943122c5ac182670a935f54a949265c1 # v2.52.4
|
||||
uses: taiki-e/install-action@d12e869b89167df346dd0ff65da342d1fb1202fb # v2.53.2
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Install cargo insta"
|
||||
uses: taiki-e/install-action@735e5933943122c5ac182670a935f54a949265c1 # v2.52.4
|
||||
uses: taiki-e/install-action@d12e869b89167df346dd0ff65da342d1fb1202fb # v2.53.2
|
||||
with:
|
||||
tool: cargo-insta
|
||||
- name: "Run tests"
|
||||
@@ -325,7 +325,7 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@735e5933943122c5ac182670a935f54a949265c1 # v2.52.4
|
||||
uses: taiki-e/install-action@d12e869b89167df346dd0ff65da342d1fb1202fb # v2.53.2
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Run tests"
|
||||
@@ -381,7 +381,7 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@b3958095189f34b95d402a680b6e96b7f194f7b9 # v1
|
||||
uses: rui314/setup-mold@85c79d00377f0d32cdbae595a46de6f7c2fa6599 # v1
|
||||
- name: "Build"
|
||||
run: cargo build --release --locked
|
||||
|
||||
@@ -406,13 +406,13 @@ jobs:
|
||||
MSRV: ${{ steps.msrv.outputs.value }}
|
||||
run: rustup default "${MSRV}"
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@b3958095189f34b95d402a680b6e96b7f194f7b9 # v1
|
||||
uses: rui314/setup-mold@85c79d00377f0d32cdbae595a46de6f7c2fa6599 # v1
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@735e5933943122c5ac182670a935f54a949265c1 # v2.52.4
|
||||
uses: taiki-e/install-action@d12e869b89167df346dd0ff65da342d1fb1202fb # v2.53.2
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Install cargo insta"
|
||||
uses: taiki-e/install-action@735e5933943122c5ac182670a935f54a949265c1 # v2.52.4
|
||||
uses: taiki-e/install-action@d12e869b89167df346dd0ff65da342d1fb1202fb # v2.53.2
|
||||
with:
|
||||
tool: cargo-insta
|
||||
- name: "Run tests"
|
||||
@@ -438,7 +438,7 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install cargo-binstall"
|
||||
uses: cargo-bins/cargo-binstall@ea65a39d2dcca142c53bddd3a097a674e903f475 # v1.12.7
|
||||
uses: cargo-bins/cargo-binstall@8aac5aa2bf0dfaa2863eccad9f43c68fe40e5ec8 # v1.14.1
|
||||
with:
|
||||
tool: cargo-fuzz@0.11.2
|
||||
- name: "Install cargo-fuzz"
|
||||
@@ -460,7 +460,7 @@ jobs:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
|
||||
- uses: astral-sh/setup-uv@445689ea25e0de0a23313031f5fe577c74ae45a1 # v6.3.0
|
||||
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
name: Download Ruff binary to test
|
||||
id: download-cached-binary
|
||||
@@ -661,7 +661,7 @@ jobs:
|
||||
branch: ${{ github.event.pull_request.base.ref }}
|
||||
workflow: "ci.yaml"
|
||||
check_artifacts: true
|
||||
- uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
|
||||
- uses: astral-sh/setup-uv@445689ea25e0de0a23313031f5fe577c74ae45a1 # v6.3.0
|
||||
- name: Fuzz
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
@@ -691,7 +691,7 @@ jobs:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: cargo-bins/cargo-binstall@ea65a39d2dcca142c53bddd3a097a674e903f475 # v1.12.7
|
||||
- uses: cargo-bins/cargo-binstall@8aac5aa2bf0dfaa2863eccad9f43c68fe40e5ec8 # v1.14.1
|
||||
- run: cargo binstall --no-confirm cargo-shear
|
||||
- run: cargo shear
|
||||
|
||||
@@ -712,7 +712,7 @@ jobs:
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels"
|
||||
uses: PyO3/maturin-action@aef21716ff3dcae8a1c301d23ec3e4446972a6e3 # v1.49.1
|
||||
uses: PyO3/maturin-action@35be3186fc8e037e329f06b68dcd807d83dcc6dc # v1.49.2
|
||||
with:
|
||||
args: --out dist
|
||||
- name: "Test wheel"
|
||||
@@ -731,7 +731,7 @@ jobs:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
|
||||
- uses: astral-sh/setup-uv@445689ea25e0de0a23313031f5fe577c74ae45a1 # v6.3.0
|
||||
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
@@ -774,7 +774,7 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
|
||||
uses: astral-sh/setup-uv@445689ea25e0de0a23313031f5fe577c74ae45a1 # v6.3.0
|
||||
- name: "Install Insiders dependencies"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
run: uv pip install -r docs/requirements-insiders.txt --system
|
||||
@@ -906,13 +906,13 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||
- uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
|
||||
- uses: astral-sh/setup-uv@445689ea25e0de0a23313031f5fe577c74ae45a1 # v6.3.0
|
||||
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
|
||||
- name: "Install codspeed"
|
||||
uses: taiki-e/install-action@735e5933943122c5ac182670a935f54a949265c1 # v2.52.4
|
||||
uses: taiki-e/install-action@d12e869b89167df346dd0ff65da342d1fb1202fb # v2.53.2
|
||||
with:
|
||||
tool: cargo-codspeed
|
||||
|
||||
@@ -939,13 +939,13 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||
- uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
|
||||
- uses: astral-sh/setup-uv@445689ea25e0de0a23313031f5fe577c74ae45a1 # v6.3.0
|
||||
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
|
||||
- name: "Install codspeed"
|
||||
uses: taiki-e/install-action@735e5933943122c5ac182670a935f54a949265c1 # v2.52.4
|
||||
uses: taiki-e/install-action@d12e869b89167df346dd0ff65da342d1fb1202fb # v2.53.2
|
||||
with:
|
||||
tool: cargo-codspeed
|
||||
|
||||
|
||||
4
.github/workflows/daily_fuzz.yaml
vendored
4
.github/workflows/daily_fuzz.yaml
vendored
@@ -34,11 +34,11 @@ jobs:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
|
||||
- uses: astral-sh/setup-uv@445689ea25e0de0a23313031f5fe577c74ae45a1 # v6.3.0
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@b3958095189f34b95d402a680b6e96b7f194f7b9 # v1
|
||||
uses: rui314/setup-mold@85c79d00377f0d32cdbae595a46de6f7c2fa6599 # v1
|
||||
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||
- name: Build ruff
|
||||
# A debug build means the script runs slower once it gets started,
|
||||
|
||||
4
.github/workflows/mypy_primer.yaml
vendored
4
.github/workflows/mypy_primer.yaml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
|
||||
uses: astral-sh/setup-uv@445689ea25e0de0a23313031f5fe577c74ae45a1 # v6.3.0
|
||||
|
||||
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||
with:
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
echo "Project selector: $PRIMER_SELECTOR"
|
||||
# Allow the exit code to be 0 or 1, only fail for actual mypy_primer crashes/bugs
|
||||
uvx \
|
||||
--from="git+https://github.com/hauntsaninja/mypy_primer@01a7ca325f674433c58e02416a867178d1571128" \
|
||||
--from="git+https://github.com/hauntsaninja/mypy_primer@e5f55447969d33ae3c7ccdb183e2a37101867270" \
|
||||
mypy_primer \
|
||||
--repo ruff \
|
||||
--type-checker ty \
|
||||
|
||||
2
.github/workflows/publish-pypi.yml
vendored
2
.github/workflows/publish-pypi.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
id-token: write
|
||||
steps:
|
||||
- name: "Install uv"
|
||||
uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
|
||||
uses: astral-sh/setup-uv@445689ea25e0de0a23313031f5fe577c74ae45a1 # v6.3.0
|
||||
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
with:
|
||||
pattern: wheels-*
|
||||
|
||||
94
.github/workflows/ty-ecosystem-analyzer.yaml
vendored
Normal file
94
.github/workflows/ty-ecosystem-analyzer.yaml
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
name: ty ecosystem-analyzer
|
||||
|
||||
permissions: {}
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [labeled]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.sha }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
CARGO_INCREMENTAL: 0
|
||||
CARGO_NET_RETRY: 10
|
||||
CARGO_TERM_COLOR: always
|
||||
RUSTUP_MAX_RETRIES: 10
|
||||
RUST_BACKTRACE: 1
|
||||
REF_NAME: ${{ github.ref_name }}
|
||||
|
||||
jobs:
|
||||
ty-ecosystem-analyzer:
|
||||
name: Compute diagnostic diff
|
||||
runs-on: depot-ubuntu-22.04-32
|
||||
timeout-minutes: 20
|
||||
if: contains(github.event.label.name, 'ecosystem-analyzer')
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
path: ruff
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@445689ea25e0de0a23313031f5fe577c74ae45a1 # v6.3.0
|
||||
|
||||
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||
with:
|
||||
workspaces: "ruff"
|
||||
|
||||
- name: Install Rust toolchain
|
||||
run: rustup show
|
||||
|
||||
- name: Compute diagnostic diff
|
||||
shell: bash
|
||||
run: |
|
||||
cd ruff
|
||||
|
||||
echo "Enabling configuration overloads (see .github/mypy-primer-ty.toml)"
|
||||
mkdir -p ~/.config/ty
|
||||
cp .github/mypy-primer-ty.toml ~/.config/ty/ty.toml
|
||||
|
||||
echo "new commit"
|
||||
git checkout -b new_commit "$GITHUB_SHA"
|
||||
git rev-list --format=%s --max-count=1 new_commit
|
||||
cp crates/ty_python_semantic/resources/primer/good.txt projects_new.txt
|
||||
|
||||
echo "old commit (merge base)"
|
||||
MERGE_BASE="$(git merge-base "$GITHUB_SHA" "origin/$GITHUB_BASE_REF")"
|
||||
git checkout -b old_commit "$MERGE_BASE"
|
||||
git rev-list --format=%s --max-count=1 old_commit
|
||||
cp crates/ty_python_semantic/resources/primer/good.txt projects_old.txt
|
||||
|
||||
cd ..
|
||||
|
||||
uv tool install "git+https://github.com/astral-sh/ecosystem-analyzer@9c34dc514ee9aef6735db1dfebb80f63acbc3440"
|
||||
|
||||
ecosystem-analyzer \
|
||||
--repository ruff \
|
||||
analyze \
|
||||
--projects ruff/projects_old.txt \
|
||||
--commit old_commit \
|
||||
--output diagnostics_old.json
|
||||
|
||||
ecosystem-analyzer \
|
||||
--repository ruff \
|
||||
analyze \
|
||||
--projects ruff/projects_new.txt \
|
||||
--commit new_commit \
|
||||
--output diagnostics_new.json
|
||||
|
||||
ecosystem-analyzer \
|
||||
generate-diff \
|
||||
diagnostics_old.json \
|
||||
diagnostics_new.json \
|
||||
--old-name "main (merge base)" \
|
||||
--new-name "$REF_NAME" \
|
||||
--output-html diff.html
|
||||
|
||||
- name: Upload HTML diff report
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: diff.html
|
||||
path: diff.html
|
||||
49
Cargo.lock
generated
49
Cargo.lock
generated
@@ -930,35 +930,12 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
||||
|
||||
[[package]]
|
||||
name = "env_filter"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_home"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.11.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"env_filter",
|
||||
"jiff",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
@@ -1663,9 +1640,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.173"
|
||||
version = "0.2.174"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
|
||||
[[package]]
|
||||
name = "libcst"
|
||||
@@ -1694,9 +1671,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libmimalloc-sys"
|
||||
version = "0.1.42"
|
||||
version = "0.1.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec9d6fac27761dabcd4ee73571cdb06b7022dc99089acbe5435691edffaac0f4"
|
||||
checksum = "bf88cd67e9de251c1781dbe2f641a1a3ad66eaae831b8a2c38fbdc5ddae16d4d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -1825,9 +1802,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mimalloc"
|
||||
version = "0.1.46"
|
||||
version = "0.1.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "995942f432bbb4822a7e9c3faa87a695185b0d09273ba85f097b54f4e458f2af"
|
||||
checksum = "b1791cbe101e95af5764f06f20f6760521f7158f69dbf9d6baf941ee1bf6bc40"
|
||||
dependencies = [
|
||||
"libmimalloc-sys",
|
||||
]
|
||||
@@ -3046,16 +3023,6 @@ dependencies = [
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_resolver"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"env_logger",
|
||||
"insta",
|
||||
"log",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_semantic"
|
||||
version = "0.0.0"
|
||||
@@ -3603,9 +3570,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.103"
|
||||
version = "2.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8"
|
||||
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
@@ -75,7 +75,6 @@ dashmap = { version = "6.0.1" }
|
||||
dir-test = { version = "0.4.0" }
|
||||
dunce = { version = "1.0.5" }
|
||||
drop_bomb = { version = "0.1.5" }
|
||||
env_logger = { version = "0.11.0" }
|
||||
etcetera = { version = "0.10.0" }
|
||||
fern = { version = "0.7.0" }
|
||||
filetime = { version = "0.2.23" }
|
||||
|
||||
@@ -19,7 +19,7 @@ use tempfile::NamedTempFile;
|
||||
|
||||
use ruff_cache::{CacheKey, CacheKeyHasher};
|
||||
use ruff_diagnostics::Fix;
|
||||
use ruff_linter::message::Message;
|
||||
use ruff_linter::message::OldDiagnostic;
|
||||
use ruff_linter::package::PackageRoot;
|
||||
use ruff_linter::{VERSION, warn_user};
|
||||
use ruff_macros::CacheKey;
|
||||
@@ -341,16 +341,16 @@ impl FileCache {
|
||||
/// Convert the file cache into `Diagnostics`, using `path` as file name.
|
||||
pub(crate) fn to_diagnostics(&self, path: &Path) -> Option<Diagnostics> {
|
||||
self.data.lint.as_ref().map(|lint| {
|
||||
let messages = if lint.messages.is_empty() {
|
||||
let diagnostics = if lint.messages.is_empty() {
|
||||
Vec::new()
|
||||
} else {
|
||||
let file = SourceFileBuilder::new(path.to_string_lossy(), &*lint.source).finish();
|
||||
lint.messages
|
||||
.iter()
|
||||
.map(|msg| {
|
||||
Message::diagnostic(
|
||||
msg.body.clone(),
|
||||
msg.suggestion.clone(),
|
||||
OldDiagnostic::lint(
|
||||
&msg.body,
|
||||
msg.suggestion.as_ref(),
|
||||
msg.range,
|
||||
msg.fix.clone(),
|
||||
msg.parent,
|
||||
@@ -366,7 +366,7 @@ impl FileCache {
|
||||
} else {
|
||||
FxHashMap::default()
|
||||
};
|
||||
Diagnostics::new(messages, notebook_indexes)
|
||||
Diagnostics::new(diagnostics, notebook_indexes)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -427,17 +427,17 @@ pub(crate) struct LintCacheData {
|
||||
}
|
||||
|
||||
impl LintCacheData {
|
||||
pub(crate) fn from_messages(
|
||||
messages: &[Message],
|
||||
pub(crate) fn from_diagnostics(
|
||||
diagnostics: &[OldDiagnostic],
|
||||
notebook_index: Option<NotebookIndex>,
|
||||
) -> Self {
|
||||
let source = if let Some(msg) = messages.first() {
|
||||
let source = if let Some(msg) = diagnostics.first() {
|
||||
msg.source_file().source_text().to_owned()
|
||||
} else {
|
||||
String::new() // No messages, no need to keep the source!
|
||||
};
|
||||
|
||||
let messages = messages
|
||||
let messages = diagnostics
|
||||
.iter()
|
||||
// Parse the kebab-case rule name into a `Rule`. This will fail for syntax errors, so
|
||||
// this also serves to filter them out, but we shouldn't be caching files with syntax
|
||||
@@ -447,7 +447,7 @@ impl LintCacheData {
|
||||
// Make sure that all message use the same source file.
|
||||
assert_eq!(
|
||||
msg.source_file(),
|
||||
messages.first().unwrap().source_file(),
|
||||
diagnostics.first().unwrap().source_file(),
|
||||
"message uses a different source file"
|
||||
);
|
||||
CacheMessage {
|
||||
@@ -612,7 +612,7 @@ mod tests {
|
||||
use test_case::test_case;
|
||||
|
||||
use ruff_cache::CACHE_DIR_NAME;
|
||||
use ruff_linter::message::Message;
|
||||
use ruff_linter::message::OldDiagnostic;
|
||||
use ruff_linter::package::PackageRoot;
|
||||
use ruff_linter::settings::flags;
|
||||
use ruff_linter::settings::types::UnsafeFixes;
|
||||
@@ -680,7 +680,7 @@ mod tests {
|
||||
UnsafeFixes::Enabled,
|
||||
)
|
||||
.unwrap();
|
||||
if diagnostics.messages.iter().any(Message::is_syntax_error) {
|
||||
if diagnostics.inner.iter().any(OldDiagnostic::is_syntax_error) {
|
||||
parse_errors.push(path.clone());
|
||||
}
|
||||
paths.push(path);
|
||||
|
||||
@@ -13,7 +13,6 @@ use rustc_hash::FxHashMap;
|
||||
|
||||
use ruff_db::panic::catch_unwind;
|
||||
use ruff_linter::OldDiagnostic;
|
||||
use ruff_linter::message::Message;
|
||||
use ruff_linter::package::PackageRoot;
|
||||
use ruff_linter::registry::Rule;
|
||||
use ruff_linter::settings::types::UnsafeFixes;
|
||||
@@ -130,9 +129,10 @@ pub(crate) fn check(
|
||||
SourceFileBuilder::new(path.to_string_lossy().as_ref(), "").finish();
|
||||
|
||||
Diagnostics::new(
|
||||
vec![Message::from_diagnostic(
|
||||
OldDiagnostic::new(IOError { message }, TextRange::default(), &dummy),
|
||||
None,
|
||||
vec![OldDiagnostic::new(
|
||||
IOError { message },
|
||||
TextRange::default(),
|
||||
&dummy,
|
||||
)],
|
||||
FxHashMap::default(),
|
||||
)
|
||||
@@ -166,7 +166,7 @@ pub(crate) fn check(
|
||||
|a, b| (a.0 + b.0, a.1 + b.1),
|
||||
);
|
||||
|
||||
all_diagnostics.messages.sort();
|
||||
all_diagnostics.inner.sort();
|
||||
|
||||
// Store the caches.
|
||||
caches.persist()?;
|
||||
@@ -283,7 +283,7 @@ mod test {
|
||||
.with_show_fix_status(true)
|
||||
.emit(
|
||||
&mut output,
|
||||
&diagnostics.messages,
|
||||
&diagnostics.inner,
|
||||
&EmitterContext::new(&FxHashMap::default()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -52,6 +52,6 @@ pub(crate) fn check_stdin(
|
||||
noqa,
|
||||
fix_mode,
|
||||
)?;
|
||||
diagnostics.messages.sort_unstable();
|
||||
diagnostics.inner.sort_unstable();
|
||||
Ok(diagnostics)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ use rustc_hash::FxHashMap;
|
||||
use ruff_linter::OldDiagnostic;
|
||||
use ruff_linter::codes::Rule;
|
||||
use ruff_linter::linter::{FixTable, FixerResult, LinterResult, ParseSource, lint_fix, lint_only};
|
||||
use ruff_linter::message::Message;
|
||||
use ruff_linter::package::PackageRoot;
|
||||
use ruff_linter::pyproject_toml::lint_pyproject_toml;
|
||||
use ruff_linter::settings::types::UnsafeFixes;
|
||||
@@ -32,18 +31,18 @@ use crate::cache::{Cache, FileCacheKey, LintCacheData};
|
||||
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub(crate) struct Diagnostics {
|
||||
pub(crate) messages: Vec<Message>,
|
||||
pub(crate) inner: Vec<OldDiagnostic>,
|
||||
pub(crate) fixed: FixMap,
|
||||
pub(crate) notebook_indexes: FxHashMap<String, NotebookIndex>,
|
||||
}
|
||||
|
||||
impl Diagnostics {
|
||||
pub(crate) fn new(
|
||||
messages: Vec<Message>,
|
||||
diagnostics: Vec<OldDiagnostic>,
|
||||
notebook_indexes: FxHashMap<String, NotebookIndex>,
|
||||
) -> Self {
|
||||
Self {
|
||||
messages,
|
||||
inner: diagnostics,
|
||||
fixed: FixMap::default(),
|
||||
notebook_indexes,
|
||||
}
|
||||
@@ -63,15 +62,12 @@ impl Diagnostics {
|
||||
let name = path.map_or_else(|| "-".into(), Path::to_string_lossy);
|
||||
let source_file = SourceFileBuilder::new(name, "").finish();
|
||||
Self::new(
|
||||
vec![Message::from_diagnostic(
|
||||
OldDiagnostic::new(
|
||||
IOError {
|
||||
message: err.to_string(),
|
||||
},
|
||||
TextRange::default(),
|
||||
&source_file,
|
||||
),
|
||||
None,
|
||||
vec![OldDiagnostic::new(
|
||||
IOError {
|
||||
message: err.to_string(),
|
||||
},
|
||||
TextRange::default(),
|
||||
&source_file,
|
||||
)],
|
||||
FxHashMap::default(),
|
||||
)
|
||||
@@ -102,7 +98,11 @@ impl Diagnostics {
|
||||
let name = path.map_or_else(|| "-".into(), Path::to_string_lossy);
|
||||
let dummy = SourceFileBuilder::new(name, "").finish();
|
||||
Self::new(
|
||||
vec![Message::syntax_error(err, TextRange::default(), dummy)],
|
||||
vec![OldDiagnostic::syntax_error(
|
||||
err,
|
||||
TextRange::default(),
|
||||
dummy,
|
||||
)],
|
||||
FxHashMap::default(),
|
||||
)
|
||||
}
|
||||
@@ -121,7 +121,7 @@ impl Add for Diagnostics {
|
||||
|
||||
impl AddAssign for Diagnostics {
|
||||
fn add_assign(&mut self, other: Self) {
|
||||
self.messages.extend(other.messages);
|
||||
self.inner.extend(other.inner);
|
||||
self.fixed += other.fixed;
|
||||
self.notebook_indexes.extend(other.notebook_indexes);
|
||||
}
|
||||
@@ -202,7 +202,7 @@ pub(crate) fn lint_path(
|
||||
if match fix_mode {
|
||||
flags::FixMode::Generate => true,
|
||||
flags::FixMode::Apply | flags::FixMode::Diff => {
|
||||
diagnostics.messages.is_empty() && diagnostics.fixed.is_empty()
|
||||
diagnostics.inner.is_empty() && diagnostics.fixed.is_empty()
|
||||
}
|
||||
} {
|
||||
return Ok(diagnostics);
|
||||
@@ -222,7 +222,7 @@ pub(crate) fn lint_path(
|
||||
Some(source_type) => source_type,
|
||||
None => match SourceType::from(path) {
|
||||
SourceType::Toml(TomlSourceType::Pyproject) => {
|
||||
let messages = if settings
|
||||
let diagnostics = if settings
|
||||
.rules
|
||||
.iter_enabled()
|
||||
.any(|rule_code| rule_code.lint_source().is_pyproject_toml())
|
||||
@@ -240,7 +240,7 @@ pub(crate) fn lint_path(
|
||||
vec![]
|
||||
};
|
||||
return Ok(Diagnostics {
|
||||
messages,
|
||||
inner: diagnostics,
|
||||
..Diagnostics::default()
|
||||
});
|
||||
}
|
||||
@@ -324,7 +324,7 @@ pub(crate) fn lint_path(
|
||||
};
|
||||
|
||||
let has_error = result.has_syntax_errors();
|
||||
let messages = result.messages;
|
||||
let diagnostics = result.diagnostics;
|
||||
|
||||
if let Some((cache, relative_path, key)) = caching {
|
||||
// We don't cache parsing errors.
|
||||
@@ -335,14 +335,14 @@ pub(crate) fn lint_path(
|
||||
if match fix_mode {
|
||||
flags::FixMode::Generate => true,
|
||||
flags::FixMode::Apply | flags::FixMode::Diff => {
|
||||
messages.is_empty() && fixed.is_empty()
|
||||
diagnostics.is_empty() && fixed.is_empty()
|
||||
}
|
||||
} {
|
||||
cache.update_lint(
|
||||
relative_path.to_owned(),
|
||||
&key,
|
||||
LintCacheData::from_messages(
|
||||
&messages,
|
||||
LintCacheData::from_diagnostics(
|
||||
&diagnostics,
|
||||
transformed.as_ipy_notebook().map(Notebook::index).cloned(),
|
||||
),
|
||||
);
|
||||
@@ -357,7 +357,7 @@ pub(crate) fn lint_path(
|
||||
};
|
||||
|
||||
Ok(Diagnostics {
|
||||
messages,
|
||||
inner: diagnostics,
|
||||
fixed: FixMap::from_iter([(fs::relativize_path(path), fixed)]),
|
||||
notebook_indexes,
|
||||
})
|
||||
@@ -396,7 +396,7 @@ pub(crate) fn lint_stdin(
|
||||
}
|
||||
|
||||
return Ok(Diagnostics {
|
||||
messages: lint_pyproject_toml(&source_file, &settings.linter),
|
||||
inner: lint_pyproject_toml(&source_file, &settings.linter),
|
||||
fixed: FixMap::from_iter([(fs::relativize_path(path), FixTable::default())]),
|
||||
notebook_indexes: FxHashMap::default(),
|
||||
});
|
||||
@@ -417,7 +417,7 @@ pub(crate) fn lint_stdin(
|
||||
};
|
||||
|
||||
// Lint the inputs.
|
||||
let (LinterResult { messages, .. }, transformed, fixed) =
|
||||
let (LinterResult { diagnostics, .. }, transformed, fixed) =
|
||||
if matches!(fix_mode, flags::FixMode::Apply | flags::FixMode::Diff) {
|
||||
if let Ok(FixerResult {
|
||||
result,
|
||||
@@ -501,7 +501,7 @@ pub(crate) fn lint_stdin(
|
||||
};
|
||||
|
||||
Ok(Diagnostics {
|
||||
messages,
|
||||
inner: diagnostics,
|
||||
fixed: FixMap::from_iter([(
|
||||
fs::relativize_path(path.unwrap_or_else(|| Path::new("-"))),
|
||||
fixed,
|
||||
|
||||
@@ -363,7 +363,7 @@ pub fn check(args: CheckCommand, global_options: GlobalConfigArgs) -> Result<Exi
|
||||
Printer::clear_screen()?;
|
||||
printer.write_to_user("Starting linter in watch mode...\n");
|
||||
|
||||
let messages = commands::check::check(
|
||||
let diagnostics = commands::check::check(
|
||||
&files,
|
||||
&pyproject_config,
|
||||
&config_arguments,
|
||||
@@ -372,7 +372,7 @@ pub fn check(args: CheckCommand, global_options: GlobalConfigArgs) -> Result<Exi
|
||||
fix_mode,
|
||||
unsafe_fixes,
|
||||
)?;
|
||||
printer.write_continuously(&mut writer, &messages, preview)?;
|
||||
printer.write_continuously(&mut writer, &diagnostics, preview)?;
|
||||
|
||||
// In watch mode, we may need to re-resolve the configuration.
|
||||
// TODO(charlie): Re-compute other derivative values, like the `printer`.
|
||||
@@ -392,7 +392,7 @@ pub fn check(args: CheckCommand, global_options: GlobalConfigArgs) -> Result<Exi
|
||||
Printer::clear_screen()?;
|
||||
printer.write_to_user("File change detected...\n");
|
||||
|
||||
let messages = commands::check::check(
|
||||
let diagnostics = commands::check::check(
|
||||
&files,
|
||||
&pyproject_config,
|
||||
&config_arguments,
|
||||
@@ -401,7 +401,7 @@ pub fn check(args: CheckCommand, global_options: GlobalConfigArgs) -> Result<Exi
|
||||
fix_mode,
|
||||
unsafe_fixes,
|
||||
)?;
|
||||
printer.write_continuously(&mut writer, &messages, preview)?;
|
||||
printer.write_continuously(&mut writer, &diagnostics, preview)?;
|
||||
}
|
||||
Err(err) => return Err(err.into()),
|
||||
}
|
||||
@@ -463,11 +463,11 @@ pub fn check(args: CheckCommand, global_options: GlobalConfigArgs) -> Result<Exi
|
||||
// there are any violations, unless we're explicitly asked to exit zero on
|
||||
// fix.
|
||||
if cli.exit_non_zero_on_fix {
|
||||
if !diagnostics.fixed.is_empty() || !diagnostics.messages.is_empty() {
|
||||
if !diagnostics.fixed.is_empty() || !diagnostics.inner.is_empty() {
|
||||
return Ok(ExitStatus::Failure);
|
||||
}
|
||||
} else {
|
||||
if !diagnostics.messages.is_empty() {
|
||||
if !diagnostics.inner.is_empty() {
|
||||
return Ok(ExitStatus::Failure);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ use ruff_linter::fs::relativize_path;
|
||||
use ruff_linter::logging::LogLevel;
|
||||
use ruff_linter::message::{
|
||||
AzureEmitter, Emitter, EmitterContext, GithubEmitter, GitlabEmitter, GroupedEmitter,
|
||||
JsonEmitter, JsonLinesEmitter, JunitEmitter, Message, PylintEmitter, RdjsonEmitter,
|
||||
JsonEmitter, JsonLinesEmitter, JunitEmitter, OldDiagnostic, PylintEmitter, RdjsonEmitter,
|
||||
SarifEmitter, TextEmitter,
|
||||
};
|
||||
use ruff_linter::notify_user;
|
||||
@@ -85,7 +85,7 @@ impl Printer {
|
||||
.sum::<usize>();
|
||||
|
||||
if self.flags.intersects(Flags::SHOW_VIOLATIONS) {
|
||||
let remaining = diagnostics.messages.len();
|
||||
let remaining = diagnostics.inner.len();
|
||||
let total = fixed + remaining;
|
||||
if fixed > 0 {
|
||||
let s = if total == 1 { "" } else { "s" };
|
||||
@@ -229,16 +229,16 @@ impl Printer {
|
||||
|
||||
match self.format {
|
||||
OutputFormat::Json => {
|
||||
JsonEmitter.emit(writer, &diagnostics.messages, &context)?;
|
||||
JsonEmitter.emit(writer, &diagnostics.inner, &context)?;
|
||||
}
|
||||
OutputFormat::Rdjson => {
|
||||
RdjsonEmitter.emit(writer, &diagnostics.messages, &context)?;
|
||||
RdjsonEmitter.emit(writer, &diagnostics.inner, &context)?;
|
||||
}
|
||||
OutputFormat::JsonLines => {
|
||||
JsonLinesEmitter.emit(writer, &diagnostics.messages, &context)?;
|
||||
JsonLinesEmitter.emit(writer, &diagnostics.inner, &context)?;
|
||||
}
|
||||
OutputFormat::Junit => {
|
||||
JunitEmitter.emit(writer, &diagnostics.messages, &context)?;
|
||||
JunitEmitter.emit(writer, &diagnostics.inner, &context)?;
|
||||
}
|
||||
OutputFormat::Concise | OutputFormat::Full => {
|
||||
TextEmitter::default()
|
||||
@@ -246,7 +246,7 @@ impl Printer {
|
||||
.with_show_fix_diff(self.flags.intersects(Flags::SHOW_FIX_DIFF))
|
||||
.with_show_source(self.format == OutputFormat::Full)
|
||||
.with_unsafe_fixes(self.unsafe_fixes)
|
||||
.emit(writer, &diagnostics.messages, &context)?;
|
||||
.emit(writer, &diagnostics.inner, &context)?;
|
||||
|
||||
if self.flags.intersects(Flags::SHOW_FIX_SUMMARY) {
|
||||
if !diagnostics.fixed.is_empty() {
|
||||
@@ -262,7 +262,7 @@ impl Printer {
|
||||
GroupedEmitter::default()
|
||||
.with_show_fix_status(show_fix_status(self.fix_mode, fixables.as_ref()))
|
||||
.with_unsafe_fixes(self.unsafe_fixes)
|
||||
.emit(writer, &diagnostics.messages, &context)?;
|
||||
.emit(writer, &diagnostics.inner, &context)?;
|
||||
|
||||
if self.flags.intersects(Flags::SHOW_FIX_SUMMARY) {
|
||||
if !diagnostics.fixed.is_empty() {
|
||||
@@ -274,19 +274,19 @@ impl Printer {
|
||||
self.write_summary_text(writer, diagnostics)?;
|
||||
}
|
||||
OutputFormat::Github => {
|
||||
GithubEmitter.emit(writer, &diagnostics.messages, &context)?;
|
||||
GithubEmitter.emit(writer, &diagnostics.inner, &context)?;
|
||||
}
|
||||
OutputFormat::Gitlab => {
|
||||
GitlabEmitter::default().emit(writer, &diagnostics.messages, &context)?;
|
||||
GitlabEmitter::default().emit(writer, &diagnostics.inner, &context)?;
|
||||
}
|
||||
OutputFormat::Pylint => {
|
||||
PylintEmitter.emit(writer, &diagnostics.messages, &context)?;
|
||||
PylintEmitter.emit(writer, &diagnostics.inner, &context)?;
|
||||
}
|
||||
OutputFormat::Azure => {
|
||||
AzureEmitter.emit(writer, &diagnostics.messages, &context)?;
|
||||
AzureEmitter.emit(writer, &diagnostics.inner, &context)?;
|
||||
}
|
||||
OutputFormat::Sarif => {
|
||||
SarifEmitter.emit(writer, &diagnostics.messages, &context)?;
|
||||
SarifEmitter.emit(writer, &diagnostics.inner, &context)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,13 +301,13 @@ impl Printer {
|
||||
writer: &mut dyn Write,
|
||||
) -> Result<()> {
|
||||
let statistics: Vec<ExpandedStatistics> = diagnostics
|
||||
.messages
|
||||
.inner
|
||||
.iter()
|
||||
.map(|message| (message.noqa_code(), message))
|
||||
.sorted_by_key(|(code, message)| (*code, message.fixable()))
|
||||
.fold(
|
||||
vec![],
|
||||
|mut acc: Vec<((Option<NoqaCode>, &Message), usize)>, (code, message)| {
|
||||
|mut acc: Vec<((Option<NoqaCode>, &OldDiagnostic), usize)>, (code, message)| {
|
||||
if let Some(((prev_code, _prev_message), count)) = acc.last_mut() {
|
||||
if *prev_code == code {
|
||||
*count += 1;
|
||||
@@ -416,20 +416,20 @@ impl Printer {
|
||||
}
|
||||
|
||||
if self.log_level >= LogLevel::Default {
|
||||
let s = if diagnostics.messages.len() == 1 {
|
||||
let s = if diagnostics.inner.len() == 1 {
|
||||
""
|
||||
} else {
|
||||
"s"
|
||||
};
|
||||
notify_user!(
|
||||
"Found {} error{s}. Watching for file changes.",
|
||||
diagnostics.messages.len()
|
||||
diagnostics.inner.len()
|
||||
);
|
||||
}
|
||||
|
||||
let fixables = FixableStatistics::try_from(diagnostics, self.unsafe_fixes);
|
||||
|
||||
if !diagnostics.messages.is_empty() {
|
||||
if !diagnostics.inner.is_empty() {
|
||||
if self.log_level >= LogLevel::Default {
|
||||
writeln!(writer)?;
|
||||
}
|
||||
@@ -439,7 +439,7 @@ impl Printer {
|
||||
.with_show_fix_status(show_fix_status(self.fix_mode, fixables.as_ref()))
|
||||
.with_show_source(preview)
|
||||
.with_unsafe_fixes(self.unsafe_fixes)
|
||||
.emit(writer, &diagnostics.messages, &context)?;
|
||||
.emit(writer, &diagnostics.inner, &context)?;
|
||||
}
|
||||
writer.flush()?;
|
||||
|
||||
@@ -522,7 +522,7 @@ impl FixableStatistics {
|
||||
let mut applicable = 0;
|
||||
let mut inapplicable_unsafe = 0;
|
||||
|
||||
for message in &diagnostics.messages {
|
||||
for message in &diagnostics.inner {
|
||||
if let Some(fix) = message.fix() {
|
||||
if fix.applies(unsafe_fixes.required_applicability()) {
|
||||
applicable += 1;
|
||||
|
||||
@@ -17,6 +17,7 @@ fn command() -> Command {
|
||||
command.arg("analyze");
|
||||
command.arg("graph");
|
||||
command.arg("--preview");
|
||||
command.env_clear();
|
||||
command
|
||||
}
|
||||
|
||||
@@ -565,8 +566,8 @@ fn venv() -> Result<()> {
|
||||
|
||||
----- stderr -----
|
||||
ruff failed
|
||||
Cause: Invalid search path settings
|
||||
Cause: Failed to discover the site-packages directory: Invalid `--python` argument `none`: does not point to a Python executable or a directory on disk
|
||||
Cause: Invalid `--python` argument `none`: does not point to a Python executable or a directory on disk
|
||||
Cause: No such file or directory (os error 2)
|
||||
");
|
||||
});
|
||||
|
||||
|
||||
@@ -352,7 +352,7 @@ impl DisplaySet<'_> {
|
||||
// FIXME: `unicode_width` sometimes disagrees with terminals on how wide a `char`
|
||||
// is. For now, just accept that sometimes the code line will be longer than
|
||||
// desired.
|
||||
let next = unicode_width::UnicodeWidthChar::width(ch).unwrap_or(1);
|
||||
let next = char_width(ch).unwrap_or(1);
|
||||
if taken + next > right - left {
|
||||
was_cut_right = true;
|
||||
break;
|
||||
@@ -377,7 +377,7 @@ impl DisplaySet<'_> {
|
||||
let left: usize = text
|
||||
.chars()
|
||||
.take(left)
|
||||
.map(|ch| unicode_width::UnicodeWidthChar::width(ch).unwrap_or(1))
|
||||
.map(|ch| char_width(ch).unwrap_or(1))
|
||||
.sum();
|
||||
|
||||
let mut annotations = annotations.clone();
|
||||
@@ -1393,6 +1393,7 @@ fn format_body<'m>(
|
||||
let line_length: usize = line.len();
|
||||
let line_range = (current_index, current_index + line_length);
|
||||
let end_line_size = end_line.len();
|
||||
|
||||
body.push(DisplayLine::Source {
|
||||
lineno: Some(current_line),
|
||||
inline_marks: vec![],
|
||||
@@ -1452,12 +1453,12 @@ fn format_body<'m>(
|
||||
let annotation_start_col = line
|
||||
[0..(start - line_start_index).min(line_length)]
|
||||
.chars()
|
||||
.map(|c| unicode_width::UnicodeWidthChar::width(c).unwrap_or(0))
|
||||
.map(|c| char_width(c).unwrap_or(0))
|
||||
.sum::<usize>();
|
||||
let mut annotation_end_col = line
|
||||
[0..(end - line_start_index).min(line_length)]
|
||||
.chars()
|
||||
.map(|c| unicode_width::UnicodeWidthChar::width(c).unwrap_or(0))
|
||||
.map(|c| char_width(c).unwrap_or(0))
|
||||
.sum::<usize>();
|
||||
if annotation_start_col == annotation_end_col {
|
||||
// At least highlight something
|
||||
@@ -1499,7 +1500,7 @@ fn format_body<'m>(
|
||||
let annotation_start_col = line
|
||||
[0..(start - line_start_index).min(line_length)]
|
||||
.chars()
|
||||
.map(|c| unicode_width::UnicodeWidthChar::width(c).unwrap_or(0))
|
||||
.map(|c| char_width(c).unwrap_or(0))
|
||||
.sum::<usize>();
|
||||
let annotation_end_col = annotation_start_col + 1;
|
||||
|
||||
@@ -1558,7 +1559,7 @@ fn format_body<'m>(
|
||||
{
|
||||
let end_mark = line[0..(end - line_start_index).min(line_length)]
|
||||
.chars()
|
||||
.map(|c| unicode_width::UnicodeWidthChar::width(c).unwrap_or(0))
|
||||
.map(|c| char_width(c).unwrap_or(0))
|
||||
.sum::<usize>()
|
||||
.saturating_sub(1);
|
||||
// If the annotation ends on a line-end character, we
|
||||
@@ -1754,3 +1755,11 @@ fn format_inline_marks(
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn char_width(c: char) -> Option<usize> {
|
||||
if c == '\t' {
|
||||
Some(4)
|
||||
} else {
|
||||
unicode_width::UnicodeWidthChar::width(c)
|
||||
}
|
||||
}
|
||||
|
||||
36
crates/ruff_annotate_snippets/tests/fixtures/color/regression_leading_tab_long_line.svg
vendored
Normal file
36
crates/ruff_annotate_snippets/tests/fixtures/color/regression_leading_tab_long_line.svg
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<svg width="1196px" height="128px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { background: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan>: </tspan><tspan class="bold">mismatched types</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--></tspan><tspan> $DIR/non-whitespace-trimming.rs:4:6</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">4 |</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">...</tspan><tspan> s_data['d_dails'] = bb['contacted'][hostip]['ansible_facts']['ansible_devices']['vda']['vendor'] + " " + bb['contacted'][hostip</tspan><tspan class="fg-bright-blue bold">...</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">^^^^^^</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
20
crates/ruff_annotate_snippets/tests/fixtures/color/regression_leading_tab_long_line.toml
vendored
Normal file
20
crates/ruff_annotate_snippets/tests/fixtures/color/regression_leading_tab_long_line.toml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
[message]
|
||||
level = "Error"
|
||||
id = "E0308"
|
||||
title = "mismatched types"
|
||||
|
||||
[[message.snippets]]
|
||||
source = """
|
||||
s_data['d_dails'] = bb['contacted'][hostip]['ansible_facts']['ansible_devices']['vda']['vendor'] + " " + bb['contacted'][hostip]['an
|
||||
"""
|
||||
line_start = 4
|
||||
origin = "$DIR/non-whitespace-trimming.rs"
|
||||
|
||||
[[message.snippets.annotations]]
|
||||
label = ""
|
||||
level = "Error"
|
||||
range = [5, 11]
|
||||
|
||||
[renderer]
|
||||
# anonymized_line_numbers = true
|
||||
color = true
|
||||
@@ -21,7 +21,7 @@
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan>: </tspan><tspan class="bold">mismatched types</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--></tspan><tspan> $DIR/non-whitespace-trimming.rs:4:242</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--></tspan><tspan> $DIR/non-whitespace-trimming.rs:4:238</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
@@ -13,12 +13,12 @@ origin = "$DIR/non-whitespace-trimming.rs"
|
||||
[[message.snippets.annotations]]
|
||||
label = "expected `()`, found integer"
|
||||
level = "Error"
|
||||
range = [241, 243]
|
||||
range = [237, 239]
|
||||
|
||||
[[message.snippets.annotations]]
|
||||
label = "expected due to this"
|
||||
level = "Error"
|
||||
range = [236, 238]
|
||||
range = [232, 234]
|
||||
|
||||
|
||||
[renderer]
|
||||
|
||||
@@ -348,6 +348,58 @@ fn benchmark_many_tuple_assignments(criterion: &mut Criterion) {
|
||||
});
|
||||
}
|
||||
|
||||
fn benchmark_many_attribute_assignments(criterion: &mut Criterion) {
|
||||
setup_rayon();
|
||||
|
||||
criterion.bench_function("ty_micro[many_attribute_assignments]", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| {
|
||||
// This is a regression benchmark for https://github.com/astral-sh/ty/issues/627.
|
||||
// Before this was fixed, the following sample would take >1s to type check.
|
||||
setup_micro_case(
|
||||
r#"
|
||||
class C:
|
||||
def f(self: "C"):
|
||||
if isinstance(self.a, str):
|
||||
return
|
||||
|
||||
if isinstance(self.b, str):
|
||||
return
|
||||
if isinstance(self.b, str):
|
||||
return
|
||||
if isinstance(self.b, str):
|
||||
return
|
||||
if isinstance(self.b, str):
|
||||
return
|
||||
if isinstance(self.b, str):
|
||||
return
|
||||
if isinstance(self.b, str):
|
||||
return
|
||||
if isinstance(self.b, str):
|
||||
return
|
||||
if isinstance(self.b, str):
|
||||
return
|
||||
if isinstance(self.b, str):
|
||||
return
|
||||
if isinstance(self.b, str):
|
||||
return
|
||||
if isinstance(self.b, str):
|
||||
return
|
||||
if isinstance(self.b, str):
|
||||
return
|
||||
"#,
|
||||
)
|
||||
},
|
||||
|case| {
|
||||
let Case { db, .. } = case;
|
||||
let result = db.check();
|
||||
assert!(!result.is_empty());
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
struct ProjectBenchmark<'a> {
|
||||
project: InstalledProject<'a>,
|
||||
fs: MemoryFileSystem,
|
||||
@@ -377,8 +429,7 @@ impl<'a> ProjectBenchmark<'a> {
|
||||
metadata.apply_options(Options {
|
||||
environment: Some(EnvironmentOptions {
|
||||
python_version: Some(RangedValue::cli(self.project.config.python_version)),
|
||||
python: (!self.project.config().dependencies.is_empty())
|
||||
.then_some(RelativePathBuf::cli(SystemPath::new(".venv"))),
|
||||
python: Some(RelativePathBuf::cli(SystemPath::new(".venv"))),
|
||||
..EnvironmentOptions::default()
|
||||
}),
|
||||
..Options::default()
|
||||
@@ -483,6 +534,7 @@ criterion_group!(
|
||||
micro,
|
||||
benchmark_many_string_assignments,
|
||||
benchmark_many_tuple_assignments,
|
||||
benchmark_many_attribute_assignments,
|
||||
);
|
||||
criterion_group!(project, anyio, attrs, hydra);
|
||||
criterion_main!(check_file, micro, project);
|
||||
|
||||
@@ -36,8 +36,7 @@ impl<'a> Benchmark<'a> {
|
||||
metadata.apply_options(Options {
|
||||
environment: Some(EnvironmentOptions {
|
||||
python_version: Some(RangedValue::cli(self.project.config.python_version)),
|
||||
python: (!self.project.config().dependencies.is_empty())
|
||||
.then_some(RelativePathBuf::cli(SystemPath::new(".venv"))),
|
||||
python: Some(RelativePathBuf::cli(SystemPath::new(".venv"))),
|
||||
..EnvironmentOptions::default()
|
||||
}),
|
||||
..Options::default()
|
||||
@@ -204,8 +203,23 @@ static SYMPY: std::sync::LazyLock<Benchmark<'static>> = std::sync::LazyLock::new
|
||||
)
|
||||
});
|
||||
|
||||
#[bench(args=[&*ALTAIR, &*FREQTRADE, &*PYDANTIC], sample_size=2, sample_count=3)]
|
||||
fn small(bencher: Bencher, benchmark: &Benchmark) {
|
||||
static TANJUN: std::sync::LazyLock<Benchmark<'static>> = std::sync::LazyLock::new(|| {
|
||||
Benchmark::new(
|
||||
RealWorldProject {
|
||||
name: "tanjun",
|
||||
repository: "https://github.com/FasterSpeeding/Tanjun",
|
||||
commit: "69f40db188196bc59516b6c69849c2d85fbc2f4a",
|
||||
paths: vec![SystemPath::new("tanjun")],
|
||||
dependencies: vec!["hikari", "alluka"],
|
||||
max_dep_date: "2025-06-17",
|
||||
python_version: PythonVersion::PY312,
|
||||
},
|
||||
100,
|
||||
)
|
||||
});
|
||||
|
||||
#[track_caller]
|
||||
fn run_single_threaded(bencher: Bencher, benchmark: &Benchmark) {
|
||||
bencher
|
||||
.with_inputs(|| benchmark.setup_iteration())
|
||||
.bench_local_refs(|db| {
|
||||
@@ -213,41 +227,55 @@ fn small(bencher: Bencher, benchmark: &Benchmark) {
|
||||
});
|
||||
}
|
||||
|
||||
#[bench(args=[&*ALTAIR, &*FREQTRADE, &*PYDANTIC, &*TANJUN], sample_size=2, sample_count=3)]
|
||||
fn small(bencher: Bencher, benchmark: &Benchmark) {
|
||||
run_single_threaded(bencher, benchmark);
|
||||
}
|
||||
|
||||
#[bench(args=[&*COLOUR_SCIENCE, &*PANDAS], sample_size=1, sample_count=3)]
|
||||
fn medium(bencher: Bencher, benchmark: &Benchmark) {
|
||||
bencher
|
||||
.with_inputs(|| benchmark.setup_iteration())
|
||||
.bench_local_refs(|db| {
|
||||
check_project(db, benchmark.max_diagnostics);
|
||||
});
|
||||
run_single_threaded(bencher, benchmark);
|
||||
}
|
||||
|
||||
#[bench(args=[&*SYMPY], sample_size=1, sample_count=2)]
|
||||
fn large(bencher: Bencher, benchmark: &Benchmark) {
|
||||
run_single_threaded(bencher, benchmark);
|
||||
}
|
||||
|
||||
#[bench(args=[&*PYDANTIC], sample_size=3, sample_count=3)]
|
||||
fn multithreaded(bencher: Bencher, benchmark: &Benchmark) {
|
||||
let thread_pool = ThreadPoolBuilder::new().build().unwrap();
|
||||
|
||||
bencher
|
||||
.with_inputs(|| benchmark.setup_iteration())
|
||||
.bench_local_refs(|db| {
|
||||
check_project(db, benchmark.max_diagnostics);
|
||||
.bench_local_values(|db| {
|
||||
thread_pool.install(|| {
|
||||
check_project(&db, benchmark.max_diagnostics);
|
||||
db
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let filter =
|
||||
std::env::var("TY_LOG").unwrap_or("ty_walltime=info,ruff_benchmark=info".to_string());
|
||||
|
||||
let _logging = setup_logging_with_filter(&filter).expect("Filter to be valid");
|
||||
|
||||
// Disable multithreading for now due to
|
||||
// https://github.com/salsa-rs/salsa/issues/918.
|
||||
//
|
||||
// Salsa has a fast-path for the first db when looking up ingredients.
|
||||
// It seems that this fast-path becomes extremely slow for all db's other
|
||||
// than the first one, especially when using multithreading (10x slower than the first run).
|
||||
ThreadPoolBuilder::new()
|
||||
.num_threads(1)
|
||||
.use_current_thread()
|
||||
.build_global()
|
||||
.unwrap();
|
||||
|
||||
let filter =
|
||||
std::env::var("TY_LOG").unwrap_or("ty_walltime=info,ruff_benchmark=info".to_string());
|
||||
|
||||
let _logging = setup_logging_with_filter(&filter).expect("Filter to be valid");
|
||||
|
||||
// Salsa uses an optimized lookup for the ingredient index when using only a single database.
|
||||
// This optimization results in at least a 10% speedup compared to when using multiple databases.
|
||||
// To reduce noise, run one benchmark so that all benchmarks take the less optimized "not the first db"
|
||||
// branch when looking up the ingredient index.
|
||||
{
|
||||
let db = TANJUN.setup_iteration();
|
||||
check_project(&db, TANJUN.max_diagnostics);
|
||||
}
|
||||
|
||||
divan::main();
|
||||
}
|
||||
|
||||
@@ -74,19 +74,17 @@ impl<'a> RealWorldProject<'a> {
|
||||
};
|
||||
|
||||
// Install dependencies if specified
|
||||
if !checkout.project().dependencies.is_empty() {
|
||||
tracing::debug!(
|
||||
"Installing {} dependencies for project '{}'...",
|
||||
checkout.project().dependencies.len(),
|
||||
checkout.project().name
|
||||
);
|
||||
let start = std::time::Instant::now();
|
||||
install_dependencies(&checkout)?;
|
||||
tracing::debug!(
|
||||
"Dependency installation completed in {:.2}s",
|
||||
start.elapsed().as_secs_f64()
|
||||
);
|
||||
}
|
||||
tracing::debug!(
|
||||
"Installing {} dependencies for project '{}'...",
|
||||
checkout.project().dependencies.len(),
|
||||
checkout.project().name
|
||||
);
|
||||
let start_install = std::time::Instant::now();
|
||||
install_dependencies(&checkout)?;
|
||||
tracing::debug!(
|
||||
"Dependency installation completed in {:.2}s",
|
||||
start_install.elapsed().as_secs_f64()
|
||||
);
|
||||
|
||||
tracing::debug!("Project setup took: {:.2}s", start.elapsed().as_secs_f64());
|
||||
|
||||
@@ -281,6 +279,14 @@ fn install_dependencies(checkout: &Checkout) -> Result<()> {
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
|
||||
if checkout.project().dependencies.is_empty() {
|
||||
tracing::debug!(
|
||||
"No dependencies to install for project '{}'",
|
||||
checkout.project().name
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Install dependencies with date constraint in the isolated environment
|
||||
let mut cmd = Command::new("uv");
|
||||
cmd.args([
|
||||
|
||||
@@ -30,14 +30,14 @@ filetime = { workspace = true }
|
||||
glob = { workspace = true }
|
||||
ignore = { workspace = true, optional = true }
|
||||
matchit = { workspace = true }
|
||||
path-slash = { workspace = true }
|
||||
rustc-hash = { workspace = true }
|
||||
salsa = { workspace = true }
|
||||
schemars = { workspace = true, optional = true }
|
||||
serde = { workspace = true, optional = true }
|
||||
path-slash = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tracing-subscriber = { workspace = true, optional = true }
|
||||
rustc-hash = { workspace = true }
|
||||
zip = { workspace = true }
|
||||
|
||||
[target.'cfg(target_arch="wasm32")'.dependencies]
|
||||
|
||||
@@ -735,6 +735,9 @@ pub enum DiagnosticId {
|
||||
/// # no `[overrides.rules]`
|
||||
/// ```
|
||||
UselessOverridesSection,
|
||||
|
||||
/// Use of a deprecated setting.
|
||||
DeprecatedSetting,
|
||||
}
|
||||
|
||||
impl DiagnosticId {
|
||||
@@ -773,6 +776,7 @@ impl DiagnosticId {
|
||||
DiagnosticId::EmptyInclude => "empty-include",
|
||||
DiagnosticId::UnnecessaryOverridesSection => "unnecessary-overrides-section",
|
||||
DiagnosticId::UselessOverridesSection => "useless-overrides-section",
|
||||
DiagnosticId::DeprecatedSetting => "deprecated-setting",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use anyhow::Result;
|
||||
use anyhow::{Context, Result};
|
||||
use std::sync::Arc;
|
||||
use zip::CompressionMethod;
|
||||
|
||||
@@ -9,7 +9,7 @@ use ruff_db::{Db as SourceDb, Upcast};
|
||||
use ruff_python_ast::PythonVersion;
|
||||
use ty_python_semantic::lint::{LintRegistry, RuleSelection};
|
||||
use ty_python_semantic::{
|
||||
Db, Program, ProgramSettings, PythonPath, PythonPlatform, PythonVersionSource,
|
||||
Db, Program, ProgramSettings, PythonEnvironment, PythonPlatform, PythonVersionSource,
|
||||
PythonVersionWithSource, SearchPathSettings, SysPrefixPathOrigin, default_lint_registry,
|
||||
};
|
||||
|
||||
@@ -35,24 +35,32 @@ impl ModuleDb {
|
||||
python_version: PythonVersion,
|
||||
venv_path: Option<SystemPathBuf>,
|
||||
) -> Result<Self> {
|
||||
let mut search_paths = SearchPathSettings::new(src_roots);
|
||||
if let Some(venv_path) = venv_path {
|
||||
search_paths.python_path =
|
||||
PythonPath::sys_prefix(venv_path, SysPrefixPathOrigin::PythonCliFlag);
|
||||
}
|
||||
|
||||
let db = Self::default();
|
||||
let mut search_paths = SearchPathSettings::new(src_roots);
|
||||
// TODO: Consider calling `PythonEnvironment::discover` if the `venv_path` is not provided.
|
||||
if let Some(venv_path) = venv_path {
|
||||
let environment =
|
||||
PythonEnvironment::new(venv_path, SysPrefixPathOrigin::PythonCliFlag, db.system())?;
|
||||
search_paths.site_packages_paths = environment
|
||||
.site_packages_paths(db.system())
|
||||
.context("Failed to discover the site-packages directory")?
|
||||
.into_vec();
|
||||
}
|
||||
let search_paths = search_paths
|
||||
.to_search_paths(db.system(), db.vendored())
|
||||
.context("Invalid search path settings")?;
|
||||
|
||||
Program::from_settings(
|
||||
&db,
|
||||
ProgramSettings {
|
||||
python_version: Some(PythonVersionWithSource {
|
||||
python_version: PythonVersionWithSource {
|
||||
version: python_version,
|
||||
source: PythonVersionSource::default(),
|
||||
}),
|
||||
},
|
||||
python_platform: PythonPlatform::default(),
|
||||
search_paths,
|
||||
},
|
||||
)?;
|
||||
);
|
||||
|
||||
Ok(db)
|
||||
}
|
||||
|
||||
@@ -36,14 +36,20 @@ impl fmt::Display for AnalyzeSettings {
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, CacheKey)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Serialize, serde::Deserialize),
|
||||
serde(rename_all = "kebab-case")
|
||||
)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
|
||||
pub enum Direction {
|
||||
/// Construct a map from module to its dependencies (i.e., the modules that it imports).
|
||||
#[default]
|
||||
#[cfg_attr(feature = "serde", serde(alias = "Dependencies"))]
|
||||
Dependencies,
|
||||
/// Construct a map from module to its dependents (i.e., the modules that import it).
|
||||
#[cfg_attr(feature = "serde", serde(alias = "Dependents"))]
|
||||
Dependents,
|
||||
}
|
||||
|
||||
|
||||
@@ -165,3 +165,15 @@ async def test_trio_async115_helpers():
|
||||
|
||||
await trio.sleep(seconds=0) # ASYNC115
|
||||
await trio.sleep(delay=0) # OK
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18740
|
||||
# The autofix for this is unsafe due to the comments.
|
||||
async def func():
|
||||
import trio
|
||||
|
||||
await (
|
||||
trio # comment
|
||||
.sleep( # comment
|
||||
0 # comment
|
||||
)
|
||||
)
|
||||
|
||||
@@ -128,3 +128,11 @@ async def test_trio_async116_helpers():
|
||||
|
||||
await trio.sleep(seconds=86401) # ASYNC116
|
||||
await trio.sleep(delay=86401) # OK
|
||||
|
||||
|
||||
async def _():
|
||||
import trio
|
||||
from trio import sleep
|
||||
|
||||
await sleep(18446744073709551616)
|
||||
await trio.sleep(99999999999999999999)
|
||||
|
||||
@@ -29,3 +29,13 @@ def this_is_fine():
|
||||
o = object()
|
||||
if callable(o):
|
||||
print("Ooh, this is actually callable.")
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18741
|
||||
# The autofix for this is unsafe due to the comments.
|
||||
hasattr(
|
||||
# comment 1
|
||||
obj, # comment 2
|
||||
# comment 3
|
||||
"__call__", # comment 4
|
||||
# comment 5
|
||||
)
|
||||
|
||||
@@ -36,9 +36,19 @@ set(
|
||||
# some more
|
||||
)
|
||||
|
||||
# t-strings
|
||||
print(t"Hello {set(f(a) for a in 'abc')} World")
|
||||
print(t"Hello { set(f(a) for a in 'abc') } World")
|
||||
small_nums = t"{set(a if a < 6 else 0 for a in range(3))}"
|
||||
print(t"Hello {set(a for a in range(3))} World")
|
||||
print(t"{set(a for a in 'abc') - set(a for a in 'ab')}")
|
||||
print(t"{ set(a for a in 'abc') - set(a for a in 'ab') }")
|
||||
|
||||
|
||||
# Not built-in set.
|
||||
def set(*args, **kwargs):
|
||||
return None
|
||||
|
||||
set(2 * x for x in range(3))
|
||||
set(x for x in range(3))
|
||||
|
||||
|
||||
@@ -34,4 +34,16 @@ s = set( # outer set comment
|
||||
))))
|
||||
|
||||
# Test trailing comma case
|
||||
s = set([x for x in range(3)],)
|
||||
s = set([x for x in range(3)],)
|
||||
|
||||
s = t"{set([x for x in 'ab'])}"
|
||||
s = t'{set([x for x in "ab"])}'
|
||||
|
||||
def f(x):
|
||||
return x
|
||||
|
||||
s = t"{set([f(x) for x in 'ab'])}"
|
||||
|
||||
s = t"{ set([x for x in 'ab']) | set([x for x in 'ab']) }"
|
||||
s = t"{set([x for x in 'ab']) | set([x for x in 'ab'])}"
|
||||
|
||||
|
||||
@@ -24,3 +24,12 @@ f"{set(['a', 'b']) - set(['a'])}"
|
||||
f"{ set(['a', 'b']) - set(['a']) }"
|
||||
f"a {set(['a', 'b']) - set(['a'])} b"
|
||||
f"a { set(['a', 'b']) - set(['a']) } b"
|
||||
|
||||
t"{set([1,2,3])}"
|
||||
t"{set(['a', 'b'])}"
|
||||
t'{set(["a", "b"])}'
|
||||
|
||||
t"{set(['a', 'b']) - set(['a'])}"
|
||||
t"{ set(['a', 'b']) - set(['a']) }"
|
||||
t"a {set(['a', 'b']) - set(['a'])} b"
|
||||
t"a { set(['a', 'b']) - set(['a']) } b"
|
||||
|
||||
@@ -27,3 +27,13 @@ dict(
|
||||
|
||||
tuple( # comment
|
||||
)
|
||||
|
||||
t"{dict(x='y')}"
|
||||
t'{dict(x="y")}'
|
||||
t"{dict()}"
|
||||
t"a {dict()} b"
|
||||
|
||||
t"{dict(x='y') | dict(y='z')}"
|
||||
t"{ dict(x='y') | dict(y='z') }"
|
||||
t"a {dict(x='y') | dict(y='z')} b"
|
||||
t"a { dict(x='y') | dict(y='z') } b"
|
||||
|
||||
@@ -70,3 +70,8 @@ list(map(lambda: 1, "xyz"))
|
||||
list(map(lambda x, y: x, [(1, 2), (3, 4)]))
|
||||
list(map(lambda: 1, "xyz"))
|
||||
list(map(lambda x, y: x, [(1, 2), (3, 4)]))
|
||||
|
||||
# When inside t-string, then the fix should be surrounded by whitespace
|
||||
_ = t"{set(map(lambda x: x % 2 == 0, nums))}"
|
||||
_ = t"{dict(map(lambda v: (v, v**2), nums))}"
|
||||
|
||||
|
||||
@@ -90,3 +90,14 @@ def func():
|
||||
|
||||
def func():
|
||||
{(a, b): a + b for (a, b) in [(1, 2), (3, 4)]} # OK
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18764
|
||||
{ # 1
|
||||
a # 2
|
||||
: # 3
|
||||
None # 4
|
||||
for # 5
|
||||
a # 6
|
||||
in # 7
|
||||
iterable # 8
|
||||
} # 9
|
||||
|
||||
6
crates/ruff_linter/resources/test/fixtures/flake8_errmsg/EM101_byte_string.py
vendored
Normal file
6
crates/ruff_linter/resources/test/fixtures/flake8_errmsg/EM101_byte_string.py
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
def f_byte():
|
||||
raise RuntimeError(b"This is an example exception")
|
||||
|
||||
|
||||
def f_byte_empty():
|
||||
raise RuntimeError(b"")
|
||||
@@ -32,6 +32,12 @@ except ...:
|
||||
|
||||
### No errors
|
||||
|
||||
logging.info("", exc_info=ValueError())
|
||||
logger.info("", exc_info=ValueError())
|
||||
|
||||
logging.info("", exc_info=(exc_type, exc_value, exc_traceback))
|
||||
logger.info("", exc_info=(exc_type, exc_value, exc_traceback))
|
||||
|
||||
logging.info("", exc_info=a)
|
||||
logger.info("", exc_info=a)
|
||||
|
||||
|
||||
@@ -14,3 +14,6 @@ range(0, 10, 1)
|
||||
range(0, 10, step=1)
|
||||
range(start=0, stop=10)
|
||||
range(0, stop=10)
|
||||
|
||||
# regression test for https://github.com/astral-sh/ruff/pull/18805
|
||||
range((0), 42)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Union,
|
||||
)
|
||||
|
||||
@@ -90,3 +91,22 @@ class Foo:
|
||||
|
||||
def bad5(self, arg: int | (float | complex)) -> None:
|
||||
...
|
||||
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18298
|
||||
# fix must not yield runtime `None | None | ...` (TypeError)
|
||||
class Issue18298:
|
||||
def f1(self, arg: None | int | None | float = None) -> None: # PYI041 - no fix
|
||||
pass
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
||||
def f2(self, arg: None | int | None | float = None) -> None: ... # PYI041 - with fix
|
||||
|
||||
else:
|
||||
|
||||
def f2(self, arg=None) -> None:
|
||||
pass
|
||||
|
||||
def f3(self, arg: None | float | None | int | None = None) -> None: # PYI041 - with fix
|
||||
pass
|
||||
@@ -70,3 +70,11 @@ class Foo:
|
||||
def bad4(self, arg: Union[float | complex, int]) -> None: ... # PYI041
|
||||
|
||||
def bad5(self, arg: int | (float | complex)) -> None: ... # PYI041
|
||||
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18298
|
||||
# fix must not yield runtime `None | None | ...` (TypeError)
|
||||
class Issue18298:
|
||||
def f1(self, arg: None | int | None | float = None) -> None: ... # PYI041 - with fix
|
||||
|
||||
def f3(self, arg: None | float | None | int | None = None) -> None: ... # PYI041 - with fix
|
||||
8
crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI041_2.py
vendored
Normal file
8
crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI041_2.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18298
|
||||
# fix must not yield runtime `None | None | ...` (TypeError)
|
||||
class Issue18298:
|
||||
def f1(self, arg: None | int | None | float = None) -> None:
|
||||
pass
|
||||
@@ -76,3 +76,20 @@ def aliased_parentheses_with_params():
|
||||
)
|
||||
def aliased_parentheses_no_params_multiline():
|
||||
return 42
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18770
|
||||
@pytest.fixture(
|
||||
# TODO: use module scope
|
||||
# scope="module"
|
||||
)
|
||||
def my_fixture(): ...
|
||||
|
||||
|
||||
@(pytest.fixture())
|
||||
def outer_paren_fixture_no_params():
|
||||
return 42
|
||||
|
||||
|
||||
@(fixture())
|
||||
def outer_paren_imported_fixture_no_params():
|
||||
return 42
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
@pytest.mark.foo(scope="module")
|
||||
def ok_due_to_missing_import():
|
||||
pass
|
||||
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@@ -72,3 +77,25 @@ class TestClass:
|
||||
@pytest.mark.foo()
|
||||
def test_something():
|
||||
pass
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18770
|
||||
@pytest.mark.parametrize(
|
||||
# TODO: fix later
|
||||
# ("param1", "param2"),
|
||||
# (
|
||||
# (1, 2),
|
||||
# (3, 4),
|
||||
# ),
|
||||
)
|
||||
def test_bar(param1, param2): ...
|
||||
|
||||
|
||||
@(pytest.mark.foo())
|
||||
def test_outer_paren_mark_function():
|
||||
pass
|
||||
|
||||
|
||||
class TestClass:
|
||||
@(pytest.mark.foo())
|
||||
def test_method_outer_paren():
|
||||
pass
|
||||
|
||||
@@ -105,3 +105,8 @@ if future.exception():
|
||||
future = executor.submit(float, "a")
|
||||
if future.exception():
|
||||
raise future.Exception()
|
||||
|
||||
|
||||
raise TypeError(
|
||||
# comment
|
||||
)
|
||||
|
||||
@@ -49,3 +49,13 @@ class Baz:
|
||||
def prop4(self) -> None:
|
||||
print("I've run out of things to say")
|
||||
return None
|
||||
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18774
|
||||
class _:
|
||||
def foo(bar):
|
||||
if not bar:
|
||||
return
|
||||
return (
|
||||
None # comment
|
||||
)
|
||||
|
||||
@@ -128,3 +128,16 @@ def write_models(directory, Models):
|
||||
pass; \
|
||||
\
|
||||
#
|
||||
|
||||
# Regression tests for: https://github.com/astral-sh/ruff/issues/18209
|
||||
try:
|
||||
1 / 0
|
||||
except ():
|
||||
pass
|
||||
|
||||
|
||||
BaseException = ValueError
|
||||
try:
|
||||
int("a")
|
||||
except BaseException:
|
||||
pass
|
||||
@@ -49,3 +49,9 @@ def foo(some_other: object):
|
||||
# OK
|
||||
def foo(some_other):
|
||||
a = some_other.get('a', None)
|
||||
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18777
|
||||
def foo():
|
||||
dict = {"Tom": 23, "Maria": 23, "Dog": 11}
|
||||
age = dict.get("Cat", None)
|
||||
|
||||
@@ -23,3 +23,14 @@ for k, v in zip(d2.keys(), d2.values()): # SIM911
|
||||
items = zip(x.keys(), x.values()) # OK
|
||||
|
||||
items.bar = zip(x.keys(), x.values()) # OK
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18777
|
||||
def foo():
|
||||
dict = {}
|
||||
for country, stars in zip(dict.keys(), dict.values()):
|
||||
...
|
||||
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18776
|
||||
flag_stars = {}
|
||||
for country, stars in(zip)(flag_stars.keys(), flag_stars.values()):...
|
||||
|
||||
@@ -2,13 +2,81 @@ import os.path
|
||||
from pathlib import Path
|
||||
from os.path import getsize
|
||||
|
||||
filename = "filename"
|
||||
filename1 = __file__
|
||||
filename2 = Path("filename")
|
||||
|
||||
|
||||
os.path.getsize("filename")
|
||||
os.path.getsize(b"filename")
|
||||
os.path.getsize(Path("filename"))
|
||||
os.path.getsize(__file__)
|
||||
|
||||
os.path.getsize(filename)
|
||||
os.path.getsize(filename1)
|
||||
os.path.getsize(filename2)
|
||||
|
||||
os.path.getsize(filename="filename")
|
||||
os.path.getsize(filename=b"filename")
|
||||
os.path.getsize(filename=Path("filename"))
|
||||
os.path.getsize(filename=__file__)
|
||||
|
||||
getsize("filename")
|
||||
getsize(b"filename")
|
||||
getsize(Path("filename"))
|
||||
getsize(__file__)
|
||||
|
||||
getsize(filename="filename")
|
||||
getsize(filename=b"filename")
|
||||
getsize(filename=Path("filename"))
|
||||
getsize(filename=__file__)
|
||||
|
||||
getsize(filename)
|
||||
getsize(filename1)
|
||||
getsize(filename2)
|
||||
|
||||
|
||||
os.path.getsize(
|
||||
"filename", # comment
|
||||
)
|
||||
|
||||
os.path.getsize(
|
||||
# comment
|
||||
"filename"
|
||||
,
|
||||
# comment
|
||||
)
|
||||
|
||||
os.path.getsize(
|
||||
# comment
|
||||
b"filename"
|
||||
# comment
|
||||
)
|
||||
|
||||
os.path.getsize( # comment
|
||||
Path(__file__)
|
||||
# comment
|
||||
) # comment
|
||||
|
||||
getsize( # comment
|
||||
"filename")
|
||||
|
||||
getsize( # comment
|
||||
b"filename",
|
||||
#comment
|
||||
)
|
||||
|
||||
os.path.getsize("file" + "name")
|
||||
|
||||
getsize \
|
||||
\
|
||||
\
|
||||
( # comment
|
||||
"filename",
|
||||
)
|
||||
|
||||
getsize(Path("filename").resolve())
|
||||
|
||||
import pathlib
|
||||
|
||||
os.path.getsize(pathlib.Path("filename"))
|
||||
5
crates/ruff_linter/resources/test/fixtures/flake8_use_pathlib/PTH202_2.py
vendored
Normal file
5
crates/ruff_linter/resources/test/fixtures/flake8_use_pathlib/PTH202_2.py
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import os
|
||||
|
||||
os.path.getsize(filename="filename")
|
||||
os.path.getsize(filename=b"filename")
|
||||
os.path.getsize(filename=__file__)
|
||||
@@ -85,3 +85,19 @@ import builtins
|
||||
|
||||
for i in builtins.list(nested_tuple): # PERF101
|
||||
pass
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18783
|
||||
items = (1, 2, 3)
|
||||
for i in(list)(items):
|
||||
print(i)
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18784
|
||||
items = (1, 2, 3)
|
||||
for i in ( # 1
|
||||
list # 2
|
||||
# 3
|
||||
)( # 4
|
||||
items # 5
|
||||
# 6
|
||||
):
|
||||
print(i)
|
||||
|
||||
@@ -163,4 +163,10 @@ def foo():
|
||||
|
||||
for k, v in src:
|
||||
if lambda: 0:
|
||||
dst[k] = v
|
||||
dst[k] = v
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18859
|
||||
def foo():
|
||||
v = {}
|
||||
for o,(x,)in():
|
||||
v[x,]=o
|
||||
|
||||
@@ -14,3 +14,6 @@ hidden = {"a": "!"}
|
||||
"" % {
|
||||
'test1': '',
|
||||
'test2': '',
|
||||
}
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18806
|
||||
|
||||
@@ -4,3 +4,11 @@
|
||||
"{bar:{spam}}".format(bar=2, spam=3, eggs=4, ham=5) # F522
|
||||
(''
|
||||
.format(x=2)) # F522
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18806
|
||||
# The fix here is unsafe because the unused argument has side effect
|
||||
"Hello, {name}".format(greeting=print(1), name="World")
|
||||
|
||||
# The fix here is safe because the unused argument has no side effect,
|
||||
# even though the used argument has a side effect
|
||||
"Hello, {name}".format(greeting="Pikachu", name=print(1))
|
||||
|
||||
@@ -35,3 +35,11 @@
|
||||
# Removing the final argument.
|
||||
"Hello".format("world")
|
||||
"Hello".format("world", key="value")
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18806
|
||||
# The fix here is unsafe because the unused argument has side effect
|
||||
"Hello, {0}".format("world", print(1))
|
||||
|
||||
# The fix here is safe because the unused argument has no side effect,
|
||||
# even though the used argument has a side effect
|
||||
"Hello, {0}".format(print(1), "Pikachu")
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# Mock objects
|
||||
# ============
|
||||
# Errors
|
||||
assert my_mock.not_called()
|
||||
assert my_mock.called_once_with()
|
||||
@@ -17,3 +19,25 @@ my_mock.assert_called()
|
||||
my_mock.assert_called_once_with()
|
||||
"""like :meth:`Mock.assert_called_once_with`"""
|
||||
"""like :meth:`MagicMock.assert_called_once_with`"""
|
||||
|
||||
# AsyncMock objects
|
||||
# =================
|
||||
# Errors
|
||||
assert my_mock.not_awaited()
|
||||
assert my_mock.awaited_once_with()
|
||||
assert my_mock.not_awaited
|
||||
assert my_mock.awaited_once_with
|
||||
my_mock.assert_not_awaited
|
||||
my_mock.assert_awaited
|
||||
my_mock.assert_awaited_once_with
|
||||
my_mock.assert_awaited_once_with
|
||||
MyMock.assert_awaited_once_with
|
||||
assert my_mock.awaited
|
||||
|
||||
# OK
|
||||
assert my_mock.await_count == 1
|
||||
my_mock.assert_not_awaited()
|
||||
my_mock.assert_awaited()
|
||||
my_mock.assert_awaited_once_with()
|
||||
"""like :meth:`Mock.assert_awaited_once_with`"""
|
||||
"""like :meth:`MagicMock.assert_awaited_once_with`"""
|
||||
|
||||
@@ -70,3 +70,9 @@ class D(C):
|
||||
|
||||
class E(A, C):
|
||||
...
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18814
|
||||
class Bar(Foo, # 1
|
||||
Foo # 2
|
||||
):
|
||||
pass
|
||||
|
||||
4
crates/ruff_linter/resources/test/fixtures/pylint/import_aliasing_2/__init__.py
vendored
Normal file
4
crates/ruff_linter/resources/test/fixtures/pylint/import_aliasing_2/__init__.py
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
import collections as collections
|
||||
from collections import OrderedDict as OrderedDict
|
||||
from . import foo as foo
|
||||
from .foo import bar as bar
|
||||
Binary file not shown.
@@ -168,7 +168,7 @@ def github_issue_1879():
|
||||
def function_returning_function(r):
|
||||
return function_returning_generator(r)
|
||||
|
||||
assert len(function_returning_list(z)) # [PLC1802] differs from pylint
|
||||
assert len(function_returning_list(z)) # [PLC1802] differs from pylint
|
||||
assert len(function_returning_int(z))
|
||||
# This should raise a PLC1802 once astroid can infer it
|
||||
# See https://github.com/pylint-dev/pylint/pull/3821#issuecomment-743771514
|
||||
@@ -196,7 +196,7 @@ def f(cond:bool):
|
||||
def g(cond:bool):
|
||||
x = [1,2,3]
|
||||
if cond:
|
||||
x = [4,5,6]
|
||||
x = [4,5,6]
|
||||
if len(x): # this should be addressed
|
||||
print(x)
|
||||
del x
|
||||
@@ -236,3 +236,15 @@ def j():
|
||||
# regression tests for https://github.com/astral-sh/ruff/issues/14690
|
||||
bool(len(ascii(1)))
|
||||
bool(len(sorted("")))
|
||||
|
||||
# regression tests for https://github.com/astral-sh/ruff/issues/18811
|
||||
fruits = []
|
||||
if(len)(fruits):
|
||||
...
|
||||
|
||||
# regression tests for https://github.com/astral-sh/ruff/issues/18812
|
||||
fruits = []
|
||||
if len(
|
||||
fruits # comment
|
||||
):
|
||||
...
|
||||
|
||||
@@ -92,3 +92,8 @@ if y == np.inf:
|
||||
# OK
|
||||
if x == "nan":
|
||||
pass
|
||||
|
||||
# PLW0117
|
||||
# https://github.com/astral-sh/ruff/issues/18596
|
||||
assert x == float("-NaN ")
|
||||
assert x == float(" \n+nan \t")
|
||||
|
||||
@@ -14,7 +14,7 @@ min(1, min(2, 3, key=test))
|
||||
# This will still trigger, to merge the calls without keyword args.
|
||||
min(1, min(2, 3, key=test), min(4, 5))
|
||||
|
||||
# Don't provide a fix if there are comments within the call.
|
||||
# The fix is already unsafe, so deleting comments is okay.
|
||||
min(
|
||||
1, # This is a comment.
|
||||
min(2, 3),
|
||||
@@ -55,3 +55,8 @@ max_word_len = max(
|
||||
*(len(word) for word in "blah blah blah".split(" ")),
|
||||
len("Done!"),
|
||||
)
|
||||
|
||||
|
||||
# Outer call has a single argument, inner call has multiple arguments; should not trigger.
|
||||
min(min([2, 3], [4, 1]))
|
||||
max(max([2, 4], [3, 1]))
|
||||
|
||||
@@ -129,3 +129,6 @@ blah = dict[{"a": 1}.__delitem__("a")] # OK
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/14597
|
||||
assert "abc".__str__() == "abc"
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18813
|
||||
three = 1 if 1 else(3.0).__str__()
|
||||
|
||||
@@ -71,3 +71,47 @@ if sys.version_info <= (3, 14, 0):
|
||||
|
||||
if sys.version_info <= (3, 14, 15):
|
||||
print()
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18165
|
||||
|
||||
if sys.version_info.major >= 3:
|
||||
print("3")
|
||||
else:
|
||||
print("2")
|
||||
|
||||
if sys.version_info.major > 3:
|
||||
print("3")
|
||||
else:
|
||||
print("2")
|
||||
|
||||
if sys.version_info.major <= 3:
|
||||
print("3")
|
||||
else:
|
||||
print("2")
|
||||
|
||||
if sys.version_info.major < 3:
|
||||
print("3")
|
||||
else:
|
||||
print("2")
|
||||
|
||||
if sys.version_info.major == 3:
|
||||
print("3")
|
||||
else:
|
||||
print("2")
|
||||
|
||||
# Semantically incorrect, skip fixing
|
||||
|
||||
if sys.version_info.major[1] > 3:
|
||||
print(3)
|
||||
else:
|
||||
print(2)
|
||||
|
||||
if sys.version_info.major > (3, 13):
|
||||
print(3)
|
||||
else:
|
||||
print(2)
|
||||
|
||||
if sys.version_info.major[:2] > (3, 13):
|
||||
print(3)
|
||||
else:
|
||||
print(2)
|
||||
|
||||
@@ -105,3 +105,12 @@ with open("furb129.py") as f:
|
||||
|
||||
# Test case for issue #17683 (missing space before keyword)
|
||||
print([line for line in f.readlines()if True])
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18843
|
||||
with open("file.txt") as fp:
|
||||
for line in ( # 1
|
||||
fp. # 3 # 2
|
||||
readlines( # 4
|
||||
) # 5
|
||||
):
|
||||
...
|
||||
|
||||
@@ -44,6 +44,13 @@ log(1, math.e)
|
||||
math.log(1, 2.0001)
|
||||
math.log(1, 10.0001)
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18747
|
||||
def log():
|
||||
yield math.log((yield), math.e)
|
||||
|
||||
|
||||
def log():
|
||||
yield math.log((yield from x), math.e)
|
||||
|
||||
# see: https://github.com/astral-sh/ruff/issues/18639
|
||||
math.log(1, 10 # comment
|
||||
|
||||
@@ -20,6 +20,12 @@ _ = Decimal.from_float(float("-inf"))
|
||||
_ = Decimal.from_float(float("Infinity"))
|
||||
_ = Decimal.from_float(float("-Infinity"))
|
||||
_ = Decimal.from_float(float("nan"))
|
||||
_ = Decimal.from_float(float("-NaN "))
|
||||
_ = Decimal.from_float(float(" \n+nan \t"))
|
||||
_ = Decimal.from_float(float(" iNf \n\t "))
|
||||
_ = Decimal.from_float(float(" -inF\n \t"))
|
||||
_ = Decimal.from_float(float(" InfinIty \n\t "))
|
||||
_ = Decimal.from_float(float(" -InfinIty\n \t"))
|
||||
|
||||
# OK
|
||||
_ = Fraction(0.1)
|
||||
|
||||
@@ -110,3 +110,19 @@ class ShouldMatchB008RuleOfImmutableTypeAnnotationIgnored:
|
||||
# ignored
|
||||
this_is_fine: int = f()
|
||||
|
||||
|
||||
# Test for:
|
||||
# https://github.com/astral-sh/ruff/issues/17424
|
||||
@dataclass(frozen=True)
|
||||
class C:
|
||||
foo: int = 1
|
||||
|
||||
|
||||
@dataclass
|
||||
class D:
|
||||
c: C = C()
|
||||
|
||||
|
||||
@dataclass
|
||||
class E:
|
||||
c: C = C()
|
||||
@@ -73,3 +73,55 @@ class IntConversionDescriptor:
|
||||
@frozen
|
||||
class InventoryItem:
|
||||
quantity_on_hand: IntConversionDescriptor = IntConversionDescriptor(default=100)
|
||||
|
||||
|
||||
# Test for:
|
||||
# https://github.com/astral-sh/ruff/issues/17424
|
||||
@frozen
|
||||
class C:
|
||||
foo: int = 1
|
||||
|
||||
|
||||
@attr.frozen
|
||||
class D:
|
||||
foo: int = 1
|
||||
|
||||
|
||||
@define
|
||||
class E:
|
||||
c: C = C()
|
||||
d: D = D()
|
||||
|
||||
|
||||
@attr.s
|
||||
class F:
|
||||
foo: int = 1
|
||||
|
||||
|
||||
@attr.mutable
|
||||
class G:
|
||||
foo: int = 1
|
||||
|
||||
|
||||
@attr.attrs
|
||||
class H:
|
||||
f: F = F()
|
||||
g: G = G()
|
||||
|
||||
|
||||
@attr.define
|
||||
class I:
|
||||
f: F = F()
|
||||
g: G = G()
|
||||
|
||||
|
||||
@attr.frozen
|
||||
class J:
|
||||
f: F = F()
|
||||
g: G = G()
|
||||
|
||||
|
||||
@attr.mutable
|
||||
class K:
|
||||
f: F = F()
|
||||
g: G = G()
|
||||
|
||||
@@ -36,5 +36,19 @@ f"{ascii(bla)}" # OK
|
||||
)
|
||||
|
||||
|
||||
# OK
|
||||
# https://github.com/astral-sh/ruff/issues/16325
|
||||
f"{str({})}"
|
||||
|
||||
f"{str({} | {})}"
|
||||
|
||||
import builtins
|
||||
|
||||
f"{builtins.repr(1)}"
|
||||
|
||||
f"{repr(1)=}"
|
||||
|
||||
f"{repr(lambda: 1)}"
|
||||
|
||||
f"{repr(x := 2)}"
|
||||
|
||||
f"{str(object=3)}"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from collections import deque
|
||||
import collections
|
||||
from collections import deque
|
||||
|
||||
|
||||
def f():
|
||||
@@ -91,3 +91,14 @@ def f():
|
||||
|
||||
def f():
|
||||
deque([], iterable=[])
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18854
|
||||
deque("")
|
||||
deque(b"")
|
||||
deque(f"")
|
||||
deque(f"" "")
|
||||
deque(f"" f"")
|
||||
deque("abc") # OK
|
||||
deque(b"abc") # OK
|
||||
deque(f"" "a") # OK
|
||||
deque(f"{x}" "") # OK
|
||||
|
||||
@@ -23,69 +23,69 @@ value2 = my_dict.get("key2", [1, 2, 3]).append(4)
|
||||
# Valid
|
||||
|
||||
# Using dict.get with a falsy fallback: False
|
||||
value = my_dict.get("key", False)
|
||||
value = my_dict.get("key", False)
|
||||
|
||||
# Using dict.get with a falsy fallback: empty string
|
||||
value = my_dict.get("key", "")
|
||||
value = my_dict.get("key", "")
|
||||
|
||||
# Using dict.get with a falsy fallback: empty list
|
||||
value = my_dict.get("key", [])
|
||||
value = my_dict.get("key", [])
|
||||
|
||||
# Using dict.get with a falsy fallback: empty dict
|
||||
value = my_dict.get("key", {})
|
||||
value = my_dict.get("key", {})
|
||||
|
||||
# Using dict.get with a falsy fallback: empty set
|
||||
value = my_dict.get("key", set())
|
||||
value = my_dict.get("key", set())
|
||||
|
||||
# Using dict.get with a falsy fallback: zero integer
|
||||
value = my_dict.get("key", 0)
|
||||
value = my_dict.get("key", 0)
|
||||
|
||||
# Using dict.get with a falsy fallback: zero float
|
||||
value = my_dict.get("key", 0.0)
|
||||
value = my_dict.get("key", 0.0)
|
||||
|
||||
# Using dict.get with a falsy fallback: None
|
||||
value = my_dict.get("key", None)
|
||||
value = my_dict.get("key", None)
|
||||
|
||||
# Using dict.get with a falsy fallback via function call
|
||||
value = my_dict.get("key", list())
|
||||
value = my_dict.get("key", dict())
|
||||
value = my_dict.get("key", set())
|
||||
value = my_dict.get("key", list())
|
||||
value = my_dict.get("key", dict())
|
||||
value = my_dict.get("key", set())
|
||||
|
||||
# Reassigning with falsy fallback
|
||||
def get_value(d):
|
||||
return d.get("key", False)
|
||||
return d.get("key", False)
|
||||
|
||||
# Multiple dict.get calls with mixed fallbacks
|
||||
value1 = my_dict.get("key1", "default")
|
||||
value2 = my_dict.get("key2", 0)
|
||||
value2 = my_dict.get("key2", 0)
|
||||
value3 = my_dict.get("key3", "another default")
|
||||
|
||||
# Using dict.get in a class with falsy fallback
|
||||
class MyClass:
|
||||
def method(self):
|
||||
return self.data.get("key", {})
|
||||
return self.data.get("key", {})
|
||||
|
||||
# Using dict.get in a nested function with falsy fallback
|
||||
def outer():
|
||||
def inner():
|
||||
return my_dict.get("key", "")
|
||||
return my_dict.get("key", "")
|
||||
return inner()
|
||||
|
||||
# Using dict.get with variable fallback that is falsy
|
||||
falsy_value = None
|
||||
value = my_dict.get("key", falsy_value)
|
||||
value = my_dict.get("key", falsy_value)
|
||||
|
||||
# Using dict.get with variable fallback that is truthy
|
||||
truthy_value = "exists"
|
||||
value = my_dict.get("key", truthy_value)
|
||||
|
||||
# Using dict.get with complex expressions as fallback
|
||||
value = my_dict.get("key", 0 or "default")
|
||||
value = my_dict.get("key", [] if condition else {})
|
||||
value = my_dict.get("key", 0 or "default")
|
||||
value = my_dict.get("key", [] if condition else {})
|
||||
|
||||
# testing dict.get call using kwargs
|
||||
value = my_dict.get(key="key", default=False)
|
||||
value = my_dict.get(default=[], key="key")
|
||||
value = my_dict.get(key="key", default=False)
|
||||
value = my_dict.get(default=[], key="key")
|
||||
|
||||
|
||||
# Edge Cases
|
||||
@@ -93,16 +93,16 @@ value = my_dict.get(default=[], key="key")
|
||||
dicts = [my_dict, my_dict, my_dict]
|
||||
|
||||
# Falsy fallback in a lambda
|
||||
get_fallback = lambda d: d.get("key", False)
|
||||
get_fallback = lambda d: d.get("key", False)
|
||||
|
||||
# Falsy fallback in a list comprehension
|
||||
results = [d.get("key", "") for d in dicts]
|
||||
results = [d.get("key", "") for d in dicts]
|
||||
|
||||
# Falsy fallback in a generator expression
|
||||
results = (d.get("key", None) for d in dicts)
|
||||
results = (d.get("key", None) for d in dicts)
|
||||
|
||||
# Falsy fallback in a ternary expression
|
||||
value = my_dict.get("key", 0) if True else "default"
|
||||
value = my_dict.get("key", 0) if True else "default"
|
||||
|
||||
|
||||
# Falsy fallback with inline comment
|
||||
@@ -185,3 +185,7 @@ not my_dict.get(
|
||||
# TypeError is raised at runtime before and after the fix, but we still bail
|
||||
# out for having an unrecognized number of arguments
|
||||
not my_dict.get("key", False, foo=...)
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18798
|
||||
d = {}
|
||||
not d.get("key", (False))
|
||||
|
||||
18
crates/ruff_linter/resources/test/fixtures/ruff/RUF063.py
vendored
Normal file
18
crates/ruff_linter/resources/test/fixtures/ruff/RUF063.py
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
# RUF063
|
||||
# Cases that should trigger the violation
|
||||
|
||||
foo.__dict__.get("__annotations__") # RUF063
|
||||
foo.__dict__.get("__annotations__", None) # RUF063
|
||||
foo.__dict__.get("__annotations__", {}) # RUF063
|
||||
foo.__dict__["__annotations__"] # RUF063
|
||||
|
||||
# Cases that should NOT trigger the violation
|
||||
|
||||
foo.__dict__.get("not__annotations__")
|
||||
foo.__dict__.get("not__annotations__", None)
|
||||
foo.__dict__.get("not__annotations__", {})
|
||||
foo.__dict__["not__annotations__"]
|
||||
foo.__annotations__
|
||||
foo.get("__annotations__")
|
||||
foo.get("__annotations__", None)
|
||||
foo.get("__annotations__", {})
|
||||
53
crates/ruff_linter/resources/test/fixtures/ruff/RUF064.py
vendored
Normal file
53
crates/ruff_linter/resources/test/fixtures/ruff/RUF064.py
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
import dbm.gnu
|
||||
import dbm.ndbm
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
os.chmod("foo", 444) # Error
|
||||
os.chmod("foo", 0o444) # OK
|
||||
os.chmod("foo", 7777) # Error
|
||||
os.chmod("foo", 10000) # Error
|
||||
os.chmod("foo", 99999) # Error
|
||||
|
||||
os.umask(777) # Error
|
||||
os.umask(0o777) # OK
|
||||
|
||||
os.fchmod(0, 400) # Error
|
||||
os.fchmod(0, 0o400) # OK
|
||||
|
||||
os.lchmod("foo", 755) # Error
|
||||
os.lchmod("foo", 0o755) # OK
|
||||
|
||||
os.mkdir("foo", 600) # Error
|
||||
os.mkdir("foo", 0o600) # OK
|
||||
|
||||
os.makedirs("foo", 644) # Error
|
||||
os.makedirs("foo", 0o644) # OK
|
||||
|
||||
os.mkfifo("foo", 640) # Error
|
||||
os.mkfifo("foo", 0o640) # OK
|
||||
|
||||
os.mknod("foo", 660) # Error
|
||||
os.mknod("foo", 0o660) # OK
|
||||
|
||||
os.open("foo", os.O_CREAT, 644) # Error
|
||||
os.open("foo", os.O_CREAT, 0o644) # OK
|
||||
|
||||
Path("bar").chmod(755) # Error
|
||||
Path("bar").chmod(0o755) # OK
|
||||
|
||||
path = Path("bar")
|
||||
path.chmod(755) # Error
|
||||
path.chmod(0o755) # OK
|
||||
|
||||
dbm.open("db", "r", 600) # Error
|
||||
dbm.open("db", "r", 0o600) # OK
|
||||
|
||||
dbm.gnu.open("db", "r", 600) # Error
|
||||
dbm.gnu.open("db", "r", 0o600) # OK
|
||||
|
||||
dbm.ndbm.open("db", "r", 600) # Error
|
||||
dbm.ndbm.open("db", "r", 0o600) # OK
|
||||
|
||||
os.fchmod(0, 256) # 0o400
|
||||
os.fchmod(0, 493) # 0o755
|
||||
@@ -10,7 +10,7 @@ use crate::rules::{
|
||||
|
||||
/// Run lint rules over the [`Binding`]s.
|
||||
pub(crate) fn bindings(checker: &Checker) {
|
||||
if !checker.any_enabled(&[
|
||||
if !checker.any_rule_enabled(&[
|
||||
Rule::AssignmentInAssert,
|
||||
Rule::InvalidAllFormat,
|
||||
Rule::InvalidAllObject,
|
||||
@@ -30,11 +30,11 @@ pub(crate) fn bindings(checker: &Checker) {
|
||||
}
|
||||
|
||||
for (binding_id, binding) in checker.semantic.bindings.iter_enumerated() {
|
||||
if checker.enabled(Rule::UnusedVariable) {
|
||||
if checker.is_rule_enabled(Rule::UnusedVariable) {
|
||||
if binding.kind.is_bound_exception()
|
||||
&& binding.is_unused()
|
||||
&& !checker
|
||||
.settings
|
||||
.settings()
|
||||
.dummy_variable_rgx
|
||||
.is_match(binding.name(checker.source()))
|
||||
{
|
||||
@@ -54,47 +54,47 @@ pub(crate) fn bindings(checker: &Checker) {
|
||||
});
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::InvalidAllFormat) {
|
||||
if checker.is_rule_enabled(Rule::InvalidAllFormat) {
|
||||
pylint::rules::invalid_all_format(checker, binding);
|
||||
}
|
||||
if checker.enabled(Rule::InvalidAllObject) {
|
||||
if checker.is_rule_enabled(Rule::InvalidAllObject) {
|
||||
pylint::rules::invalid_all_object(checker, binding);
|
||||
}
|
||||
if checker.enabled(Rule::NonAsciiName) {
|
||||
if checker.is_rule_enabled(Rule::NonAsciiName) {
|
||||
pylint::rules::non_ascii_name(checker, binding);
|
||||
}
|
||||
if checker.enabled(Rule::UnconventionalImportAlias) {
|
||||
if checker.is_rule_enabled(Rule::UnconventionalImportAlias) {
|
||||
flake8_import_conventions::rules::unconventional_import_alias(
|
||||
checker,
|
||||
binding,
|
||||
&checker.settings.flake8_import_conventions.aliases,
|
||||
&checker.settings().flake8_import_conventions.aliases,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::UnaliasedCollectionsAbcSetImport) {
|
||||
if checker.is_rule_enabled(Rule::UnaliasedCollectionsAbcSetImport) {
|
||||
flake8_pyi::rules::unaliased_collections_abc_set_import(checker, binding);
|
||||
}
|
||||
if !checker.source_type.is_stub() && checker.enabled(Rule::UnquotedTypeAlias) {
|
||||
if !checker.source_type.is_stub() && checker.is_rule_enabled(Rule::UnquotedTypeAlias) {
|
||||
flake8_type_checking::rules::unquoted_type_alias(checker, binding);
|
||||
}
|
||||
if checker.enabled(Rule::UnsortedDunderSlots) {
|
||||
if checker.is_rule_enabled(Rule::UnsortedDunderSlots) {
|
||||
ruff::rules::sort_dunder_slots(checker, binding);
|
||||
}
|
||||
if checker.enabled(Rule::UsedDummyVariable) {
|
||||
if checker.is_rule_enabled(Rule::UsedDummyVariable) {
|
||||
ruff::rules::used_dummy_variable(checker, binding, binding_id);
|
||||
}
|
||||
if checker.enabled(Rule::AssignmentInAssert) {
|
||||
if checker.is_rule_enabled(Rule::AssignmentInAssert) {
|
||||
ruff::rules::assignment_in_assert(checker, binding);
|
||||
}
|
||||
if checker.enabled(Rule::PytestUnittestRaisesAssertion) {
|
||||
if checker.is_rule_enabled(Rule::PytestUnittestRaisesAssertion) {
|
||||
flake8_pytest_style::rules::unittest_raises_assertion_binding(checker, binding);
|
||||
}
|
||||
if checker.enabled(Rule::ForLoopWrites) {
|
||||
if checker.is_rule_enabled(Rule::ForLoopWrites) {
|
||||
refurb::rules::for_loop_writes_binding(checker, binding);
|
||||
}
|
||||
if checker.enabled(Rule::CustomTypeVarForSelf) {
|
||||
if checker.is_rule_enabled(Rule::CustomTypeVarForSelf) {
|
||||
flake8_pyi::rules::custom_type_var_instead_of_self(checker, binding);
|
||||
}
|
||||
if checker.enabled(Rule::PrivateTypeParameter) {
|
||||
if checker.is_rule_enabled(Rule::PrivateTypeParameter) {
|
||||
pyupgrade::rules::private_type_parameter(checker, binding);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@ use crate::rules::{flake8_simplify, refurb};
|
||||
|
||||
/// Run lint rules over a [`Comprehension`] syntax nodes.
|
||||
pub(crate) fn comprehension(comprehension: &Comprehension, checker: &Checker) {
|
||||
if checker.enabled(Rule::InDictKeys) {
|
||||
if checker.is_rule_enabled(Rule::InDictKeys) {
|
||||
flake8_simplify::rules::key_in_dict_comprehension(checker, comprehension);
|
||||
}
|
||||
if checker.enabled(Rule::ReadlinesInFor) {
|
||||
if checker.is_rule_enabled(Rule::ReadlinesInFor) {
|
||||
refurb::rules::readlines_in_comprehension(checker, comprehension);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,31 +14,31 @@ pub(crate) fn deferred_for_loops(checker: &mut Checker) {
|
||||
let Stmt::For(stmt_for) = checker.semantic.current_statement() else {
|
||||
unreachable!("Expected Stmt::For");
|
||||
};
|
||||
if checker.enabled(Rule::UnusedLoopControlVariable) {
|
||||
if checker.is_rule_enabled(Rule::UnusedLoopControlVariable) {
|
||||
flake8_bugbear::rules::unused_loop_control_variable(checker, stmt_for);
|
||||
}
|
||||
if checker.enabled(Rule::IncorrectDictIterator) {
|
||||
if checker.is_rule_enabled(Rule::IncorrectDictIterator) {
|
||||
perflint::rules::incorrect_dict_iterator(checker, stmt_for);
|
||||
}
|
||||
if checker.enabled(Rule::YieldInForLoop) {
|
||||
if checker.is_rule_enabled(Rule::YieldInForLoop) {
|
||||
pyupgrade::rules::yield_in_for_loop(checker, stmt_for);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryEnumerate) {
|
||||
if checker.is_rule_enabled(Rule::UnnecessaryEnumerate) {
|
||||
refurb::rules::unnecessary_enumerate(checker, stmt_for);
|
||||
}
|
||||
if checker.enabled(Rule::EnumerateForLoop) {
|
||||
if checker.is_rule_enabled(Rule::EnumerateForLoop) {
|
||||
flake8_simplify::rules::enumerate_for_loop(checker, stmt_for);
|
||||
}
|
||||
if checker.enabled(Rule::LoopIteratorMutation) {
|
||||
if checker.is_rule_enabled(Rule::LoopIteratorMutation) {
|
||||
flake8_bugbear::rules::loop_iterator_mutation(checker, stmt_for);
|
||||
}
|
||||
if checker.enabled(Rule::DictIndexMissingItems) {
|
||||
if checker.is_rule_enabled(Rule::DictIndexMissingItems) {
|
||||
pylint::rules::dict_index_missing_items(checker, stmt_for);
|
||||
}
|
||||
if checker.enabled(Rule::ManualDictComprehension) {
|
||||
if checker.is_rule_enabled(Rule::ManualDictComprehension) {
|
||||
perflint::rules::manual_dict_comprehension(checker, stmt_for);
|
||||
}
|
||||
if checker.enabled(Rule::ManualListComprehension) {
|
||||
if checker.is_rule_enabled(Rule::ManualListComprehension) {
|
||||
perflint::rules::manual_list_comprehension(checker, stmt_for);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,13 +15,13 @@ pub(crate) fn deferred_lambdas(checker: &mut Checker) {
|
||||
unreachable!("Expected Expr::Lambda");
|
||||
};
|
||||
|
||||
if checker.enabled(Rule::UnnecessaryLambda) {
|
||||
if checker.is_rule_enabled(Rule::UnnecessaryLambda) {
|
||||
pylint::rules::unnecessary_lambda(checker, lambda);
|
||||
}
|
||||
if checker.enabled(Rule::ReimplementedContainerBuiltin) {
|
||||
if checker.is_rule_enabled(Rule::ReimplementedContainerBuiltin) {
|
||||
flake8_pie::rules::reimplemented_container_builtin(checker, lambda);
|
||||
}
|
||||
if checker.enabled(Rule::BuiltinLambdaArgumentShadowing) {
|
||||
if checker.is_rule_enabled(Rule::BuiltinLambdaArgumentShadowing) {
|
||||
flake8_builtins::rules::builtin_lambda_argument_shadowing(checker, lambda);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
use ruff_python_semantic::analyze::visibility;
|
||||
use ruff_python_semantic::{Binding, BindingKind, Imported, ResolvedReference, ScopeKind};
|
||||
use ruff_text_size::Ranged;
|
||||
use rustc_hash::FxHashMap;
|
||||
use ruff_python_semantic::{Binding, ScopeKind};
|
||||
|
||||
use crate::Fix;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::codes::Rule;
|
||||
use crate::fix;
|
||||
use crate::rules::{
|
||||
flake8_builtins, flake8_pyi, flake8_type_checking, flake8_unused_arguments, pep8_naming,
|
||||
pyflakes, pylint, ruff,
|
||||
@@ -14,7 +9,7 @@ use crate::rules::{
|
||||
|
||||
/// Run lint rules over all deferred scopes in the [`SemanticModel`].
|
||||
pub(crate) fn deferred_scopes(checker: &Checker) {
|
||||
if !checker.any_enabled(&[
|
||||
if !checker.any_rule_enabled(&[
|
||||
Rule::AsyncioDanglingTask,
|
||||
Rule::BadStaticmethodArgument,
|
||||
Rule::BuiltinAttributeShadowing,
|
||||
@@ -58,7 +53,7 @@ pub(crate) fn deferred_scopes(checker: &Checker) {
|
||||
// used at runtime, then by default, we avoid flagging any other
|
||||
// imports from that model as typing-only.
|
||||
let enforce_typing_only_imports = !checker.source_type.is_stub()
|
||||
&& checker.any_enabled(&[
|
||||
&& checker.any_rule_enabled(&[
|
||||
Rule::TypingOnlyFirstPartyImport,
|
||||
Rule::TypingOnlyStandardLibraryImport,
|
||||
Rule::TypingOnlyThirdPartyImport,
|
||||
@@ -76,7 +71,7 @@ pub(crate) fn deferred_scopes(checker: &Checker) {
|
||||
flake8_type_checking::helpers::is_valid_runtime_import(
|
||||
binding,
|
||||
&checker.semantic,
|
||||
&checker.settings.flake8_type_checking,
|
||||
&checker.settings().flake8_type_checking,
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
@@ -89,307 +84,66 @@ pub(crate) fn deferred_scopes(checker: &Checker) {
|
||||
for scope_id in checker.analyze.scopes.iter().rev().copied() {
|
||||
let scope = &checker.semantic.scopes[scope_id];
|
||||
|
||||
if checker.enabled(Rule::UndefinedLocal) {
|
||||
if checker.is_rule_enabled(Rule::UndefinedLocal) {
|
||||
pyflakes::rules::undefined_local(checker, scope_id, scope);
|
||||
}
|
||||
|
||||
if checker.enabled(Rule::GlobalVariableNotAssigned) {
|
||||
for (name, binding_id) in scope.bindings() {
|
||||
let binding = checker.semantic.binding(binding_id);
|
||||
// If the binding is a `global`, then it's a top-level `global` that was never
|
||||
// assigned in the current scope. If it were assigned, the `global` would be
|
||||
// shadowed by the assignment.
|
||||
if binding.kind.is_global() {
|
||||
// If the binding was conditionally deleted, it will include a reference within
|
||||
// a `Del` context, but won't be shadowed by a `BindingKind::Deletion`, as in:
|
||||
// ```python
|
||||
// if condition:
|
||||
// del var
|
||||
// ```
|
||||
if binding
|
||||
.references
|
||||
.iter()
|
||||
.map(|id| checker.semantic.reference(*id))
|
||||
.all(ResolvedReference::is_load)
|
||||
{
|
||||
checker.report_diagnostic(
|
||||
pylint::rules::GlobalVariableNotAssigned {
|
||||
name: (*name).to_string(),
|
||||
},
|
||||
binding.range(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::GlobalVariableNotAssigned) {
|
||||
pylint::rules::global_variable_not_assigned(checker, scope);
|
||||
}
|
||||
|
||||
if checker.enabled(Rule::RedefinedArgumentFromLocal) {
|
||||
for (name, binding_id) in scope.bindings() {
|
||||
for shadow in checker.semantic.shadowed_bindings(scope_id, binding_id) {
|
||||
let binding = &checker.semantic.bindings[shadow.binding_id()];
|
||||
if !matches!(
|
||||
binding.kind,
|
||||
BindingKind::LoopVar
|
||||
| BindingKind::BoundException
|
||||
| BindingKind::WithItemVar
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
let shadowed = &checker.semantic.bindings[shadow.shadowed_id()];
|
||||
if !shadowed.kind.is_argument() {
|
||||
continue;
|
||||
}
|
||||
if checker.settings.dummy_variable_rgx.is_match(name) {
|
||||
continue;
|
||||
}
|
||||
let scope = &checker.semantic.scopes[binding.scope];
|
||||
if scope.kind.is_generator() {
|
||||
continue;
|
||||
}
|
||||
checker.report_diagnostic(
|
||||
pylint::rules::RedefinedArgumentFromLocal {
|
||||
name: name.to_string(),
|
||||
},
|
||||
binding.range(),
|
||||
);
|
||||
}
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::RedefinedArgumentFromLocal) {
|
||||
pylint::rules::redefined_argument_from_local(checker, scope_id, scope);
|
||||
}
|
||||
|
||||
if checker.enabled(Rule::ImportShadowedByLoopVar) {
|
||||
for (name, binding_id) in scope.bindings() {
|
||||
for shadow in checker.semantic.shadowed_bindings(scope_id, binding_id) {
|
||||
// If the shadowing binding isn't a loop variable, abort.
|
||||
let binding = &checker.semantic.bindings[shadow.binding_id()];
|
||||
if !binding.kind.is_loop_var() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the shadowed binding isn't an import, abort.
|
||||
let shadowed = &checker.semantic.bindings[shadow.shadowed_id()];
|
||||
if !matches!(
|
||||
shadowed.kind,
|
||||
BindingKind::Import(..)
|
||||
| BindingKind::FromImport(..)
|
||||
| BindingKind::SubmoduleImport(..)
|
||||
| BindingKind::FutureImport
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the bindings are in different forks, abort.
|
||||
if shadowed.source.is_none_or(|left| {
|
||||
binding
|
||||
.source
|
||||
.is_none_or(|right| !checker.semantic.same_branch(left, right))
|
||||
}) {
|
||||
continue;
|
||||
}
|
||||
|
||||
checker.report_diagnostic(
|
||||
pyflakes::rules::ImportShadowedByLoopVar {
|
||||
name: name.to_string(),
|
||||
row: checker.compute_source_row(shadowed.start()),
|
||||
},
|
||||
binding.range(),
|
||||
);
|
||||
}
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::ImportShadowedByLoopVar) {
|
||||
pyflakes::rules::import_shadowed_by_loop_var(checker, scope_id, scope);
|
||||
}
|
||||
|
||||
if checker.enabled(Rule::RedefinedWhileUnused) {
|
||||
// Index the redefined bindings by statement.
|
||||
let mut redefinitions = FxHashMap::default();
|
||||
|
||||
for (name, binding_id) in scope.bindings() {
|
||||
for shadow in checker.semantic.shadowed_bindings(scope_id, binding_id) {
|
||||
// If the shadowing binding is a loop variable, abort, to avoid overlap
|
||||
// with F402.
|
||||
let binding = &checker.semantic.bindings[shadow.binding_id()];
|
||||
if binding.kind.is_loop_var() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the shadowed binding is used, abort.
|
||||
let shadowed = &checker.semantic.bindings[shadow.shadowed_id()];
|
||||
if shadowed.is_used() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the shadowing binding isn't considered a "redefinition" of the
|
||||
// shadowed binding, abort.
|
||||
if !binding.redefines(shadowed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if shadow.same_scope() {
|
||||
// If the symbol is a dummy variable, abort, unless the shadowed
|
||||
// binding is an import.
|
||||
if !matches!(
|
||||
shadowed.kind,
|
||||
BindingKind::Import(..)
|
||||
| BindingKind::FromImport(..)
|
||||
| BindingKind::SubmoduleImport(..)
|
||||
| BindingKind::FutureImport
|
||||
) && checker.settings.dummy_variable_rgx.is_match(name)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(node_id) = shadowed.source else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// If this is an overloaded function, abort.
|
||||
if shadowed.kind.is_function_definition() {
|
||||
if checker
|
||||
.semantic
|
||||
.statement(node_id)
|
||||
.as_function_def_stmt()
|
||||
.is_some_and(|function| {
|
||||
visibility::is_overload(
|
||||
&function.decorator_list,
|
||||
&checker.semantic,
|
||||
)
|
||||
})
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Only enforce cross-scope shadowing for imports.
|
||||
if !matches!(
|
||||
shadowed.kind,
|
||||
BindingKind::Import(..)
|
||||
| BindingKind::FromImport(..)
|
||||
| BindingKind::SubmoduleImport(..)
|
||||
| BindingKind::FutureImport
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// If the bindings are in different forks, abort.
|
||||
if shadowed.source.is_none_or(|left| {
|
||||
binding
|
||||
.source
|
||||
.is_none_or(|right| !checker.semantic.same_branch(left, right))
|
||||
}) {
|
||||
continue;
|
||||
}
|
||||
|
||||
redefinitions
|
||||
.entry(binding.source)
|
||||
.or_insert_with(Vec::new)
|
||||
.push((shadowed, binding));
|
||||
}
|
||||
}
|
||||
|
||||
// Create a fix for each source statement.
|
||||
let mut fixes = FxHashMap::default();
|
||||
for (source, entries) in &redefinitions {
|
||||
let Some(source) = source else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let member_names = entries
|
||||
.iter()
|
||||
.filter_map(|(shadowed, binding)| {
|
||||
if let Some(shadowed_import) = shadowed.as_any_import() {
|
||||
if let Some(import) = binding.as_any_import() {
|
||||
if shadowed_import.qualified_name() == import.qualified_name() {
|
||||
return Some(import.member_name());
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if !member_names.is_empty() {
|
||||
let statement = checker.semantic.statement(*source);
|
||||
let parent = checker.semantic.parent_statement(*source);
|
||||
let Ok(edit) = fix::edits::remove_unused_imports(
|
||||
member_names.iter().map(std::convert::AsRef::as_ref),
|
||||
statement,
|
||||
parent,
|
||||
checker.locator(),
|
||||
checker.stylist(),
|
||||
checker.indexer(),
|
||||
) else {
|
||||
continue;
|
||||
};
|
||||
fixes.insert(
|
||||
*source,
|
||||
Fix::safe_edit(edit).isolate(Checker::isolation(
|
||||
checker.semantic().parent_statement_id(*source),
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Create diagnostics for each statement.
|
||||
for (source, entries) in &redefinitions {
|
||||
for (shadowed, binding) in entries {
|
||||
let mut diagnostic = checker.report_diagnostic(
|
||||
pyflakes::rules::RedefinedWhileUnused {
|
||||
name: binding.name(checker.source()).to_string(),
|
||||
row: checker.compute_source_row(shadowed.start()),
|
||||
},
|
||||
binding.range(),
|
||||
);
|
||||
|
||||
if let Some(range) = binding.parent_range(&checker.semantic) {
|
||||
diagnostic.set_parent(range.start());
|
||||
}
|
||||
|
||||
if let Some(fix) = source.as_ref().and_then(|source| fixes.get(source)) {
|
||||
diagnostic.set_fix(fix.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::RedefinedWhileUnused) {
|
||||
pyflakes::rules::redefined_while_unused(checker, scope_id, scope);
|
||||
}
|
||||
|
||||
if checker.source_type.is_stub()
|
||||
|| matches!(scope.kind, ScopeKind::Module | ScopeKind::Function(_))
|
||||
{
|
||||
if checker.enabled(Rule::UnusedPrivateTypeVar) {
|
||||
if checker.is_rule_enabled(Rule::UnusedPrivateTypeVar) {
|
||||
flake8_pyi::rules::unused_private_type_var(checker, scope);
|
||||
}
|
||||
if checker.enabled(Rule::UnusedPrivateProtocol) {
|
||||
if checker.is_rule_enabled(Rule::UnusedPrivateProtocol) {
|
||||
flake8_pyi::rules::unused_private_protocol(checker, scope);
|
||||
}
|
||||
if checker.enabled(Rule::UnusedPrivateTypeAlias) {
|
||||
if checker.is_rule_enabled(Rule::UnusedPrivateTypeAlias) {
|
||||
flake8_pyi::rules::unused_private_type_alias(checker, scope);
|
||||
}
|
||||
if checker.enabled(Rule::UnusedPrivateTypedDict) {
|
||||
if checker.is_rule_enabled(Rule::UnusedPrivateTypedDict) {
|
||||
flake8_pyi::rules::unused_private_typed_dict(checker, scope);
|
||||
}
|
||||
}
|
||||
|
||||
if checker.enabled(Rule::AsyncioDanglingTask) {
|
||||
if checker.is_rule_enabled(Rule::AsyncioDanglingTask) {
|
||||
ruff::rules::asyncio_dangling_binding(scope, checker);
|
||||
}
|
||||
|
||||
if let Some(class_def) = scope.kind.as_class() {
|
||||
if checker.enabled(Rule::BuiltinAttributeShadowing) {
|
||||
if checker.is_rule_enabled(Rule::BuiltinAttributeShadowing) {
|
||||
flake8_builtins::rules::builtin_attribute_shadowing(
|
||||
checker, scope_id, scope, class_def,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::FunctionCallInDataclassDefaultArgument) {
|
||||
if checker.is_rule_enabled(Rule::FunctionCallInDataclassDefaultArgument) {
|
||||
ruff::rules::function_call_in_dataclass_default(checker, class_def);
|
||||
}
|
||||
if checker.enabled(Rule::MutableClassDefault) {
|
||||
if checker.is_rule_enabled(Rule::MutableClassDefault) {
|
||||
ruff::rules::mutable_class_default(checker, class_def);
|
||||
}
|
||||
if checker.enabled(Rule::MutableDataclassDefault) {
|
||||
if checker.is_rule_enabled(Rule::MutableDataclassDefault) {
|
||||
ruff::rules::mutable_dataclass_default(checker, class_def);
|
||||
}
|
||||
}
|
||||
|
||||
if matches!(scope.kind, ScopeKind::Function(_) | ScopeKind::Lambda(_)) {
|
||||
if checker.any_enabled(&[Rule::UnusedVariable, Rule::UnusedUnpackedVariable])
|
||||
if checker.any_rule_enabled(&[Rule::UnusedVariable, Rule::UnusedUnpackedVariable])
|
||||
&& !(scope.uses_locals() && scope.kind.is_function())
|
||||
{
|
||||
let unused_bindings = scope
|
||||
@@ -402,7 +156,7 @@ pub(crate) fn deferred_scopes(checker: &Checker) {
|
||||
&& binding.is_unused()
|
||||
&& !binding.is_nonlocal()
|
||||
&& !binding.is_global()
|
||||
&& !checker.settings.dummy_variable_rgx.is_match(name)
|
||||
&& !checker.settings().dummy_variable_rgx.is_match(name)
|
||||
&& !matches!(
|
||||
name,
|
||||
"__tracebackhide__"
|
||||
@@ -418,22 +172,22 @@ pub(crate) fn deferred_scopes(checker: &Checker) {
|
||||
});
|
||||
|
||||
for (unused_name, unused_binding) in unused_bindings {
|
||||
if checker.enabled(Rule::UnusedVariable) {
|
||||
if checker.is_rule_enabled(Rule::UnusedVariable) {
|
||||
pyflakes::rules::unused_variable(checker, unused_name, unused_binding);
|
||||
}
|
||||
|
||||
if checker.enabled(Rule::UnusedUnpackedVariable) {
|
||||
if checker.is_rule_enabled(Rule::UnusedUnpackedVariable) {
|
||||
ruff::rules::unused_unpacked_variable(checker, unused_name, unused_binding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if checker.enabled(Rule::UnusedAnnotation) {
|
||||
if checker.is_rule_enabled(Rule::UnusedAnnotation) {
|
||||
pyflakes::rules::unused_annotation(checker, scope);
|
||||
}
|
||||
|
||||
if !checker.source_type.is_stub() {
|
||||
if checker.any_enabled(&[
|
||||
if checker.any_rule_enabled(&[
|
||||
Rule::UnusedClassMethodArgument,
|
||||
Rule::UnusedFunctionArgument,
|
||||
Rule::UnusedLambdaArgument,
|
||||
@@ -447,7 +201,7 @@ pub(crate) fn deferred_scopes(checker: &Checker) {
|
||||
|
||||
if matches!(scope.kind, ScopeKind::Function(_) | ScopeKind::Module) {
|
||||
if !checker.source_type.is_stub()
|
||||
&& checker.enabled(Rule::RuntimeImportInTypeCheckingBlock)
|
||||
&& checker.is_rule_enabled(Rule::RuntimeImportInTypeCheckingBlock)
|
||||
{
|
||||
flake8_type_checking::rules::runtime_import_in_type_checking_block(checker, scope);
|
||||
}
|
||||
@@ -467,37 +221,37 @@ pub(crate) fn deferred_scopes(checker: &Checker) {
|
||||
);
|
||||
}
|
||||
|
||||
if checker.enabled(Rule::UnusedImport) {
|
||||
if checker.is_rule_enabled(Rule::UnusedImport) {
|
||||
pyflakes::rules::unused_import(checker, scope);
|
||||
}
|
||||
|
||||
if checker.enabled(Rule::ImportPrivateName) {
|
||||
if checker.is_rule_enabled(Rule::ImportPrivateName) {
|
||||
pylint::rules::import_private_name(checker, scope);
|
||||
}
|
||||
}
|
||||
|
||||
if scope.kind.is_function() {
|
||||
if checker.enabled(Rule::NoSelfUse) {
|
||||
if checker.is_rule_enabled(Rule::NoSelfUse) {
|
||||
pylint::rules::no_self_use(checker, scope_id, scope);
|
||||
}
|
||||
|
||||
if checker.enabled(Rule::TooManyLocals) {
|
||||
if checker.is_rule_enabled(Rule::TooManyLocals) {
|
||||
pylint::rules::too_many_locals(checker, scope);
|
||||
}
|
||||
|
||||
if checker.enabled(Rule::SingledispatchMethod) {
|
||||
if checker.is_rule_enabled(Rule::SingledispatchMethod) {
|
||||
pylint::rules::singledispatch_method(checker, scope);
|
||||
}
|
||||
|
||||
if checker.enabled(Rule::SingledispatchmethodFunction) {
|
||||
if checker.is_rule_enabled(Rule::SingledispatchmethodFunction) {
|
||||
pylint::rules::singledispatchmethod_function(checker, scope);
|
||||
}
|
||||
|
||||
if checker.enabled(Rule::BadStaticmethodArgument) {
|
||||
if checker.is_rule_enabled(Rule::BadStaticmethodArgument) {
|
||||
pylint::rules::bad_staticmethod_argument(checker, scope);
|
||||
}
|
||||
|
||||
if checker.any_enabled(&[
|
||||
if checker.any_rule_enabled(&[
|
||||
Rule::InvalidFirstArgumentNameForClassMethod,
|
||||
Rule::InvalidFirstArgumentNameForMethod,
|
||||
]) {
|
||||
|
||||
@@ -17,7 +17,7 @@ use crate::{docstrings, warn_user};
|
||||
/// it is expected that all [`Definition`] nodes have been visited by the time, and that this
|
||||
/// method will not recurse into any other nodes.
|
||||
pub(crate) fn definitions(checker: &mut Checker) {
|
||||
let enforce_annotations = checker.any_enabled(&[
|
||||
let enforce_annotations = checker.any_rule_enabled(&[
|
||||
Rule::AnyType,
|
||||
Rule::MissingReturnTypeClassMethod,
|
||||
Rule::MissingReturnTypePrivateFunction,
|
||||
@@ -28,10 +28,11 @@ pub(crate) fn definitions(checker: &mut Checker) {
|
||||
Rule::MissingTypeFunctionArgument,
|
||||
Rule::MissingTypeKwargs,
|
||||
]);
|
||||
let enforce_stubs = checker.source_type.is_stub() && checker.enabled(Rule::DocstringInStub);
|
||||
let enforce_stubs_and_runtime = checker.enabled(Rule::IterMethodReturnIterable);
|
||||
let enforce_dunder_method = checker.enabled(Rule::BadDunderMethodName);
|
||||
let enforce_docstrings = checker.any_enabled(&[
|
||||
let enforce_stubs =
|
||||
checker.source_type.is_stub() && checker.is_rule_enabled(Rule::DocstringInStub);
|
||||
let enforce_stubs_and_runtime = checker.is_rule_enabled(Rule::IterMethodReturnIterable);
|
||||
let enforce_dunder_method = checker.is_rule_enabled(Rule::BadDunderMethodName);
|
||||
let enforce_docstrings = checker.any_rule_enabled(&[
|
||||
Rule::MissingBlankLineAfterLastSection,
|
||||
Rule::MissingBlankLineAfterSummary,
|
||||
Rule::BlankLineBeforeClass,
|
||||
@@ -79,7 +80,7 @@ pub(crate) fn definitions(checker: &mut Checker) {
|
||||
Rule::UndocumentedPublicNestedClass,
|
||||
Rule::UndocumentedPublicPackage,
|
||||
]);
|
||||
let enforce_pydoclint = checker.any_enabled(&[
|
||||
let enforce_pydoclint = checker.any_rule_enabled(&[
|
||||
Rule::DocstringMissingReturns,
|
||||
Rule::DocstringExtraneousReturns,
|
||||
Rule::DocstringMissingYields,
|
||||
@@ -166,7 +167,7 @@ pub(crate) fn definitions(checker: &mut Checker) {
|
||||
if enforce_docstrings || enforce_pydoclint {
|
||||
if pydocstyle::helpers::should_ignore_definition(
|
||||
definition,
|
||||
&checker.settings.pydocstyle,
|
||||
&checker.settings().pydocstyle,
|
||||
&checker.semantic,
|
||||
) {
|
||||
continue;
|
||||
@@ -202,74 +203,76 @@ pub(crate) fn definitions(checker: &mut Checker) {
|
||||
if !pydocstyle::rules::not_empty(checker, &docstring) {
|
||||
continue;
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryMultilineDocstring) {
|
||||
if checker.is_rule_enabled(Rule::UnnecessaryMultilineDocstring) {
|
||||
pydocstyle::rules::one_liner(checker, &docstring);
|
||||
}
|
||||
if checker.any_enabled(&[Rule::BlankLineAfterFunction, Rule::BlankLineBeforeFunction]) {
|
||||
if checker
|
||||
.any_rule_enabled(&[Rule::BlankLineAfterFunction, Rule::BlankLineBeforeFunction])
|
||||
{
|
||||
pydocstyle::rules::blank_before_after_function(checker, &docstring);
|
||||
}
|
||||
if checker.any_enabled(&[
|
||||
if checker.any_rule_enabled(&[
|
||||
Rule::BlankLineBeforeClass,
|
||||
Rule::IncorrectBlankLineAfterClass,
|
||||
Rule::IncorrectBlankLineBeforeClass,
|
||||
]) {
|
||||
pydocstyle::rules::blank_before_after_class(checker, &docstring);
|
||||
}
|
||||
if checker.enabled(Rule::MissingBlankLineAfterSummary) {
|
||||
if checker.is_rule_enabled(Rule::MissingBlankLineAfterSummary) {
|
||||
pydocstyle::rules::blank_after_summary(checker, &docstring);
|
||||
}
|
||||
if checker.any_enabled(&[
|
||||
if checker.any_rule_enabled(&[
|
||||
Rule::DocstringTabIndentation,
|
||||
Rule::OverIndentation,
|
||||
Rule::UnderIndentation,
|
||||
]) {
|
||||
pydocstyle::rules::indent(checker, &docstring);
|
||||
}
|
||||
if checker.enabled(Rule::NewLineAfterLastParagraph) {
|
||||
if checker.is_rule_enabled(Rule::NewLineAfterLastParagraph) {
|
||||
pydocstyle::rules::newline_after_last_paragraph(checker, &docstring);
|
||||
}
|
||||
if checker.enabled(Rule::SurroundingWhitespace) {
|
||||
if checker.is_rule_enabled(Rule::SurroundingWhitespace) {
|
||||
pydocstyle::rules::no_surrounding_whitespace(checker, &docstring);
|
||||
}
|
||||
if checker.any_enabled(&[
|
||||
if checker.any_rule_enabled(&[
|
||||
Rule::MultiLineSummaryFirstLine,
|
||||
Rule::MultiLineSummarySecondLine,
|
||||
]) {
|
||||
pydocstyle::rules::multi_line_summary_start(checker, &docstring);
|
||||
}
|
||||
if checker.enabled(Rule::TripleSingleQuotes) {
|
||||
if checker.is_rule_enabled(Rule::TripleSingleQuotes) {
|
||||
pydocstyle::rules::triple_quotes(checker, &docstring);
|
||||
}
|
||||
if checker.enabled(Rule::EscapeSequenceInDocstring) {
|
||||
if checker.is_rule_enabled(Rule::EscapeSequenceInDocstring) {
|
||||
pydocstyle::rules::backslashes(checker, &docstring);
|
||||
}
|
||||
if checker.enabled(Rule::MissingTrailingPeriod) {
|
||||
if checker.is_rule_enabled(Rule::MissingTrailingPeriod) {
|
||||
pydocstyle::rules::ends_with_period(checker, &docstring);
|
||||
}
|
||||
if checker.enabled(Rule::NonImperativeMood) {
|
||||
if checker.is_rule_enabled(Rule::NonImperativeMood) {
|
||||
pydocstyle::rules::non_imperative_mood(
|
||||
checker,
|
||||
&docstring,
|
||||
&checker.settings.pydocstyle,
|
||||
&checker.settings().pydocstyle,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::SignatureInDocstring) {
|
||||
if checker.is_rule_enabled(Rule::SignatureInDocstring) {
|
||||
pydocstyle::rules::no_signature(checker, &docstring);
|
||||
}
|
||||
if checker.enabled(Rule::FirstWordUncapitalized) {
|
||||
if checker.is_rule_enabled(Rule::FirstWordUncapitalized) {
|
||||
pydocstyle::rules::capitalized(checker, &docstring);
|
||||
}
|
||||
if checker.enabled(Rule::DocstringStartsWithThis) {
|
||||
if checker.is_rule_enabled(Rule::DocstringStartsWithThis) {
|
||||
pydocstyle::rules::starts_with_this(checker, &docstring);
|
||||
}
|
||||
if checker.enabled(Rule::MissingTerminalPunctuation) {
|
||||
if checker.is_rule_enabled(Rule::MissingTerminalPunctuation) {
|
||||
pydocstyle::rules::ends_with_punctuation(checker, &docstring);
|
||||
}
|
||||
if checker.enabled(Rule::OverloadWithDocstring) {
|
||||
if checker.is_rule_enabled(Rule::OverloadWithDocstring) {
|
||||
pydocstyle::rules::if_needed(checker, &docstring);
|
||||
}
|
||||
|
||||
let enforce_sections = checker.any_enabled(&[
|
||||
let enforce_sections = checker.any_rule_enabled(&[
|
||||
Rule::MissingBlankLineAfterLastSection,
|
||||
Rule::BlankLinesBetweenHeaderAndContent,
|
||||
Rule::NonCapitalizedSectionName,
|
||||
@@ -289,7 +292,7 @@ pub(crate) fn definitions(checker: &mut Checker) {
|
||||
if enforce_sections || enforce_pydoclint {
|
||||
let section_contexts = pydocstyle::helpers::get_section_contexts(
|
||||
&docstring,
|
||||
checker.settings.pydocstyle.convention(),
|
||||
checker.settings().pydocstyle.convention(),
|
||||
);
|
||||
|
||||
if enforce_sections {
|
||||
@@ -297,7 +300,7 @@ pub(crate) fn definitions(checker: &mut Checker) {
|
||||
checker,
|
||||
&docstring,
|
||||
§ion_contexts,
|
||||
checker.settings.pydocstyle.convention(),
|
||||
checker.settings().pydocstyle.convention(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -307,7 +310,7 @@ pub(crate) fn definitions(checker: &mut Checker) {
|
||||
definition,
|
||||
&docstring,
|
||||
§ion_contexts,
|
||||
checker.settings.pydocstyle.convention(),
|
||||
checker.settings().pydocstyle.convention(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,17 +17,17 @@ pub(crate) fn except_handler(except_handler: &ExceptHandler, checker: &Checker)
|
||||
range: _,
|
||||
node_index: _,
|
||||
}) => {
|
||||
if checker.enabled(Rule::BareExcept) {
|
||||
if checker.is_rule_enabled(Rule::BareExcept) {
|
||||
pycodestyle::rules::bare_except(checker, type_.as_deref(), body, except_handler);
|
||||
}
|
||||
if checker.enabled(Rule::RaiseWithoutFromInsideExcept) {
|
||||
if checker.is_rule_enabled(Rule::RaiseWithoutFromInsideExcept) {
|
||||
flake8_bugbear::rules::raise_without_from_inside_except(
|
||||
checker,
|
||||
name.as_deref(),
|
||||
body,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::BlindExcept) {
|
||||
if checker.is_rule_enabled(Rule::BlindExcept) {
|
||||
flake8_blind_except::rules::blind_except(
|
||||
checker,
|
||||
type_.as_deref(),
|
||||
@@ -35,42 +35,42 @@ pub(crate) fn except_handler(except_handler: &ExceptHandler, checker: &Checker)
|
||||
body,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::TryExceptPass) {
|
||||
if checker.is_rule_enabled(Rule::TryExceptPass) {
|
||||
flake8_bandit::rules::try_except_pass(
|
||||
checker,
|
||||
except_handler,
|
||||
type_.as_deref(),
|
||||
body,
|
||||
checker.settings.flake8_bandit.check_typed_exception,
|
||||
checker.settings().flake8_bandit.check_typed_exception,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::TryExceptContinue) {
|
||||
if checker.is_rule_enabled(Rule::TryExceptContinue) {
|
||||
flake8_bandit::rules::try_except_continue(
|
||||
checker,
|
||||
except_handler,
|
||||
type_.as_deref(),
|
||||
body,
|
||||
checker.settings.flake8_bandit.check_typed_exception,
|
||||
checker.settings().flake8_bandit.check_typed_exception,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::ExceptWithEmptyTuple) {
|
||||
if checker.is_rule_enabled(Rule::ExceptWithEmptyTuple) {
|
||||
flake8_bugbear::rules::except_with_empty_tuple(checker, except_handler);
|
||||
}
|
||||
if checker.enabled(Rule::ExceptWithNonExceptionClasses) {
|
||||
if checker.is_rule_enabled(Rule::ExceptWithNonExceptionClasses) {
|
||||
flake8_bugbear::rules::except_with_non_exception_classes(checker, except_handler);
|
||||
}
|
||||
if checker.enabled(Rule::BinaryOpException) {
|
||||
if checker.is_rule_enabled(Rule::BinaryOpException) {
|
||||
pylint::rules::binary_op_exception(checker, except_handler);
|
||||
}
|
||||
if let Some(name) = name {
|
||||
if checker.enabled(Rule::AmbiguousVariableName) {
|
||||
if checker.is_rule_enabled(Rule::AmbiguousVariableName) {
|
||||
pycodestyle::rules::ambiguous_variable_name(
|
||||
checker,
|
||||
name.as_str(),
|
||||
name.range(),
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::BuiltinVariableShadowing) {
|
||||
if checker.is_rule_enabled(Rule::BuiltinVariableShadowing) {
|
||||
flake8_builtins::rules::builtin_variable_shadowing(checker, name, name.range());
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,10 +6,10 @@ use crate::rules::{flake8_bugbear, ruff};
|
||||
|
||||
/// Run lint rules over a module.
|
||||
pub(crate) fn module(suite: &Suite, checker: &Checker) {
|
||||
if checker.enabled(Rule::FStringDocstring) {
|
||||
if checker.is_rule_enabled(Rule::FStringDocstring) {
|
||||
flake8_bugbear::rules::f_string_docstring(checker, suite);
|
||||
}
|
||||
if checker.enabled(Rule::InvalidFormatterSuppressionComment) {
|
||||
if checker.is_rule_enabled(Rule::InvalidFormatterSuppressionComment) {
|
||||
ruff::rules::ignored_formatter_suppression_comment(checker, suite);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,14 +7,14 @@ use crate::rules::{flake8_builtins, pycodestyle};
|
||||
|
||||
/// Run lint rules over a [`Parameter`] syntax node.
|
||||
pub(crate) fn parameter(parameter: &Parameter, checker: &Checker) {
|
||||
if checker.enabled(Rule::AmbiguousVariableName) {
|
||||
if checker.is_rule_enabled(Rule::AmbiguousVariableName) {
|
||||
pycodestyle::rules::ambiguous_variable_name(
|
||||
checker,
|
||||
¶meter.name,
|
||||
parameter.name.range(),
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::BuiltinArgumentShadowing) {
|
||||
if checker.is_rule_enabled(Rule::BuiltinArgumentShadowing) {
|
||||
flake8_builtins::rules::builtin_argument_shadowing(checker, parameter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,17 +6,17 @@ use crate::rules::{flake8_bugbear, flake8_pyi, ruff};
|
||||
|
||||
/// Run lint rules over a [`Parameters`] syntax node.
|
||||
pub(crate) fn parameters(parameters: &Parameters, checker: &Checker) {
|
||||
if checker.enabled(Rule::FunctionCallInDefaultArgument) {
|
||||
if checker.is_rule_enabled(Rule::FunctionCallInDefaultArgument) {
|
||||
flake8_bugbear::rules::function_call_in_argument_default(checker, parameters);
|
||||
}
|
||||
if checker.settings.rules.enabled(Rule::ImplicitOptional) {
|
||||
if checker.is_rule_enabled(Rule::ImplicitOptional) {
|
||||
ruff::rules::implicit_optional(checker, parameters);
|
||||
}
|
||||
if checker.source_type.is_stub() {
|
||||
if checker.enabled(Rule::TypedArgumentDefaultInStub) {
|
||||
if checker.is_rule_enabled(Rule::TypedArgumentDefaultInStub) {
|
||||
flake8_pyi::rules::typed_argument_simple_defaults(checker, parameters);
|
||||
}
|
||||
if checker.enabled(Rule::ArgumentDefaultInStub) {
|
||||
if checker.is_rule_enabled(Rule::ArgumentDefaultInStub) {
|
||||
flake8_pyi::rules::argument_simple_defaults(checker, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,37 +6,39 @@ use crate::rules::{flake8_bandit, flake8_pyi, flake8_quotes, pycodestyle, ruff};
|
||||
|
||||
/// Run lint rules over a [`StringLike`] syntax nodes.
|
||||
pub(crate) fn string_like(string_like: StringLike, checker: &Checker) {
|
||||
if checker.any_enabled(&[
|
||||
if checker.any_rule_enabled(&[
|
||||
Rule::AmbiguousUnicodeCharacterString,
|
||||
Rule::AmbiguousUnicodeCharacterDocstring,
|
||||
]) {
|
||||
ruff::rules::ambiguous_unicode_character_string(checker, string_like);
|
||||
}
|
||||
if checker.enabled(Rule::HardcodedBindAllInterfaces) {
|
||||
if checker.is_rule_enabled(Rule::HardcodedBindAllInterfaces) {
|
||||
flake8_bandit::rules::hardcoded_bind_all_interfaces(checker, string_like);
|
||||
}
|
||||
if checker.enabled(Rule::HardcodedTempFile) {
|
||||
if checker.is_rule_enabled(Rule::HardcodedTempFile) {
|
||||
flake8_bandit::rules::hardcoded_tmp_directory(checker, string_like);
|
||||
}
|
||||
if checker.source_type.is_stub() {
|
||||
if checker.enabled(Rule::StringOrBytesTooLong) {
|
||||
if checker.is_rule_enabled(Rule::StringOrBytesTooLong) {
|
||||
flake8_pyi::rules::string_or_bytes_too_long(checker, string_like);
|
||||
}
|
||||
}
|
||||
if checker.any_enabled(&[
|
||||
if checker.any_rule_enabled(&[
|
||||
Rule::BadQuotesInlineString,
|
||||
Rule::BadQuotesMultilineString,
|
||||
Rule::BadQuotesDocstring,
|
||||
]) {
|
||||
flake8_quotes::rules::check_string_quotes(checker, string_like);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryEscapedQuote) {
|
||||
if checker.is_rule_enabled(Rule::UnnecessaryEscapedQuote) {
|
||||
flake8_quotes::rules::unnecessary_escaped_quote(checker, string_like);
|
||||
}
|
||||
if checker.enabled(Rule::AvoidableEscapedQuote) && checker.settings.flake8_quotes.avoid_escape {
|
||||
if checker.is_rule_enabled(Rule::AvoidableEscapedQuote)
|
||||
&& checker.settings().flake8_quotes.avoid_escape
|
||||
{
|
||||
flake8_quotes::rules::avoidable_escaped_quote(checker, string_like);
|
||||
}
|
||||
if checker.enabled(Rule::InvalidEscapeSequence) {
|
||||
if checker.is_rule_enabled(Rule::InvalidEscapeSequence) {
|
||||
pycodestyle::rules::invalid_escape_sequence(checker, string_like);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ use crate::rules::refurb;
|
||||
|
||||
/// Run lint rules over a suite of [`Stmt`] syntax nodes.
|
||||
pub(crate) fn suite(suite: &[Stmt], checker: &Checker) {
|
||||
if checker.enabled(Rule::UnnecessaryPlaceholder) {
|
||||
if checker.is_rule_enabled(Rule::UnnecessaryPlaceholder) {
|
||||
flake8_pie::rules::unnecessary_placeholder(checker, suite);
|
||||
}
|
||||
if checker.enabled(Rule::RepeatedGlobal) {
|
||||
if checker.is_rule_enabled(Rule::RepeatedGlobal) {
|
||||
refurb::rules::repeated_global(checker, suite);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,22 +7,22 @@ use crate::rules::pyflakes;
|
||||
|
||||
/// Run lint rules over all [`UnresolvedReference`] entities in the [`SemanticModel`].
|
||||
pub(crate) fn unresolved_references(checker: &Checker) {
|
||||
if !checker.any_enabled(&[Rule::UndefinedLocalWithImportStarUsage, Rule::UndefinedName]) {
|
||||
if !checker.any_rule_enabled(&[Rule::UndefinedLocalWithImportStarUsage, Rule::UndefinedName]) {
|
||||
return;
|
||||
}
|
||||
|
||||
for reference in checker.semantic.unresolved_references() {
|
||||
if reference.is_wildcard_import() {
|
||||
if checker.enabled(Rule::UndefinedLocalWithImportStarUsage) {
|
||||
checker.report_diagnostic(
|
||||
pyflakes::rules::UndefinedLocalWithImportStarUsage {
|
||||
name: reference.name(checker.source()).to_string(),
|
||||
},
|
||||
reference.range(),
|
||||
);
|
||||
}
|
||||
// F406
|
||||
checker.report_diagnostic_if_enabled(
|
||||
pyflakes::rules::UndefinedLocalWithImportStarUsage {
|
||||
name: reference.name(checker.source()).to_string(),
|
||||
},
|
||||
reference.range(),
|
||||
);
|
||||
} else {
|
||||
if checker.enabled(Rule::UndefinedName) {
|
||||
// F821
|
||||
if checker.is_rule_enabled(Rule::UndefinedName) {
|
||||
if checker.semantic.in_no_type_check() {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ use itertools::Itertools;
|
||||
use log::debug;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
|
||||
use ruff_diagnostics::IsolationLevel;
|
||||
use ruff_diagnostics::{Applicability, Fix, IsolationLevel};
|
||||
use ruff_notebook::{CellOffsets, NotebookIndex};
|
||||
use ruff_python_ast::helpers::{collect_import_from_member, is_docstring_stmt, to_module_path};
|
||||
use ruff_python_ast::identifier::Identifier;
|
||||
@@ -37,8 +37,8 @@ use ruff_python_ast::str::Quote;
|
||||
use ruff_python_ast::visitor::{Visitor, walk_except_handler, walk_pattern};
|
||||
use ruff_python_ast::{
|
||||
self as ast, AnyParameterRef, ArgOrKeyword, Comprehension, ElifElseClause, ExceptHandler, Expr,
|
||||
ExprContext, InterpolatedStringElement, Keyword, MatchCase, ModModule, Parameter, Parameters,
|
||||
Pattern, PythonVersion, Stmt, Suite, UnaryOp,
|
||||
ExprContext, ExprFString, ExprTString, InterpolatedStringElement, Keyword, MatchCase,
|
||||
ModModule, Parameter, Parameters, Pattern, PythonVersion, Stmt, Suite, UnaryOp,
|
||||
};
|
||||
use ruff_python_ast::{PySourceType, helpers, str, visitor};
|
||||
use ruff_python_codegen::{Generator, Stylist};
|
||||
@@ -57,7 +57,7 @@ use ruff_python_semantic::{
|
||||
};
|
||||
use ruff_python_stdlib::builtins::{MAGIC_GLOBALS, python_builtins};
|
||||
use ruff_python_trivia::CommentRanges;
|
||||
use ruff_source_file::{OneIndexed, SourceFile, SourceRow};
|
||||
use ruff_source_file::{OneIndexed, SourceFile, SourceFileBuilder, SourceRow};
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
||||
use crate::checkers::ast::annotation::AnnotationContext;
|
||||
@@ -66,12 +66,13 @@ use crate::importer::{ImportRequest, Importer, ResolutionError};
|
||||
use crate::noqa::NoqaMapping;
|
||||
use crate::package::PackageRoot;
|
||||
use crate::preview::is_undefined_export_in_dunder_init_enabled;
|
||||
use crate::registry::{AsRule, Rule};
|
||||
use crate::registry::Rule;
|
||||
use crate::rules::pyflakes::rules::{
|
||||
LateFutureImport, ReturnOutsideFunction, YieldOutsideFunction,
|
||||
};
|
||||
use crate::rules::pylint::rules::{AwaitOutsideAsync, LoadBeforeGlobalDeclaration};
|
||||
use crate::rules::{flake8_pyi, flake8_type_checking, pyflakes, pyupgrade};
|
||||
use crate::settings::rule_table::RuleTable;
|
||||
use crate::settings::{LinterSettings, TargetVersion, flags};
|
||||
use crate::{Edit, OldDiagnostic, Violation};
|
||||
use crate::{Locator, docstrings, noqa};
|
||||
@@ -206,8 +207,6 @@ pub(crate) struct Checker<'a> {
|
||||
/// The [`NoqaMapping`] for the current analysis (i.e., the mapping from line number to
|
||||
/// suppression commented line number).
|
||||
noqa_line_for: &'a NoqaMapping,
|
||||
/// The [`LinterSettings`] for the current analysis, including the enabled rules.
|
||||
pub(crate) settings: &'a LinterSettings,
|
||||
/// The [`Locator`] for the current file, which enables extraction of source code from byte
|
||||
/// offsets.
|
||||
locator: &'a Locator<'a>,
|
||||
@@ -259,13 +258,12 @@ impl<'a> Checker<'a> {
|
||||
notebook_index: Option<&'a NotebookIndex>,
|
||||
target_version: TargetVersion,
|
||||
context: &'a LintContext<'a>,
|
||||
) -> Checker<'a> {
|
||||
) -> Self {
|
||||
let semantic = SemanticModel::new(&settings.typing_modules, path, module);
|
||||
Self {
|
||||
parsed,
|
||||
parsed_type_annotation: None,
|
||||
parsed_annotations_cache: ParsedAnnotationsCache::new(parsed_annotations_arena),
|
||||
settings,
|
||||
noqa_line_for,
|
||||
noqa,
|
||||
path,
|
||||
@@ -323,7 +321,8 @@ impl<'a> Checker<'a> {
|
||||
/// Return the preferred quote for a generated `StringLiteral` node, given where we are in the
|
||||
/// AST.
|
||||
fn preferred_quote(&self) -> Quote {
|
||||
self.f_string_quote_style().unwrap_or(self.stylist.quote())
|
||||
self.interpolated_string_quote_style()
|
||||
.unwrap_or(self.stylist.quote())
|
||||
}
|
||||
|
||||
/// Return the default string flags a generated `StringLiteral` node should use, given where we
|
||||
@@ -345,21 +344,27 @@ impl<'a> Checker<'a> {
|
||||
ast::FStringFlags::empty().with_quote_style(self.preferred_quote())
|
||||
}
|
||||
|
||||
/// Returns the appropriate quoting for f-string by reversing the one used outside of
|
||||
/// the f-string.
|
||||
/// Returns the appropriate quoting for interpolated strings by reversing the one used outside of
|
||||
/// the interpolated string.
|
||||
///
|
||||
/// If the current expression in the context is not an f-string, returns ``None``.
|
||||
pub(crate) fn f_string_quote_style(&self) -> Option<Quote> {
|
||||
if !self.semantic.in_f_string() {
|
||||
/// If the current expression in the context is not an interpolated string, returns ``None``.
|
||||
pub(crate) fn interpolated_string_quote_style(&self) -> Option<Quote> {
|
||||
if !self.semantic.in_interpolated_string() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Find the quote character used to start the containing f-string.
|
||||
let ast::ExprFString { value, .. } = self
|
||||
.semantic
|
||||
// Find the quote character used to start the containing interpolated string.
|
||||
self.semantic
|
||||
.current_expressions()
|
||||
.find_map(|expr| expr.as_f_string_expr())?;
|
||||
Some(value.iter().next()?.quote_style().opposite())
|
||||
.find_map(|expr| match expr {
|
||||
Expr::FString(ExprFString { value, .. }) => {
|
||||
Some(value.iter().next()?.quote_style().opposite())
|
||||
}
|
||||
Expr::TString(ExprTString { value, .. }) => {
|
||||
Some(value.iter().next()?.quote_style().opposite())
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the [`SourceRow`] for the given offset.
|
||||
@@ -383,7 +388,7 @@ impl<'a> Checker<'a> {
|
||||
|
||||
/// Return a [`DiagnosticGuard`] for reporting a diagnostic.
|
||||
///
|
||||
/// The guard derefs to a [`Diagnostic`], so it can be used to further modify the diagnostic
|
||||
/// The guard derefs to an [`OldDiagnostic`], so it can be used to further modify the diagnostic
|
||||
/// before it is added to the collection in the checker on `Drop`.
|
||||
pub(crate) fn report_diagnostic<'chk, T: Violation>(
|
||||
&'chk self,
|
||||
@@ -396,8 +401,8 @@ impl<'a> Checker<'a> {
|
||||
/// Return a [`DiagnosticGuard`] for reporting a diagnostic if the corresponding rule is
|
||||
/// enabled.
|
||||
///
|
||||
/// Prefer [`Checker::report_diagnostic`] in general because the conversion from a `Diagnostic`
|
||||
/// to a `Rule` is somewhat expensive.
|
||||
/// The guard derefs to an [`OldDiagnostic`], so it can be used to further modify the diagnostic
|
||||
/// before it is added to the collection in the checker on `Drop`.
|
||||
pub(crate) fn report_diagnostic_if_enabled<'chk, T: Violation>(
|
||||
&'chk self,
|
||||
kind: T,
|
||||
@@ -456,6 +461,11 @@ impl<'a> Checker<'a> {
|
||||
&self.semantic
|
||||
}
|
||||
|
||||
/// The [`LinterSettings`] for the current analysis, including the enabled rules.
|
||||
pub(crate) const fn settings(&self) -> &'a LinterSettings {
|
||||
self.context.settings
|
||||
}
|
||||
|
||||
/// The [`Path`] to the file under analysis.
|
||||
pub(crate) const fn path(&self) -> &'a Path {
|
||||
self.path
|
||||
@@ -473,14 +483,14 @@ impl<'a> Checker<'a> {
|
||||
|
||||
/// Returns whether the given rule should be checked.
|
||||
#[inline]
|
||||
pub(crate) const fn enabled(&self, rule: Rule) -> bool {
|
||||
self.settings.rules.enabled(rule)
|
||||
pub(crate) const fn is_rule_enabled(&self, rule: Rule) -> bool {
|
||||
self.context.is_rule_enabled(rule)
|
||||
}
|
||||
|
||||
/// Returns whether any of the given rules should be checked.
|
||||
#[inline]
|
||||
pub(crate) const fn any_enabled(&self, rules: &[Rule]) -> bool {
|
||||
self.settings.rules.any_enabled(rules)
|
||||
pub(crate) const fn any_rule_enabled(&self, rules: &[Rule]) -> bool {
|
||||
self.context.any_rule_enabled(rules)
|
||||
}
|
||||
|
||||
/// Returns the [`IsolationLevel`] to isolate fixes for a given node.
|
||||
@@ -568,7 +578,7 @@ impl<'a> Checker<'a> {
|
||||
) -> Option<TypingImporter<'b, 'a>> {
|
||||
let source_module = if self.target_version() >= version_added_to_typing {
|
||||
"typing"
|
||||
} else if !self.settings.typing_extensions {
|
||||
} else if !self.settings().typing_extensions {
|
||||
return None;
|
||||
} else {
|
||||
"typing_extensions"
|
||||
@@ -615,16 +625,13 @@ impl SemanticSyntaxContext for Checker<'_> {
|
||||
fn report_semantic_error(&self, error: SemanticSyntaxError) {
|
||||
match error.kind {
|
||||
SemanticSyntaxErrorKind::LateFutureImport => {
|
||||
if self.settings.rules.enabled(Rule::LateFutureImport) {
|
||||
// F404
|
||||
if self.is_rule_enabled(Rule::LateFutureImport) {
|
||||
self.report_diagnostic(LateFutureImport, error.range);
|
||||
}
|
||||
}
|
||||
SemanticSyntaxErrorKind::LoadBeforeGlobalDeclaration { name, start } => {
|
||||
if self
|
||||
.settings
|
||||
.rules
|
||||
.enabled(Rule::LoadBeforeGlobalDeclaration)
|
||||
{
|
||||
if self.is_rule_enabled(Rule::LoadBeforeGlobalDeclaration) {
|
||||
self.report_diagnostic(
|
||||
LoadBeforeGlobalDeclaration {
|
||||
name,
|
||||
@@ -635,17 +642,18 @@ impl SemanticSyntaxContext for Checker<'_> {
|
||||
}
|
||||
}
|
||||
SemanticSyntaxErrorKind::YieldOutsideFunction(kind) => {
|
||||
if self.settings.rules.enabled(Rule::YieldOutsideFunction) {
|
||||
if self.is_rule_enabled(Rule::YieldOutsideFunction) {
|
||||
self.report_diagnostic(YieldOutsideFunction::new(kind), error.range);
|
||||
}
|
||||
}
|
||||
SemanticSyntaxErrorKind::ReturnOutsideFunction => {
|
||||
if self.settings.rules.enabled(Rule::ReturnOutsideFunction) {
|
||||
// F706
|
||||
if self.is_rule_enabled(Rule::ReturnOutsideFunction) {
|
||||
self.report_diagnostic(ReturnOutsideFunction, error.range);
|
||||
}
|
||||
}
|
||||
SemanticSyntaxErrorKind::AwaitOutsideAsyncFunction(_) => {
|
||||
if self.settings.rules.enabled(Rule::AwaitOutsideAsync) {
|
||||
if self.is_rule_enabled(Rule::AwaitOutsideAsync) {
|
||||
self.report_diagnostic(AwaitOutsideAsync, error.range);
|
||||
}
|
||||
}
|
||||
@@ -1050,7 +1058,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
|
||||
let annotation = AnnotationContext::from_function(
|
||||
function_def,
|
||||
&self.semantic,
|
||||
self.settings,
|
||||
self.settings(),
|
||||
self.target_version(),
|
||||
);
|
||||
|
||||
@@ -1252,7 +1260,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
|
||||
}) => {
|
||||
match AnnotationContext::from_model(
|
||||
&self.semantic,
|
||||
self.settings,
|
||||
self.settings(),
|
||||
self.target_version(),
|
||||
) {
|
||||
AnnotationContext::RuntimeRequired => {
|
||||
@@ -1864,8 +1872,8 @@ impl<'a> Visitor<'a> for Checker<'a> {
|
||||
match typing::match_annotated_subscript(
|
||||
value,
|
||||
&self.semantic,
|
||||
self.settings.typing_modules.iter().map(String::as_str),
|
||||
&self.settings.pyflakes.extend_generics,
|
||||
self.settings().typing_modules.iter().map(String::as_str),
|
||||
&self.settings().pyflakes.extend_generics,
|
||||
) {
|
||||
// Ex) Literal["Class"]
|
||||
Some(typing::SubscriptKind::Literal) => {
|
||||
@@ -2357,7 +2365,7 @@ impl<'a> Checker<'a> {
|
||||
fn visit_cast_type_argument(&mut self, arg: &'a Expr) {
|
||||
self.visit_type_definition(arg);
|
||||
|
||||
if !self.source_type.is_stub() && self.enabled(Rule::RuntimeCastValue) {
|
||||
if !self.source_type.is_stub() && self.is_rule_enabled(Rule::RuntimeCastValue) {
|
||||
flake8_type_checking::rules::runtime_cast_value(self, arg);
|
||||
}
|
||||
}
|
||||
@@ -2472,6 +2480,7 @@ impl<'a> Checker<'a> {
|
||||
|
||||
fn bind_builtins(&mut self) {
|
||||
let target_version = self.target_version();
|
||||
let settings = self.settings();
|
||||
let mut bind_builtin = |builtin| {
|
||||
// Add the builtin to the scope.
|
||||
let binding_id = self.semantic.push_builtin();
|
||||
@@ -2485,7 +2494,7 @@ impl<'a> Checker<'a> {
|
||||
for builtin in MAGIC_GLOBALS {
|
||||
bind_builtin(builtin);
|
||||
}
|
||||
for builtin in &self.settings.builtins {
|
||||
for builtin in &settings.builtins {
|
||||
bind_builtin(builtin);
|
||||
}
|
||||
}
|
||||
@@ -2759,12 +2768,12 @@ impl<'a> Checker<'a> {
|
||||
if self.semantic.in_annotation()
|
||||
&& self.semantic.in_typing_only_annotation()
|
||||
{
|
||||
if self.enabled(Rule::QuotedAnnotation) {
|
||||
if self.is_rule_enabled(Rule::QuotedAnnotation) {
|
||||
pyupgrade::rules::quoted_annotation(self, annotation, range);
|
||||
}
|
||||
}
|
||||
if self.source_type.is_stub() {
|
||||
if self.enabled(Rule::QuotedAnnotationInStub) {
|
||||
if self.is_rule_enabled(Rule::QuotedAnnotationInStub) {
|
||||
flake8_pyi::rules::quoted_annotation_in_stub(
|
||||
self, annotation, range,
|
||||
);
|
||||
@@ -2786,7 +2795,9 @@ impl<'a> Checker<'a> {
|
||||
self.visit_expr(parsed_expr);
|
||||
if self.semantic.in_type_alias_value() {
|
||||
// stub files are covered by PYI020
|
||||
if !self.source_type.is_stub() && self.enabled(Rule::QuotedTypeAlias) {
|
||||
if !self.source_type.is_stub()
|
||||
&& self.is_rule_enabled(Rule::QuotedTypeAlias)
|
||||
{
|
||||
flake8_type_checking::rules::quoted_type_alias(
|
||||
self,
|
||||
parsed_expr,
|
||||
@@ -2799,7 +2810,8 @@ impl<'a> Checker<'a> {
|
||||
Err(parse_error) => {
|
||||
self.semantic.restore(snapshot);
|
||||
|
||||
if self.enabled(Rule::ForwardAnnotationSyntaxError) {
|
||||
// F722
|
||||
if self.is_rule_enabled(Rule::ForwardAnnotationSyntaxError) {
|
||||
self.report_type_diagnostic(
|
||||
pyflakes::rules::ForwardAnnotationSyntaxError {
|
||||
parse_error: parse_error.error.to_string(),
|
||||
@@ -2946,7 +2958,8 @@ impl<'a> Checker<'a> {
|
||||
self.semantic.flags -= SemanticModelFlags::DUNDER_ALL_DEFINITION;
|
||||
} else {
|
||||
if self.semantic.global_scope().uses_star_imports() {
|
||||
if self.enabled(Rule::UndefinedLocalWithImportStarUsage) {
|
||||
// F405
|
||||
if self.is_rule_enabled(Rule::UndefinedLocalWithImportStarUsage) {
|
||||
self.report_diagnostic(
|
||||
pyflakes::rules::UndefinedLocalWithImportStarUsage {
|
||||
name: name.to_string(),
|
||||
@@ -2956,8 +2969,9 @@ impl<'a> Checker<'a> {
|
||||
.set_parent(definition.start());
|
||||
}
|
||||
} else {
|
||||
if self.enabled(Rule::UndefinedExport) {
|
||||
if is_undefined_export_in_dunder_init_enabled(self.settings)
|
||||
// F822
|
||||
if self.is_rule_enabled(Rule::UndefinedExport) {
|
||||
if is_undefined_export_in_dunder_init_enabled(self.settings())
|
||||
|| !self.path.ends_with("__init__.py")
|
||||
{
|
||||
self.report_diagnostic(
|
||||
@@ -3107,17 +3121,28 @@ pub(crate) fn check_ast(
|
||||
/// a [`Violation`] to the contained [`OldDiagnostic`] collection on `Drop`.
|
||||
pub(crate) struct LintContext<'a> {
|
||||
diagnostics: RefCell<Vec<OldDiagnostic>>,
|
||||
source_file: &'a SourceFile,
|
||||
source_file: SourceFile,
|
||||
rules: RuleTable,
|
||||
settings: &'a LinterSettings,
|
||||
}
|
||||
|
||||
impl<'a> LintContext<'a> {
|
||||
/// Create a new collector with the given `source_file` and an empty collection of
|
||||
/// `OldDiagnostic`s.
|
||||
pub(crate) fn new(source_file: &'a SourceFile, settings: &'a LinterSettings) -> Self {
|
||||
pub(crate) fn new(path: &Path, contents: &str, settings: &'a LinterSettings) -> Self {
|
||||
let source_file =
|
||||
SourceFileBuilder::new(path.to_string_lossy().as_ref(), contents).finish();
|
||||
|
||||
// Ignore diagnostics based on per-file-ignores.
|
||||
let mut rules = settings.rules.clone();
|
||||
for ignore in crate::fs::ignores_from_path(path, &settings.per_file_ignores) {
|
||||
rules.disable(ignore);
|
||||
}
|
||||
|
||||
Self {
|
||||
diagnostics: RefCell::default(),
|
||||
source_file,
|
||||
rules,
|
||||
settings,
|
||||
}
|
||||
}
|
||||
@@ -3125,7 +3150,7 @@ impl<'a> LintContext<'a> {
|
||||
/// Return a [`DiagnosticGuard`] for reporting a diagnostic.
|
||||
///
|
||||
/// The guard derefs to an [`OldDiagnostic`], so it can be used to further modify the diagnostic
|
||||
/// before it is added to the collection in the collector on `Drop`.
|
||||
/// before it is added to the collection in the context on `Drop`.
|
||||
pub(crate) fn report_diagnostic<'chk, T: Violation>(
|
||||
&'chk self,
|
||||
kind: T,
|
||||
@@ -3133,43 +3158,59 @@ impl<'a> LintContext<'a> {
|
||||
) -> DiagnosticGuard<'chk, 'a> {
|
||||
DiagnosticGuard {
|
||||
context: self,
|
||||
diagnostic: Some(OldDiagnostic::new(kind, range, self.source_file)),
|
||||
diagnostic: Some(OldDiagnostic::new(kind, range, &self.source_file)),
|
||||
rule: T::rule(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a [`DiagnosticGuard`] for reporting a diagnostic if the corresponding rule is
|
||||
/// enabled.
|
||||
///
|
||||
/// Prefer [`DiagnosticsCollector::report_diagnostic`] in general because the conversion from an
|
||||
/// `OldDiagnostic` to a `Rule` is somewhat expensive.
|
||||
/// The guard derefs to an [`OldDiagnostic`], so it can be used to further modify the diagnostic
|
||||
/// before it is added to the collection in the context on `Drop`.
|
||||
pub(crate) fn report_diagnostic_if_enabled<'chk, T: Violation>(
|
||||
&'chk self,
|
||||
kind: T,
|
||||
range: TextRange,
|
||||
) -> Option<DiagnosticGuard<'chk, 'a>> {
|
||||
let diagnostic = OldDiagnostic::new(kind, range, self.source_file);
|
||||
if self.settings.rules.enabled(diagnostic.rule()) {
|
||||
let rule = T::rule();
|
||||
if self.is_rule_enabled(rule) {
|
||||
Some(DiagnosticGuard {
|
||||
context: self,
|
||||
diagnostic: Some(diagnostic),
|
||||
diagnostic: Some(OldDiagnostic::new(kind, range, &self.source_file)),
|
||||
rule,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn into_diagnostics(self) -> Vec<OldDiagnostic> {
|
||||
self.diagnostics.into_inner()
|
||||
#[inline]
|
||||
pub(crate) const fn is_rule_enabled(&self, rule: Rule) -> bool {
|
||||
self.rules.enabled(rule)
|
||||
}
|
||||
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
self.diagnostics.borrow().is_empty()
|
||||
#[inline]
|
||||
pub(crate) const fn any_rule_enabled(&self, rules: &[Rule]) -> bool {
|
||||
self.rules.any_enabled(rules)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn iter_enabled_rules(&self) -> impl Iterator<Item = Rule> + '_ {
|
||||
self.rules.iter_enabled()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn into_parts(self) -> (Vec<OldDiagnostic>, SourceFile) {
|
||||
(self.diagnostics.into_inner(), self.source_file)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn as_mut_vec(&mut self) -> &mut Vec<OldDiagnostic> {
|
||||
self.diagnostics.get_mut()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn iter(&mut self) -> impl Iterator<Item = &OldDiagnostic> {
|
||||
self.diagnostics.get_mut().iter()
|
||||
}
|
||||
@@ -3189,6 +3230,7 @@ pub(crate) struct DiagnosticGuard<'a, 'b> {
|
||||
///
|
||||
/// This is always `Some` until the `Drop` (or `defuse`) call.
|
||||
diagnostic: Option<OldDiagnostic>,
|
||||
rule: Rule,
|
||||
}
|
||||
|
||||
impl DiagnosticGuard<'_, '_> {
|
||||
@@ -3201,6 +3243,50 @@ impl DiagnosticGuard<'_, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl DiagnosticGuard<'_, '_> {
|
||||
fn resolve_applicability(&self, fix: &Fix) -> Applicability {
|
||||
self.context
|
||||
.settings
|
||||
.fix_safety
|
||||
.resolve_applicability(self.rule, fix.applicability())
|
||||
}
|
||||
|
||||
/// Set the [`Fix`] used to fix the diagnostic.
|
||||
#[inline]
|
||||
pub(crate) fn set_fix(&mut self, fix: Fix) {
|
||||
if !self.context.rules.should_fix(self.rule) {
|
||||
self.fix = None;
|
||||
return;
|
||||
}
|
||||
let applicability = self.resolve_applicability(&fix);
|
||||
self.fix = Some(fix.with_applicability(applicability));
|
||||
}
|
||||
|
||||
/// Set the [`Fix`] used to fix the diagnostic, if the provided function returns `Ok`.
|
||||
/// Otherwise, log the error.
|
||||
#[inline]
|
||||
pub(crate) fn try_set_fix(&mut self, func: impl FnOnce() -> anyhow::Result<Fix>) {
|
||||
match func() {
|
||||
Ok(fix) => self.set_fix(fix),
|
||||
Err(err) => log::debug!("Failed to create fix for {}: {}", self.name(), err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the [`Fix`] used to fix the diagnostic, if the provided function returns `Ok`.
|
||||
/// Otherwise, log the error.
|
||||
#[inline]
|
||||
pub(crate) fn try_set_optional_fix(
|
||||
&mut self,
|
||||
func: impl FnOnce() -> anyhow::Result<Option<Fix>>,
|
||||
) {
|
||||
match func() {
|
||||
Ok(None) => {}
|
||||
Ok(Some(fix)) => self.set_fix(fix),
|
||||
Err(err) => log::debug!("Failed to create fix for {}: {}", self.name(), err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for DiagnosticGuard<'_, '_> {
|
||||
type Target = OldDiagnostic;
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ pub(crate) fn check_file_path(
|
||||
context: &LintContext,
|
||||
) {
|
||||
// flake8-no-pep420
|
||||
if settings.rules.enabled(Rule::ImplicitNamespacePackage) {
|
||||
if context.is_rule_enabled(Rule::ImplicitNamespacePackage) {
|
||||
let allow_nested_roots = is_allow_nested_roots_enabled(settings);
|
||||
implicit_namespace_package(
|
||||
path,
|
||||
@@ -38,12 +38,12 @@ pub(crate) fn check_file_path(
|
||||
}
|
||||
|
||||
// pep8-naming
|
||||
if settings.rules.enabled(Rule::InvalidModuleName) {
|
||||
if context.is_rule_enabled(Rule::InvalidModuleName) {
|
||||
invalid_module_name(path, package, &settings.pep8_naming.ignore_names, context);
|
||||
}
|
||||
|
||||
// flake8-builtins
|
||||
if settings.rules.enabled(Rule::StdlibModuleShadowing) {
|
||||
if context.is_rule_enabled(Rule::StdlibModuleShadowing) {
|
||||
stdlib_module_shadowing(path, settings, target_version, context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ pub(crate) fn check_imports(
|
||||
let blocks: Vec<&Block> = tracker.iter().collect();
|
||||
|
||||
// Enforce import rules.
|
||||
if settings.rules.enabled(Rule::UnsortedImports) {
|
||||
if context.is_rule_enabled(Rule::UnsortedImports) {
|
||||
for block in &blocks {
|
||||
if !block.imports.is_empty() {
|
||||
isort::rules::organize_imports(
|
||||
@@ -60,7 +60,7 @@ pub(crate) fn check_imports(
|
||||
}
|
||||
}
|
||||
}
|
||||
if settings.rules.enabled(Rule::MissingRequiredImport) {
|
||||
if context.is_rule_enabled(Rule::MissingRequiredImport) {
|
||||
isort::rules::add_required_imports(
|
||||
parsed,
|
||||
locator,
|
||||
|
||||
@@ -47,49 +47,48 @@ pub(crate) fn check_logical_lines(
|
||||
let mut prev_indent_level = None;
|
||||
let indent_char = stylist.indentation().as_char();
|
||||
|
||||
let enforce_space_around_operator = settings.rules.any_enabled(&[
|
||||
let enforce_space_around_operator = context.any_rule_enabled(&[
|
||||
Rule::MultipleSpacesBeforeOperator,
|
||||
Rule::MultipleSpacesAfterOperator,
|
||||
Rule::TabBeforeOperator,
|
||||
Rule::TabAfterOperator,
|
||||
]);
|
||||
let enforce_whitespace_around_named_parameter_equals = settings.rules.any_enabled(&[
|
||||
let enforce_whitespace_around_named_parameter_equals = context.any_rule_enabled(&[
|
||||
Rule::UnexpectedSpacesAroundKeywordParameterEquals,
|
||||
Rule::MissingWhitespaceAroundParameterEquals,
|
||||
]);
|
||||
let enforce_missing_whitespace_around_operator = settings.rules.any_enabled(&[
|
||||
let enforce_missing_whitespace_around_operator = context.any_rule_enabled(&[
|
||||
Rule::MissingWhitespaceAroundOperator,
|
||||
Rule::MissingWhitespaceAroundArithmeticOperator,
|
||||
Rule::MissingWhitespaceAroundBitwiseOrShiftOperator,
|
||||
Rule::MissingWhitespaceAroundModuloOperator,
|
||||
]);
|
||||
let enforce_missing_whitespace = settings.rules.enabled(Rule::MissingWhitespace);
|
||||
let enforce_space_after_comma = settings
|
||||
.rules
|
||||
.any_enabled(&[Rule::MultipleSpacesAfterComma, Rule::TabAfterComma]);
|
||||
let enforce_extraneous_whitespace = settings.rules.any_enabled(&[
|
||||
let enforce_missing_whitespace = context.is_rule_enabled(Rule::MissingWhitespace);
|
||||
let enforce_space_after_comma =
|
||||
context.any_rule_enabled(&[Rule::MultipleSpacesAfterComma, Rule::TabAfterComma]);
|
||||
let enforce_extraneous_whitespace = context.any_rule_enabled(&[
|
||||
Rule::WhitespaceAfterOpenBracket,
|
||||
Rule::WhitespaceBeforeCloseBracket,
|
||||
Rule::WhitespaceBeforePunctuation,
|
||||
]);
|
||||
let enforce_whitespace_around_keywords = settings.rules.any_enabled(&[
|
||||
let enforce_whitespace_around_keywords = context.any_rule_enabled(&[
|
||||
Rule::MultipleSpacesAfterKeyword,
|
||||
Rule::MultipleSpacesBeforeKeyword,
|
||||
Rule::TabAfterKeyword,
|
||||
Rule::TabBeforeKeyword,
|
||||
]);
|
||||
let enforce_missing_whitespace_after_keyword =
|
||||
settings.rules.enabled(Rule::MissingWhitespaceAfterKeyword);
|
||||
let enforce_whitespace_before_comment = settings.rules.any_enabled(&[
|
||||
context.is_rule_enabled(Rule::MissingWhitespaceAfterKeyword);
|
||||
let enforce_whitespace_before_comment = context.any_rule_enabled(&[
|
||||
Rule::TooFewSpacesBeforeInlineComment,
|
||||
Rule::NoSpaceAfterInlineComment,
|
||||
Rule::NoSpaceAfterBlockComment,
|
||||
Rule::MultipleLeadingHashesForBlockComment,
|
||||
]);
|
||||
let enforce_whitespace_before_parameters =
|
||||
settings.rules.enabled(Rule::WhitespaceBeforeParameters);
|
||||
let enforce_redundant_backslash = settings.rules.enabled(Rule::RedundantBackslash);
|
||||
let enforce_indentation = settings.rules.any_enabled(&[
|
||||
context.is_rule_enabled(Rule::WhitespaceBeforeParameters);
|
||||
let enforce_redundant_backslash = context.is_rule_enabled(Rule::RedundantBackslash);
|
||||
let enforce_indentation = context.any_rule_enabled(&[
|
||||
Rule::IndentationWithInvalidMultiple,
|
||||
Rule::NoIndentedBlock,
|
||||
Rule::UnexpectedIndentation,
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::fix::edits::delete_comment;
|
||||
use crate::noqa::{
|
||||
Code, Directive, FileExemption, FileNoqaDirectives, NoqaDirectives, NoqaMapping,
|
||||
};
|
||||
use crate::registry::{AsRule, Rule, RuleSet};
|
||||
use crate::registry::Rule;
|
||||
use crate::rule_redirects::get_redirect_target;
|
||||
use crate::rules::pygrep_hooks;
|
||||
use crate::rules::ruff;
|
||||
@@ -22,7 +22,7 @@ use crate::{Edit, Fix, Locator};
|
||||
|
||||
use super::ast::LintContext;
|
||||
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
/// RUF100
|
||||
pub(crate) fn check_noqa(
|
||||
context: &mut LintContext,
|
||||
path: &Path,
|
||||
@@ -30,7 +30,6 @@ pub(crate) fn check_noqa(
|
||||
comment_ranges: &CommentRanges,
|
||||
noqa_line_for: &NoqaMapping,
|
||||
analyze_directives: bool,
|
||||
per_file_ignores: &RuleSet,
|
||||
settings: &LinterSettings,
|
||||
) -> Vec<usize> {
|
||||
// Identify any codes that are globally exempted (within the current file).
|
||||
@@ -47,14 +46,15 @@ pub(crate) fn check_noqa(
|
||||
|
||||
// Remove any ignored diagnostics.
|
||||
'outer: for (index, diagnostic) in context.iter().enumerate() {
|
||||
let rule = diagnostic.rule();
|
||||
// Can't ignore syntax errors.
|
||||
let Some(code) = diagnostic.noqa_code() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if matches!(rule, Rule::BlanketNOQA) {
|
||||
if code == Rule::BlanketNOQA.noqa_code() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let code = rule.noqa_code();
|
||||
|
||||
match &exemption {
|
||||
FileExemption::All(_) => {
|
||||
// If the file is exempted, ignore all diagnostics.
|
||||
@@ -106,10 +106,9 @@ pub(crate) fn check_noqa(
|
||||
|
||||
// Enforce that the noqa directive was actually used (RUF100), unless RUF100 was itself
|
||||
// suppressed.
|
||||
if settings.rules.enabled(Rule::UnusedNOQA)
|
||||
if context.is_rule_enabled(Rule::UnusedNOQA)
|
||||
&& analyze_directives
|
||||
&& !exemption.includes(Rule::UnusedNOQA)
|
||||
&& !per_file_ignores.contains(Rule::UnusedNOQA)
|
||||
{
|
||||
let directives = noqa_directives
|
||||
.lines()
|
||||
@@ -148,7 +147,9 @@ pub(crate) fn check_noqa(
|
||||
|
||||
if seen_codes.insert(original_code) {
|
||||
let is_code_used = if is_file_level {
|
||||
context.iter().any(|diag| diag.rule().noqa_code() == code)
|
||||
context
|
||||
.iter()
|
||||
.any(|diag| diag.noqa_code().is_some_and(|noqa| noqa == code))
|
||||
} else {
|
||||
matches.iter().any(|match_| *match_ == code)
|
||||
} || settings
|
||||
@@ -159,7 +160,7 @@ pub(crate) fn check_noqa(
|
||||
if is_code_used {
|
||||
valid_codes.push(original_code);
|
||||
} else if let Ok(rule) = Rule::from_code(code) {
|
||||
if settings.rules.enabled(rule) {
|
||||
if context.is_rule_enabled(rule) {
|
||||
unmatched_codes.push(original_code);
|
||||
} else {
|
||||
disabled_codes.push(original_code);
|
||||
@@ -229,18 +230,12 @@ pub(crate) fn check_noqa(
|
||||
}
|
||||
}
|
||||
|
||||
if settings.rules.enabled(Rule::RedirectedNOQA)
|
||||
&& !per_file_ignores.contains(Rule::RedirectedNOQA)
|
||||
&& !exemption.includes(Rule::RedirectedNOQA)
|
||||
{
|
||||
if context.is_rule_enabled(Rule::RedirectedNOQA) && !exemption.includes(Rule::RedirectedNOQA) {
|
||||
ruff::rules::redirected_noqa(context, &noqa_directives);
|
||||
ruff::rules::redirected_file_noqa(context, &file_noqa_directives);
|
||||
}
|
||||
|
||||
if settings.rules.enabled(Rule::BlanketNOQA)
|
||||
&& !per_file_ignores.contains(Rule::BlanketNOQA)
|
||||
&& !exemption.enumerates(Rule::BlanketNOQA)
|
||||
{
|
||||
if context.is_rule_enabled(Rule::BlanketNOQA) && !exemption.enumerates(Rule::BlanketNOQA) {
|
||||
pygrep_hooks::rules::blanket_noqa(
|
||||
context,
|
||||
&noqa_directives,
|
||||
@@ -249,8 +244,7 @@ pub(crate) fn check_noqa(
|
||||
);
|
||||
}
|
||||
|
||||
if settings.rules.enabled(Rule::InvalidRuleCode)
|
||||
&& !per_file_ignores.contains(Rule::InvalidRuleCode)
|
||||
if context.is_rule_enabled(Rule::InvalidRuleCode)
|
||||
&& !exemption.enumerates(Rule::InvalidRuleCode)
|
||||
{
|
||||
ruff::rules::invalid_noqa_code(context, &noqa_directives, locator, &settings.external);
|
||||
|
||||
@@ -26,15 +26,16 @@ pub(crate) fn check_physical_lines(
|
||||
settings: &LinterSettings,
|
||||
context: &LintContext,
|
||||
) {
|
||||
let enforce_doc_line_too_long = settings.rules.enabled(Rule::DocLineTooLong);
|
||||
let enforce_line_too_long = settings.rules.enabled(Rule::LineTooLong);
|
||||
let enforce_no_newline_at_end_of_file = settings.rules.enabled(Rule::MissingNewlineAtEndOfFile);
|
||||
let enforce_mixed_spaces_and_tabs = settings.rules.enabled(Rule::MixedSpacesAndTabs);
|
||||
let enforce_bidirectional_unicode = settings.rules.enabled(Rule::BidirectionalUnicode);
|
||||
let enforce_trailing_whitespace = settings.rules.enabled(Rule::TrailingWhitespace);
|
||||
let enforce_doc_line_too_long = context.is_rule_enabled(Rule::DocLineTooLong);
|
||||
let enforce_line_too_long = context.is_rule_enabled(Rule::LineTooLong);
|
||||
let enforce_no_newline_at_end_of_file =
|
||||
context.is_rule_enabled(Rule::MissingNewlineAtEndOfFile);
|
||||
let enforce_mixed_spaces_and_tabs = context.is_rule_enabled(Rule::MixedSpacesAndTabs);
|
||||
let enforce_bidirectional_unicode = context.is_rule_enabled(Rule::BidirectionalUnicode);
|
||||
let enforce_trailing_whitespace = context.is_rule_enabled(Rule::TrailingWhitespace);
|
||||
let enforce_blank_line_contains_whitespace =
|
||||
settings.rules.enabled(Rule::BlankLineWithWhitespace);
|
||||
let enforce_copyright_notice = settings.rules.enabled(Rule::MissingCopyrightNotice);
|
||||
context.is_rule_enabled(Rule::BlankLineWithWhitespace);
|
||||
let enforce_copyright_notice = context.is_rule_enabled(Rule::MissingCopyrightNotice);
|
||||
|
||||
let mut doc_lines_iter = doc_lines.iter().peekable();
|
||||
let comment_ranges = indexer.comment_ranges();
|
||||
@@ -62,10 +63,10 @@ pub(crate) fn check_physical_lines(
|
||||
}
|
||||
|
||||
if enforce_trailing_whitespace || enforce_blank_line_contains_whitespace {
|
||||
trailing_whitespace(&line, locator, indexer, settings, context);
|
||||
trailing_whitespace(&line, locator, indexer, context);
|
||||
}
|
||||
|
||||
if settings.rules.enabled(Rule::IndentedFormFeed) {
|
||||
if context.is_rule_enabled(Rule::IndentedFormFeed) {
|
||||
indented_form_feed(&line, context);
|
||||
}
|
||||
}
|
||||
@@ -81,10 +82,11 @@ pub(crate) fn check_physical_lines(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::Path;
|
||||
|
||||
use ruff_python_codegen::Stylist;
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_python_parser::parse_module;
|
||||
use ruff_source_file::SourceFileBuilder;
|
||||
|
||||
use crate::Locator;
|
||||
use crate::checkers::ast::LintContext;
|
||||
@@ -104,7 +106,6 @@ mod tests {
|
||||
let stylist = Stylist::from_tokens(parsed.tokens(), locator.contents());
|
||||
|
||||
let check_with_max_line_length = |line_length: LineLength| {
|
||||
let source_file = SourceFileBuilder::new("<filename>", line).finish();
|
||||
let settings = LinterSettings {
|
||||
pycodestyle: pycodestyle::settings::Settings {
|
||||
max_line_length: line_length,
|
||||
@@ -112,9 +113,9 @@ mod tests {
|
||||
},
|
||||
..LinterSettings::for_rule(Rule::LineTooLong)
|
||||
};
|
||||
let diagnostics = LintContext::new(&source_file, &settings);
|
||||
let diagnostics = LintContext::new(Path::new("<filename>"), line, &settings);
|
||||
check_physical_lines(&locator, &stylist, &indexer, &[], &settings, &diagnostics);
|
||||
diagnostics.into_diagnostics()
|
||||
diagnostics.into_parts().0
|
||||
};
|
||||
let line_length = LineLength::try_from(8).unwrap();
|
||||
assert_eq!(check_with_max_line_length(line_length), vec![]);
|
||||
|
||||
@@ -34,7 +34,7 @@ pub(crate) fn check_tokens(
|
||||
) {
|
||||
let comment_ranges = indexer.comment_ranges();
|
||||
|
||||
if settings.rules.any_enabled(&[
|
||||
if context.any_rule_enabled(&[
|
||||
Rule::BlankLineBetweenMethods,
|
||||
Rule::BlankLinesTopLevel,
|
||||
Rule::TooManyBlankLines,
|
||||
@@ -53,36 +53,33 @@ pub(crate) fn check_tokens(
|
||||
.check_lines(tokens);
|
||||
}
|
||||
|
||||
if settings.rules.enabled(Rule::BlanketTypeIgnore) {
|
||||
if context.is_rule_enabled(Rule::BlanketTypeIgnore) {
|
||||
pygrep_hooks::rules::blanket_type_ignore(context, comment_ranges, locator);
|
||||
}
|
||||
|
||||
if settings.rules.enabled(Rule::EmptyComment) {
|
||||
if context.is_rule_enabled(Rule::EmptyComment) {
|
||||
pylint::rules::empty_comments(context, comment_ranges, locator);
|
||||
}
|
||||
|
||||
if settings
|
||||
.rules
|
||||
.enabled(Rule::AmbiguousUnicodeCharacterComment)
|
||||
{
|
||||
if context.is_rule_enabled(Rule::AmbiguousUnicodeCharacterComment) {
|
||||
for range in comment_ranges {
|
||||
ruff::rules::ambiguous_unicode_character_comment(context, locator, range, settings);
|
||||
}
|
||||
}
|
||||
|
||||
if settings.rules.enabled(Rule::CommentedOutCode) {
|
||||
if context.is_rule_enabled(Rule::CommentedOutCode) {
|
||||
eradicate::rules::commented_out_code(context, locator, comment_ranges, settings);
|
||||
}
|
||||
|
||||
if settings.rules.enabled(Rule::UTF8EncodingDeclaration) {
|
||||
if context.is_rule_enabled(Rule::UTF8EncodingDeclaration) {
|
||||
pyupgrade::rules::unnecessary_coding_comment(context, locator, comment_ranges);
|
||||
}
|
||||
|
||||
if settings.rules.enabled(Rule::TabIndentation) {
|
||||
if context.is_rule_enabled(Rule::TabIndentation) {
|
||||
pycodestyle::rules::tab_indentation(context, locator, indexer);
|
||||
}
|
||||
|
||||
if settings.rules.any_enabled(&[
|
||||
if context.any_rule_enabled(&[
|
||||
Rule::InvalidCharacterBackspace,
|
||||
Rule::InvalidCharacterSub,
|
||||
Rule::InvalidCharacterEsc,
|
||||
@@ -94,7 +91,7 @@ pub(crate) fn check_tokens(
|
||||
}
|
||||
}
|
||||
|
||||
if settings.rules.any_enabled(&[
|
||||
if context.any_rule_enabled(&[
|
||||
Rule::MultipleStatementsOnOneLineColon,
|
||||
Rule::MultipleStatementsOnOneLineSemicolon,
|
||||
Rule::UselessSemicolon,
|
||||
@@ -109,14 +106,14 @@ pub(crate) fn check_tokens(
|
||||
);
|
||||
}
|
||||
|
||||
if settings.rules.any_enabled(&[
|
||||
if context.any_rule_enabled(&[
|
||||
Rule::SingleLineImplicitStringConcatenation,
|
||||
Rule::MultiLineImplicitStringConcatenation,
|
||||
]) {
|
||||
flake8_implicit_str_concat::rules::implicit(context, tokens, locator, indexer, settings);
|
||||
}
|
||||
|
||||
if settings.rules.any_enabled(&[
|
||||
if context.any_rule_enabled(&[
|
||||
Rule::MissingTrailingComma,
|
||||
Rule::TrailingCommaOnBareTuple,
|
||||
Rule::ProhibitedTrailingComma,
|
||||
@@ -124,25 +121,25 @@ pub(crate) fn check_tokens(
|
||||
flake8_commas::rules::trailing_commas(context, tokens, locator, indexer);
|
||||
}
|
||||
|
||||
if settings.rules.enabled(Rule::ExtraneousParentheses) {
|
||||
if context.is_rule_enabled(Rule::ExtraneousParentheses) {
|
||||
pyupgrade::rules::extraneous_parentheses(context, tokens, locator);
|
||||
}
|
||||
|
||||
if source_type.is_stub() && settings.rules.enabled(Rule::TypeCommentInStub) {
|
||||
if source_type.is_stub() && context.is_rule_enabled(Rule::TypeCommentInStub) {
|
||||
flake8_pyi::rules::type_comment_in_stub(context, locator, comment_ranges);
|
||||
}
|
||||
|
||||
if settings.rules.any_enabled(&[
|
||||
if context.any_rule_enabled(&[
|
||||
Rule::ShebangNotExecutable,
|
||||
Rule::ShebangMissingExecutableFile,
|
||||
Rule::ShebangLeadingWhitespace,
|
||||
Rule::ShebangNotFirstLine,
|
||||
Rule::ShebangMissingPython,
|
||||
]) {
|
||||
flake8_executable::rules::from_tokens(context, path, locator, comment_ranges, settings);
|
||||
flake8_executable::rules::from_tokens(context, path, locator, comment_ranges);
|
||||
}
|
||||
|
||||
if settings.rules.any_enabled(&[
|
||||
if context.any_rule_enabled(&[
|
||||
Rule::InvalidTodoTag,
|
||||
Rule::MissingTodoAuthor,
|
||||
Rule::MissingTodoLink,
|
||||
@@ -167,7 +164,7 @@ pub(crate) fn check_tokens(
|
||||
flake8_fixme::rules::todos(context, &todo_comments);
|
||||
}
|
||||
|
||||
if settings.rules.enabled(Rule::TooManyNewlinesAtEndOfFile) {
|
||||
if context.is_rule_enabled(Rule::TooManyNewlinesAtEndOfFile) {
|
||||
pycodestyle::rules::too_many_newlines_at_end_of_file(context, tokens, cell_offsets);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1028,6 +1028,8 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Ruff, "059") => (RuleGroup::Preview, rules::ruff::rules::UnusedUnpackedVariable),
|
||||
(Ruff, "060") => (RuleGroup::Preview, rules::ruff::rules::InEmptyCollection),
|
||||
(Ruff, "061") => (RuleGroup::Preview, rules::ruff::rules::LegacyFormPytestRaises),
|
||||
(Ruff, "063") => (RuleGroup::Preview, rules::ruff::rules::AccessAnnotationsFromClassDict),
|
||||
(Ruff, "064") => (RuleGroup::Preview, rules::ruff::rules::NonOctalPermissions),
|
||||
(Ruff, "100") => (RuleGroup::Stable, rules::ruff::rules::UnusedNOQA),
|
||||
(Ruff, "101") => (RuleGroup::Stable, rules::ruff::rules::RedirectedNOQA),
|
||||
(Ruff, "102") => (RuleGroup::Preview, rules::ruff::rules::InvalidRuleCode),
|
||||
|
||||
@@ -3,8 +3,8 @@ use anyhow::{Result, bail};
|
||||
use libcst_native::{
|
||||
Arg, Attribute, Call, Comparison, CompoundStatement, Dict, Expression, FormattedString,
|
||||
FormattedStringContent, FormattedStringExpression, FunctionDef, GeneratorExp, If, Import,
|
||||
ImportAlias, ImportFrom, ImportNames, IndentedBlock, Lambda, ListComp, Module, Name,
|
||||
SmallStatement, Statement, Suite, Tuple, With,
|
||||
ImportAlias, ImportFrom, ImportNames, IndentedBlock, Lambda, ListComp, Module, SmallStatement,
|
||||
Statement, Suite, Tuple, With,
|
||||
};
|
||||
use ruff_python_codegen::Stylist;
|
||||
|
||||
@@ -104,14 +104,6 @@ pub(crate) fn match_attribute<'a, 'b>(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn match_name<'a, 'b>(expression: &'a Expression<'b>) -> Result<&'a Name<'b>> {
|
||||
if let Expression::Name(name) = expression {
|
||||
Ok(name)
|
||||
} else {
|
||||
bail!("Expected Expression::Name")
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn match_arg<'a, 'b>(call: &'a Call<'b>) -> Result<&'a Arg<'b>> {
|
||||
if let Some(arg) = call.args.first() {
|
||||
Ok(arg)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user