Compare commits
108 Commits
david/enum
...
dcreager/c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81cfa8ea0a | ||
|
|
6f957475ef | ||
|
|
8235062718 | ||
|
|
b8b4a192c7 | ||
|
|
4dc2ca50ca | ||
|
|
78e1df037c | ||
|
|
5e163e2a5e | ||
|
|
530aa2429b | ||
|
|
d0719e127b | ||
|
|
ce3d84ba1b | ||
|
|
8762a3a868 | ||
|
|
824a5d7d3b | ||
|
|
e2b8b36b7a | ||
|
|
94947cbf65 | ||
|
|
7dccb6a98c | ||
|
|
948f3f856c | ||
|
|
3af0b31de3 | ||
|
|
7df7be5c7d | ||
|
|
2d2841e20d | ||
|
|
14fbc2b167 | ||
|
|
351121c5c5 | ||
|
|
64bcc8db2f | ||
|
|
b0f01ba514 | ||
|
|
3a9341f7be | ||
|
|
739c94f95a | ||
|
|
af8587eabf | ||
|
|
41207ec901 | ||
|
|
bc6e8b58ce | ||
|
|
e4d6b54a16 | ||
|
|
17ee2a28ba | ||
|
|
de77b29798 | ||
|
|
f473f6b6e5 | ||
|
|
736c4ab05a | ||
|
|
8289432252 | ||
|
|
808c94d509 | ||
|
|
b95d22c08e | ||
|
|
f3e66dd503 | ||
|
|
6516db7835 | ||
|
|
03c873765e | ||
|
|
c90707875e | ||
|
|
8e20e589f1 | ||
|
|
113e32b956 | ||
|
|
fdc18eefc3 | ||
|
|
5f40651ae7 | ||
|
|
ea031a3b39 | ||
|
|
93b64daa4a | ||
|
|
74376375e4 | ||
|
|
30f52d8cf5 | ||
|
|
77bc32b9b9 | ||
|
|
1a368b0bf9 | ||
|
|
134435415e | ||
|
|
bc6e105c18 | ||
|
|
6bd413df6c | ||
|
|
85bd961fd3 | ||
|
|
d37911685f | ||
|
|
580577e667 | ||
|
|
dce25da19a | ||
|
|
06cd249a9b | ||
|
|
48d5bd13fa | ||
|
|
e7e7b7bf21 | ||
|
|
57e2e8664f | ||
|
|
18aae21b9a | ||
|
|
d8151f0239 | ||
|
|
2ee56735e2 | ||
|
|
ade6a4262a | ||
|
|
d43e6fb9c6 | ||
|
|
b30d97e5e0 | ||
|
|
5c5d50d57a | ||
|
|
b3a26a50ad | ||
|
|
6a2d358d7a | ||
|
|
b07def07c9 | ||
|
|
2ab1502e51 | ||
|
|
a3f28baab4 | ||
|
|
a71513bae1 | ||
|
|
d2d4b115e3 | ||
|
|
27b03a9d7b | ||
|
|
32c454bb56 | ||
|
|
f6b7418def | ||
|
|
8f8c39c435 | ||
|
|
4739bc8d14 | ||
|
|
7b4103bcb6 | ||
|
|
38049aae12 | ||
|
|
ec3d5ebda2 | ||
|
|
d797592f70 | ||
|
|
eb02aa5676 | ||
|
|
e593761232 | ||
|
|
8979271ea8 | ||
|
|
d1a286226c | ||
|
|
1ba32684da | ||
|
|
70d4b271da | ||
|
|
feaedb1812 | ||
|
|
6237ecb4db | ||
|
|
2a5ace6e55 | ||
|
|
4ecf1d205a | ||
|
|
c5ac998892 | ||
|
|
04a8f64cd7 | ||
|
|
6e00adf308 | ||
|
|
864196b988 | ||
|
|
ae26fa020c | ||
|
|
88a679945c | ||
|
|
941be52358 | ||
|
|
13624ce17f | ||
|
|
edb2f8e997 | ||
|
|
5e6ad849ff | ||
|
|
865a9b3424 | ||
|
|
d449c541cb | ||
|
|
f7c6a6b2d0 | ||
|
|
656273bf3d |
6
.github/CODEOWNERS
vendored
6
.github/CODEOWNERS
vendored
@@ -19,6 +19,10 @@
|
||||
|
||||
# ty
|
||||
/crates/ty* @carljm @MichaReiser @AlexWaygood @sharkdp @dcreager
|
||||
/crates/ruff_db/ @carljm @MichaReiser @AlexWaygood @sharkdp @dcreager
|
||||
/crates/ruff_db/ @carljm @MichaReiser @sharkdp @dcreager
|
||||
/crates/ty_project/ @carljm @MichaReiser @sharkdp @dcreager
|
||||
/crates/ty_server/ @carljm @MichaReiser @sharkdp @dcreager
|
||||
/crates/ty/ @carljm @MichaReiser @sharkdp @dcreager
|
||||
/crates/ty_wasm/ @carljm @MichaReiser @sharkdp @dcreager
|
||||
/scripts/ty_benchmark/ @carljm @MichaReiser @AlexWaygood @sharkdp @dcreager
|
||||
/crates/ty_python_semantic @carljm @AlexWaygood @sharkdp @dcreager
|
||||
|
||||
8
.github/workflows/build-docker.yml
vendored
8
.github/workflows/build-docker.yml
vendored
@@ -63,7 +63,7 @@ jobs:
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
|
||||
with:
|
||||
images: ${{ env.RUFF_BASE_IMG }}
|
||||
# Defining this makes sure the org.opencontainers.image.version OCI label becomes the actual release version and not the branch name
|
||||
@@ -123,7 +123,7 @@ jobs:
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
|
||||
with:
|
||||
images: ${{ env.RUFF_BASE_IMG }}
|
||||
# Order is on purpose such that the label org.opencontainers.image.version has the first pattern with the full version
|
||||
@@ -219,7 +219,7 @@ jobs:
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
|
||||
# ghcr.io prefers index level annotations
|
||||
env:
|
||||
DOCKER_METADATA_ANNOTATIONS_LEVELS: index
|
||||
@@ -266,7 +266,7 @@ jobs:
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
|
||||
env:
|
||||
DOCKER_METADATA_ANNOTATIONS_LEVELS: index
|
||||
with:
|
||||
|
||||
18
.github/workflows/ci.yaml
vendored
18
.github/workflows/ci.yaml
vendored
@@ -240,11 +240,11 @@ jobs:
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@702b1908b5edf30d71a8d1666b724e0f0c6fa035 # v1
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@c99cc51b309eee71a866715cfa08c922f11cf898 # v2.56.19
|
||||
uses: taiki-e/install-action@6064345e6658255e90e9500fdf9a06ab77e6909c # v2.57.6
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Install cargo insta"
|
||||
uses: taiki-e/install-action@c99cc51b309eee71a866715cfa08c922f11cf898 # v2.56.19
|
||||
uses: taiki-e/install-action@6064345e6658255e90e9500fdf9a06ab77e6909c # v2.57.6
|
||||
with:
|
||||
tool: cargo-insta
|
||||
- name: ty mdtests (GitHub annotations)
|
||||
@@ -298,11 +298,11 @@ jobs:
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@702b1908b5edf30d71a8d1666b724e0f0c6fa035 # v1
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@c99cc51b309eee71a866715cfa08c922f11cf898 # v2.56.19
|
||||
uses: taiki-e/install-action@6064345e6658255e90e9500fdf9a06ab77e6909c # v2.57.6
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Install cargo insta"
|
||||
uses: taiki-e/install-action@c99cc51b309eee71a866715cfa08c922f11cf898 # v2.56.19
|
||||
uses: taiki-e/install-action@6064345e6658255e90e9500fdf9a06ab77e6909c # v2.57.6
|
||||
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@c99cc51b309eee71a866715cfa08c922f11cf898 # v2.56.19
|
||||
uses: taiki-e/install-action@6064345e6658255e90e9500fdf9a06ab77e6909c # v2.57.6
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Run tests"
|
||||
@@ -429,7 +429,7 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install cargo-binstall"
|
||||
uses: cargo-bins/cargo-binstall@808dcb1b503398677d089d3216c51ac7cc11e7ab # v1.14.2
|
||||
uses: cargo-bins/cargo-binstall@dd6a0ac24caa1243d18df0f770b941e990e8facc # v1.14.3
|
||||
with:
|
||||
tool: cargo-fuzz@0.11.2
|
||||
- name: "Install cargo-fuzz"
|
||||
@@ -682,7 +682,7 @@ jobs:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: cargo-bins/cargo-binstall@808dcb1b503398677d089d3216c51ac7cc11e7ab # v1.14.2
|
||||
- uses: cargo-bins/cargo-binstall@dd6a0ac24caa1243d18df0f770b941e990e8facc # v1.14.3
|
||||
- run: cargo binstall --no-confirm cargo-shear
|
||||
- run: cargo shear
|
||||
|
||||
@@ -903,7 +903,7 @@ jobs:
|
||||
run: rustup show
|
||||
|
||||
- name: "Install codspeed"
|
||||
uses: taiki-e/install-action@c99cc51b309eee71a866715cfa08c922f11cf898 # v2.56.19
|
||||
uses: taiki-e/install-action@6064345e6658255e90e9500fdf9a06ab77e6909c # v2.57.6
|
||||
with:
|
||||
tool: cargo-codspeed
|
||||
|
||||
@@ -936,7 +936,7 @@ jobs:
|
||||
run: rustup show
|
||||
|
||||
- name: "Install codspeed"
|
||||
uses: taiki-e/install-action@c99cc51b309eee71a866715cfa08c922f11cf898 # v2.56.19
|
||||
uses: taiki-e/install-action@6064345e6658255e90e9500fdf9a06ab77e6909c # v2.57.6
|
||||
with:
|
||||
tool: cargo-codspeed
|
||||
|
||||
|
||||
2
.github/workflows/mypy_primer.yaml
vendored
2
.github/workflows/mypy_primer.yaml
vendored
@@ -83,7 +83,7 @@ jobs:
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
|
||||
|
||||
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
||||
with:
|
||||
workspaces: "ruff"
|
||||
|
||||
|
||||
18
.github/workflows/typing_conformance.yaml
vendored
18
.github/workflows/typing_conformance.yaml
vendored
@@ -24,6 +24,7 @@ env:
|
||||
CARGO_TERM_COLOR: always
|
||||
RUSTUP_MAX_RETRIES: 10
|
||||
RUST_BACKTRACE: 1
|
||||
CONFORMANCE_SUITE_COMMIT: d4f39b27a4a47aac8b6d4019e1b0b5b3156fabdc
|
||||
|
||||
jobs:
|
||||
typing_conformance:
|
||||
@@ -40,13 +41,10 @@ jobs:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
repository: python/typing
|
||||
ref: d4f39b27a4a47aac8b6d4019e1b0b5b3156fabdc
|
||||
ref: ${{ env.CONFORMANCE_SUITE_COMMIT }}
|
||||
path: typing
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@e92bafb6253dcd438e0484186d7669ea7a8ca1cc # v6.4.3
|
||||
|
||||
- uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
|
||||
with:
|
||||
workspaces: "ruff"
|
||||
@@ -64,14 +62,13 @@ jobs:
|
||||
cd ruff
|
||||
|
||||
echo "new commit"
|
||||
git checkout -b new_commit "${{ github.event.pull_request.head.sha }}"
|
||||
git rev-list --format=%s --max-count=1 new_commit
|
||||
git rev-list --format=%s --max-count=1 "$GITHUB_SHA"
|
||||
cargo build --release --bin ty
|
||||
mv target/release/ty ty-new
|
||||
|
||||
echo "old commit (merge base)"
|
||||
MERGE_BASE="$(git merge-base "$GITHUB_SHA" "origin/$GITHUB_BASE_REF")"
|
||||
git checkout -b old_commit "$MERGE_BASE"
|
||||
echo "old commit (merge base)"
|
||||
git rev-list --format=%s --max-count=1 old_commit
|
||||
cargo build --release --bin ty
|
||||
mv target/release/ty ty-old
|
||||
@@ -95,6 +92,7 @@ jobs:
|
||||
fi
|
||||
|
||||
echo ${{ github.event.number }} > pr-number
|
||||
echo "${CONFORMANCE_SUITE_COMMIT}" > conformance-suite-commit
|
||||
|
||||
- name: Upload diff
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
@@ -107,3 +105,9 @@ jobs:
|
||||
with:
|
||||
name: pr-number
|
||||
path: pr-number
|
||||
|
||||
- name: Upload conformance suite commit
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: conformance-suite-commit
|
||||
path: conformance-suite-commit
|
||||
|
||||
@@ -32,6 +32,14 @@ jobs:
|
||||
echo "pr-number=$(<pr-number)" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- uses: dawidd6/action-download-artifact@20319c5641d495c8a52e688b7dc5fada6c3a9fbc # v8
|
||||
name: Download typing conformance suite commit
|
||||
with:
|
||||
name: conformance-suite-commit
|
||||
run_id: ${{ github.event.workflow_run.id || github.event.inputs.workflow_run_id }}
|
||||
if_no_artifact_found: ignore
|
||||
allow_forks: true
|
||||
|
||||
- uses: dawidd6/action-download-artifact@20319c5641d495c8a52e688b7dc5fada6c3a9fbc # v8
|
||||
name: "Download typing_conformance results"
|
||||
id: download-typing_conformance_diff
|
||||
@@ -61,7 +69,14 @@ jobs:
|
||||
# subsequent runs
|
||||
echo '<!-- generated-comment typing_conformance_diagnostics_diff -->' >> comment.txt
|
||||
|
||||
echo '## Diagnostic diff on typing conformance tests' >> comment.txt
|
||||
if [[ -f conformance-suite-commit ]]
|
||||
then
|
||||
echo "## Diagnostic diff on [typing conformance tests](https://github.com/python/typing/tree/$(<conformance-suite-commit)/conformance)" >> comment.txt
|
||||
else
|
||||
echo "conformance-suite-commit file not found"
|
||||
echo "## Diagnostic diff on typing conformance tests" >> comment.txt
|
||||
fi
|
||||
|
||||
if [ -s "pr/typing_conformance_diagnostics_diff/typing_conformance_diagnostics.diff" ]; then
|
||||
echo '<details>' >> comment.txt
|
||||
echo '<summary>Changes were detected when running ty on typing conformance tests</summary>' >> comment.txt
|
||||
|
||||
@@ -81,7 +81,7 @@ repos:
|
||||
pass_filenames: false # This makes it a lot faster
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.12.5
|
||||
rev: v0.12.7
|
||||
hooks:
|
||||
- id: ruff-format
|
||||
- id: ruff-check
|
||||
|
||||
30
CHANGELOG.md
30
CHANGELOG.md
@@ -1,5 +1,35 @@
|
||||
# Changelog
|
||||
|
||||
## 0.12.7
|
||||
|
||||
This is a follow-up release to 0.12.6. Because of an issue in the package metadata, 0.12.6 failed to publish fully to PyPI and has been yanked. Similarly, there is no GitHub release or Git tag for 0.12.6. The contents of the 0.12.7 release are identical to 0.12.6, except for the updated metadata.
|
||||
|
||||
## 0.12.6
|
||||
|
||||
### Preview features
|
||||
|
||||
- \[`flake8-commas`\] Add support for trailing comma checks in type parameter lists (`COM812`, `COM819`) ([#19390](https://github.com/astral-sh/ruff/pull/19390))
|
||||
- \[`pylint`\] Implement auto-fix for `missing-maxsplit-arg` (`PLC0207`) ([#19387](https://github.com/astral-sh/ruff/pull/19387))
|
||||
- \[`ruff`\] Offer fixes for `RUF039` in more cases ([#19065](https://github.com/astral-sh/ruff/pull/19065))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Support `.pyi` files in ruff analyze graph ([#19611](https://github.com/astral-sh/ruff/pull/19611))
|
||||
- \[`flake8-pyi`\] Preserve inline comment in ellipsis removal (`PYI013`) ([#19399](https://github.com/astral-sh/ruff/pull/19399))
|
||||
- \[`perflint`\] Ignore rule if target is `global` or `nonlocal` (`PERF401`) ([#19539](https://github.com/astral-sh/ruff/pull/19539))
|
||||
- \[`pyupgrade`\] Fix `UP030` to avoid modifying double curly braces in format strings ([#19378](https://github.com/astral-sh/ruff/pull/19378))
|
||||
- \[`refurb`\] Ignore decorated functions for `FURB118` ([#19339](https://github.com/astral-sh/ruff/pull/19339))
|
||||
- \[`refurb`\] Mark `int` and `bool` cases for `Decimal.from_float` as safe fixes (`FURB164`) ([#19468](https://github.com/astral-sh/ruff/pull/19468))
|
||||
- \[`ruff`\] Fix `RUF033` for named default expressions ([#19115](https://github.com/astral-sh/ruff/pull/19115))
|
||||
|
||||
### Rule changes
|
||||
|
||||
- \[`flake8-blind-except`\] Change `BLE001` to permit `logging.critical(..., exc_info=True)` ([#19520](https://github.com/astral-sh/ruff/pull/19520))
|
||||
|
||||
### Performance
|
||||
|
||||
- Add support for specifying minimum dots in detected string imports ([#19538](https://github.com/astral-sh/ruff/pull/19538))
|
||||
|
||||
## 0.12.5
|
||||
|
||||
### Preview features
|
||||
|
||||
343
Cargo.lock
generated
343
Cargo.lock
generated
@@ -4,9 +4,9 @@ version = 4
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.0"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
@@ -77,52 +77,52 @@ checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-lossy"
|
||||
version = "1.1.3"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "934ff8719effd2023a48cf63e69536c1c3ced9d3895068f6f5cc9a4ff845e59b"
|
||||
checksum = "04d3a5dc826f84d0ea11882bb8054ff7f3d482602e11bb181101303a279ea01f"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.6"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.2"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||
checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-svg"
|
||||
version = "0.1.7"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3607949e9f6de49ea4bafe12f5e4fd73613ebf24795e48587302a8cc0e4bb35"
|
||||
checksum = "0a43964079ef399480603125d5afae2b219aceffb77478956e25f17b9bc3435c"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"anstyle-lossy",
|
||||
"anstyle-parse",
|
||||
"html-escape",
|
||||
"unicode-width 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.7"
|
||||
version = "3.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
|
||||
checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
@@ -210,9 +210,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
@@ -301,9 +301,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.17.0"
|
||||
version = "3.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
||||
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
@@ -337,18 +337,18 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||
|
||||
[[package]]
|
||||
name = "castaway"
|
||||
version = "0.2.3"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5"
|
||||
checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a"
|
||||
dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.23"
|
||||
version = "1.2.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766"
|
||||
checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
@@ -357,9 +357,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
@@ -408,9 +408,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.41"
|
||||
version = "4.5.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9"
|
||||
checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -418,9 +418,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.41"
|
||||
version = "4.5.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d"
|
||||
checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -431,9 +431,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "4.5.50"
|
||||
version = "4.5.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c91d3baa3bcd889d60e6ef28874126a0b384fd225ab83aa6d8a801c519194ce1"
|
||||
checksum = "a5abde44486daf70c5be8b8f8f1b66c49f86236edf6fa2abadb4d961c4c6229a"
|
||||
dependencies = [
|
||||
"clap",
|
||||
]
|
||||
@@ -451,9 +451,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete_nushell"
|
||||
version = "4.5.5"
|
||||
version = "4.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6a8b1593457dfc2fe539002b795710d022dc62a65bf15023f039f9760c7b18a"
|
||||
checksum = "0a0c951694691e65bf9d421d597d68416c22de9632e884c28412cb8cd8b73dce"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"clap_complete",
|
||||
@@ -473,9 +473,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.4"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||
checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
|
||||
|
||||
[[package]]
|
||||
name = "clearscreen"
|
||||
@@ -492,9 +492,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "codspeed"
|
||||
version = "3.0.2"
|
||||
version = "3.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "922018102595f6668cdd09c03f4bff2d951ce2318c6dca4fe11bdcb24b65b2bf"
|
||||
checksum = "d29180405ab3b37bb020246ea66bf8ae233708766fd59581ae929feaef10ce91"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode 1.3.3",
|
||||
@@ -510,9 +510,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "codspeed-criterion-compat"
|
||||
version = "3.0.2"
|
||||
version = "3.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d8ad82d2383cb74995f58993cbdd2914aed57b2f91f46580310dd81dc3d05a"
|
||||
checksum = "2454d874ca820ffd71273565530ad318f413195bbc99dce6c958ca07db362c63"
|
||||
dependencies = [
|
||||
"codspeed",
|
||||
"codspeed-criterion-compat-walltime",
|
||||
@@ -521,9 +521,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "codspeed-criterion-compat-walltime"
|
||||
version = "3.0.2"
|
||||
version = "3.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61badaa6c452d192a29f8387147888f0ab358553597c3fe9bf8a162ef7c2fa64"
|
||||
checksum = "093a9383cdd1a5a0bd1a47cdafb49ae0c6dcd0793c8fb8f79768bab423128c9c"
|
||||
dependencies = [
|
||||
"anes",
|
||||
"cast",
|
||||
@@ -546,9 +546,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "codspeed-divan-compat"
|
||||
version = "3.0.2"
|
||||
version = "3.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3acf1d6fe367c2ff5ff136ca723f678490c3691d59d7f2b83d5e53b7b25ac91e"
|
||||
checksum = "e1c73bce1e3f47738bf74a6b58b72a49b4f40c837ce420d8d65a270298592aac"
|
||||
dependencies = [
|
||||
"codspeed",
|
||||
"codspeed-divan-compat-macros",
|
||||
@@ -557,9 +557,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "codspeed-divan-compat-macros"
|
||||
version = "3.0.2"
|
||||
version = "3.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcfa2013d7bee54a497d0e1410751d5de690fd67a3e9eb728ca049b6a3d16d0b"
|
||||
checksum = "ea51dd8add7eba774cc24b4a98324252ac3ec092ccb5f07e52bbe1cb72a6d373"
|
||||
dependencies = [
|
||||
"divan-macros",
|
||||
"itertools 0.14.0",
|
||||
@@ -571,9 +571,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "codspeed-divan-compat-walltime"
|
||||
version = "3.0.2"
|
||||
version = "3.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e513100fb0e7ba02fb3824546ecd2abfb8f334262f0972225b463aad07f99ff0"
|
||||
checksum = "417e9edfc4b0289d4b9b48e62f98c6168d5e30c0e612b2935e394b0dd930fe83"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"clap",
|
||||
@@ -586,15 +586,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "collection_literals"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "186dce98367766de751c42c4f03970fc60fc012296e706ccbb9d5df9b6c1e271"
|
||||
checksum = "26b3f65b8fb8e88ba339f7d23a390fe1b0896217da05e2a66c584c9b29a91df8"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
@@ -704,9 +704,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.4.2"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
|
||||
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
@@ -810,9 +810,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.3"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929"
|
||||
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
@@ -1000,9 +1000,9 @@ checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
|
||||
|
||||
[[package]]
|
||||
name = "dyn-clone"
|
||||
version = "1.0.19"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005"
|
||||
checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
@@ -1030,12 +1030,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.12"
|
||||
version = "0.3.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
|
||||
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1096,9 +1096,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
|
||||
checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
@@ -1184,11 +1184,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "getopts"
|
||||
version = "0.2.21"
|
||||
version = "0.2.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
|
||||
checksum = "cba6ae63eb948698e300f645f87c70f76630d505f23b8907cf1e193ee85048c1"
|
||||
dependencies = [
|
||||
"unicode-width 0.1.14",
|
||||
"unicode-width 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1199,7 +1199,7 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1290,15 +1290,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.9"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08"
|
||||
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
|
||||
|
||||
[[package]]
|
||||
name = "home"
|
||||
@@ -1391,9 +1385,9 @@ checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties"
|
||||
version = "2.0.0"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2549ca8c7241c82f59c80ba2a6f415d931c5b58d24fb8412caa1a1f02c49139a"
|
||||
checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
@@ -1407,9 +1401,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties_data"
|
||||
version = "2.0.0"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8197e866e47b68f8f7d95249e172903bec06004b18b2937f1095d40a0c57de04"
|
||||
checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider"
|
||||
@@ -1621,7 +1615,7 @@ version = "0.4.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
|
||||
dependencies = [
|
||||
"hermit-abi 0.5.1",
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
@@ -1811,9 +1805,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.3"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
||||
checksum = "360e552c93fa0e8152ab463bc4c4837fce76a225df11dfaeea66c313de5e61f7"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"libc",
|
||||
@@ -1980,23 +1974,23 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.8"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
|
||||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
|
||||
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys 0.52.0",
|
||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2007,9 +2001,9 @@ checksum = "308d96db8debc727c3fd9744aac51751243420e46edf401010908da7f8d5e57c"
|
||||
|
||||
[[package]]
|
||||
name = "newtype-uuid"
|
||||
version = "1.2.1"
|
||||
version = "1.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee3224f0e8be7c2a1ebc77ef9c3eecb90f55c6594399ee825de964526b3c9056"
|
||||
checksum = "a17d82edb1c8a6c20c238747ae7aae9181133e766bc92cd2556fdd764407d0d1"
|
||||
dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
@@ -2056,9 +2050,9 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
|
||||
|
||||
[[package]]
|
||||
name = "notify"
|
||||
version = "8.1.0"
|
||||
version = "8.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3163f59cd3fa0e9ef8c32f242966a7b9994fd7378366099593e0e73077cd8c97"
|
||||
checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"fsevent-sys",
|
||||
@@ -2099,11 +2093,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.16.0"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
|
||||
checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b"
|
||||
dependencies = [
|
||||
"hermit-abi 0.3.9",
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@@ -2113,6 +2107,12 @@ version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
|
||||
|
||||
[[package]]
|
||||
name = "oorandom"
|
||||
version = "11.1.5"
|
||||
@@ -2137,9 +2137,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "os_pipe"
|
||||
version = "1.2.1"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982"
|
||||
checksum = "db335f4760b14ead6290116f2427bf33a14d4f0617d49f78a246de10c1831224"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
@@ -2147,9 +2147,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "7.1.0"
|
||||
version = "7.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c86e2db86dd008b4c88c77a9bb83d9286bf77204e255bb3fda3b2eebcae66b62"
|
||||
checksum = "63eceb7b5d757011a87d08eb2123db15d87fb0c281f65d101ce30a1e96c3ad5c"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -2162,9 +2162,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.3"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
|
||||
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
@@ -2172,9 +2172,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.10"
|
||||
version = "0.9.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
||||
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
@@ -2289,9 +2289,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.8.0"
|
||||
version = "2.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6"
|
||||
checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"thiserror 2.0.12",
|
||||
@@ -2300,9 +2300,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_derive"
|
||||
version = "2.8.0"
|
||||
version = "2.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5"
|
||||
checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_generator",
|
||||
@@ -2310,9 +2310,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_generator"
|
||||
version = "2.8.0"
|
||||
version = "2.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841"
|
||||
checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_meta",
|
||||
@@ -2323,11 +2323,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_meta"
|
||||
version = "2.8.0"
|
||||
version = "2.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0"
|
||||
checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"pest",
|
||||
"sha2",
|
||||
]
|
||||
@@ -2384,9 +2383,9 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.11.0"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
|
||||
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic-util"
|
||||
@@ -2572,9 +2571,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.2.0"
|
||||
version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "radium"
|
||||
@@ -2663,9 +2662,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.12"
|
||||
version = "0.5.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
|
||||
checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
]
|
||||
@@ -2744,7 +2743,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.12.5"
|
||||
version = "0.12.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argfile",
|
||||
@@ -2796,7 +2795,7 @@ dependencies = [
|
||||
"test-case",
|
||||
"thiserror 2.0.12",
|
||||
"tikv-jemallocator",
|
||||
"toml 0.9.2",
|
||||
"toml 0.9.4",
|
||||
"tracing",
|
||||
"walkdir",
|
||||
"wild",
|
||||
@@ -2812,7 +2811,7 @@ dependencies = [
|
||||
"ruff_annotate_snippets",
|
||||
"serde",
|
||||
"snapbox",
|
||||
"toml 0.9.2",
|
||||
"toml 0.9.4",
|
||||
"tryfn",
|
||||
"unicode-width 0.2.1",
|
||||
]
|
||||
@@ -2891,7 +2890,6 @@ dependencies = [
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"ty_static",
|
||||
"unicode-width 0.2.1",
|
||||
"web-time",
|
||||
"zip",
|
||||
]
|
||||
@@ -2929,7 +2927,7 @@ dependencies = [
|
||||
"similar",
|
||||
"strum",
|
||||
"tempfile",
|
||||
"toml 0.9.2",
|
||||
"toml 0.9.4",
|
||||
"tracing",
|
||||
"tracing-indicatif",
|
||||
"tracing-subscriber",
|
||||
@@ -2997,7 +2995,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_linter"
|
||||
version = "0.12.5"
|
||||
version = "0.12.7"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"anyhow",
|
||||
@@ -3050,7 +3048,7 @@ dependencies = [
|
||||
"tempfile",
|
||||
"test-case",
|
||||
"thiserror 2.0.12",
|
||||
"toml 0.9.2",
|
||||
"toml 0.9.4",
|
||||
"typed-arena",
|
||||
"unicode-normalization",
|
||||
"unicode-width 0.2.1",
|
||||
@@ -3300,7 +3298,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"shellexpand",
|
||||
"thiserror 2.0.12",
|
||||
"toml 0.9.2",
|
||||
"toml 0.9.4",
|
||||
"tracing",
|
||||
"tracing-log",
|
||||
"tracing-subscriber",
|
||||
@@ -3329,7 +3327,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_wasm"
|
||||
version = "0.12.5"
|
||||
version = "0.12.7"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"console_log",
|
||||
@@ -3390,7 +3388,7 @@ dependencies = [
|
||||
"shellexpand",
|
||||
"strum",
|
||||
"tempfile",
|
||||
"toml 0.9.2",
|
||||
"toml 0.9.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3417,22 +3415,22 @@ checksum = "781442f29170c5c93b7185ad559492601acdc71d5bb0706f5868094f45cfcd08"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.0.7"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
|
||||
checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.20"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
|
||||
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
@@ -3443,7 +3441,7 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
[[package]]
|
||||
name = "salsa"
|
||||
version = "0.23.0"
|
||||
source = "git+https://github.com/salsa-rs/salsa?rev=dba66f1a37acca014c2402f231ed5b361bd7d8fe#dba66f1a37acca014c2402f231ed5b361bd7d8fe"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=d66fe331d546216132ace503512b94d5c68d2c50#d66fe331d546216132ace503512b94d5c68d2c50"
|
||||
dependencies = [
|
||||
"boxcar",
|
||||
"compact_str",
|
||||
@@ -3456,7 +3454,6 @@ dependencies = [
|
||||
"inventory",
|
||||
"parking_lot",
|
||||
"portable-atomic",
|
||||
"rayon",
|
||||
"rustc-hash",
|
||||
"salsa-macro-rules",
|
||||
"salsa-macros",
|
||||
@@ -3468,12 +3465,12 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "salsa-macro-rules"
|
||||
version = "0.23.0"
|
||||
source = "git+https://github.com/salsa-rs/salsa?rev=dba66f1a37acca014c2402f231ed5b361bd7d8fe#dba66f1a37acca014c2402f231ed5b361bd7d8fe"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=d66fe331d546216132ace503512b94d5c68d2c50#d66fe331d546216132ace503512b94d5c68d2c50"
|
||||
|
||||
[[package]]
|
||||
name = "salsa-macros"
|
||||
version = "0.23.0"
|
||||
source = "git+https://github.com/salsa-rs/salsa?rev=dba66f1a37acca014c2402f231ed5b361bd7d8fe#dba66f1a37acca014c2402f231ed5b361bd7d8fe"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=d66fe331d546216132ace503512b94d5c68d2c50#d66fe331d546216132ace503512b94d5c68d2c50"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3570,9 +3567,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.141"
|
||||
version = "1.0.142"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3"
|
||||
checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
@@ -3940,12 +3937,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.8"
|
||||
version = "1.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
|
||||
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4026,9 +4022,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.9.2"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed0aee96c12fa71097902e0bb061a5e1ebd766a6636bb605ba401c45c1650eac"
|
||||
checksum = "41ae868b5a0f67631c14589f7e250c1ea2c574ee5ba21c6c8dd4b1485705a5a1"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
@@ -4099,9 +4095,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.28"
|
||||
version = "0.1.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
|
||||
checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -4131,9 +4127,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-indicatif"
|
||||
version = "0.3.11"
|
||||
version = "0.3.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c714cc8fc46db04fcfddbd274c6ef59bebb1b435155984e7c6e89c3ce66f200"
|
||||
checksum = "e1983afead46ff13a3c93581e0cec31d20b29efdd22cbdaa8b9f850eccf2c352"
|
||||
dependencies = [
|
||||
"indicatif",
|
||||
"tracing",
|
||||
@@ -4190,6 +4186,7 @@ dependencies = [
|
||||
"argfile",
|
||||
"clap",
|
||||
"clap_complete_command",
|
||||
"clearscreen",
|
||||
"colored 3.0.0",
|
||||
"crossbeam",
|
||||
"ctrlc",
|
||||
@@ -4206,7 +4203,7 @@ dependencies = [
|
||||
"ruff_python_trivia",
|
||||
"salsa",
|
||||
"tempfile",
|
||||
"toml 0.9.2",
|
||||
"toml 0.9.4",
|
||||
"tracing",
|
||||
"tracing-flame",
|
||||
"tracing-subscriber",
|
||||
@@ -4267,7 +4264,7 @@ dependencies = [
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror 2.0.12",
|
||||
"toml 0.9.2",
|
||||
"toml 0.9.4",
|
||||
"tracing",
|
||||
"ty_python_semantic",
|
||||
"ty_vendored",
|
||||
@@ -4390,7 +4387,7 @@ dependencies = [
|
||||
"smallvec",
|
||||
"tempfile",
|
||||
"thiserror 2.0.12",
|
||||
"toml 0.9.2",
|
||||
"toml 0.9.4",
|
||||
"tracing",
|
||||
"ty_python_semantic",
|
||||
"ty_static",
|
||||
@@ -4714,9 +4711,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
@@ -4895,9 +4892,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.61.1"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46ec44dc15085cea82cf9c78f85a9114c463a369786585ad2882d1ff0b0acf40"
|
||||
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
@@ -4930,37 +4927,28 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.1"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
|
||||
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.3.3"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b895b5356fc36103d0f64dd1e94dfa7ac5633f1c9dd6e80fe9ec4adef69e09d"
|
||||
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a7ab927b2637c19b3dbe0965e75d8f2d30bdd697a1516191cad2ec4df8fb28a"
|
||||
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
@@ -4976,7 +4964,7 @@ version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets 0.53.2",
|
||||
"windows-targets 0.53.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4997,10 +4985,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.2"
|
||||
version = "0.53.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
|
||||
checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows_aarch64_gnullvm 0.53.0",
|
||||
"windows_aarch64_msvc 0.53.0",
|
||||
"windows_i686_gnu 0.53.0",
|
||||
@@ -5109,9 +5098,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.10"
|
||||
version = "0.7.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec"
|
||||
checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -5178,18 +5167,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.25"
|
||||
version = "0.8.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
|
||||
checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.25"
|
||||
version = "0.8.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
|
||||
checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
@@ -141,7 +141,12 @@ regex-automata = { version = "0.4.9" }
|
||||
rustc-hash = { version = "2.0.0" }
|
||||
rustc-stable-hash = { version = "0.1.2" }
|
||||
# When updating salsa, make sure to also update the revision in `fuzz/Cargo.toml`
|
||||
salsa = { git = "https://github.com/salsa-rs/salsa", rev = "dba66f1a37acca014c2402f231ed5b361bd7d8fe" }
|
||||
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "d66fe331d546216132ace503512b94d5c68d2c50", default-features = false, features = [
|
||||
"compact_str",
|
||||
"macros",
|
||||
"salsa_unstable",
|
||||
"inventory",
|
||||
] }
|
||||
schemars = { version = "0.8.16" }
|
||||
seahash = { version = "4.1.0" }
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
|
||||
@@ -148,8 +148,8 @@ curl -LsSf https://astral.sh/ruff/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/install.ps1 | iex"
|
||||
|
||||
# For a specific version.
|
||||
curl -LsSf https://astral.sh/ruff/0.12.5/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/0.12.5/install.ps1 | iex"
|
||||
curl -LsSf https://astral.sh/ruff/0.12.7/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/0.12.7/install.ps1 | iex"
|
||||
```
|
||||
|
||||
You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff),
|
||||
@@ -182,7 +182,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff
|
||||
```yaml
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.12.5
|
||||
rev: v0.12.7
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff-check
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.12.5"
|
||||
version = "0.12.7"
|
||||
publish = true
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
|
||||
@@ -95,6 +95,6 @@ is stricter, which could affect the suggested fix. See [this FAQ section](https:
|
||||
## References
|
||||
- [Python documentation: `import`](https://docs.python.org/3/reference/simple_stmts.html#the-import-statement)
|
||||
- [Python documentation: `importlib.util.find_spec`](https://docs.python.org/3/library/importlib.html#importlib.util.find_spec)
|
||||
- [Typing documentation: interface conventions](https://typing.python.org/en/latest/source/libraries.html#library-interface-public-and-private-symbols)
|
||||
- [Typing documentation: interface conventions](https://typing.python.org/en/latest/spec/distributing.html#library-interface-public-and-private-symbols)
|
||||
|
||||
----- stderr -----
|
||||
|
||||
@@ -22,11 +22,17 @@ exit_code: 1
|
||||
"description": "`os` imported but unused",
|
||||
"fingerprint": "4dbad37161e65c72",
|
||||
"location": {
|
||||
"lines": {
|
||||
"begin": 1,
|
||||
"end": 1
|
||||
},
|
||||
"path": "input.py"
|
||||
"path": "input.py",
|
||||
"positions": {
|
||||
"begin": {
|
||||
"column": 8,
|
||||
"line": 1
|
||||
},
|
||||
"end": {
|
||||
"column": 10,
|
||||
"line": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"severity": "major"
|
||||
},
|
||||
@@ -35,11 +41,17 @@ exit_code: 1
|
||||
"description": "Undefined name `y`",
|
||||
"fingerprint": "7af59862a085230",
|
||||
"location": {
|
||||
"lines": {
|
||||
"begin": 2,
|
||||
"end": 2
|
||||
},
|
||||
"path": "input.py"
|
||||
"path": "input.py",
|
||||
"positions": {
|
||||
"begin": {
|
||||
"column": 5,
|
||||
"line": 2
|
||||
},
|
||||
"end": {
|
||||
"column": 6,
|
||||
"line": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
"severity": "major"
|
||||
},
|
||||
@@ -48,11 +60,17 @@ exit_code: 1
|
||||
"description": "Cannot use `match` statement on Python 3.9 (syntax was added in Python 3.10)",
|
||||
"fingerprint": "e558cec859bb66e8",
|
||||
"location": {
|
||||
"lines": {
|
||||
"begin": 3,
|
||||
"end": 3
|
||||
},
|
||||
"path": "input.py"
|
||||
"path": "input.py",
|
||||
"positions": {
|
||||
"begin": {
|
||||
"column": 1,
|
||||
"line": 3
|
||||
},
|
||||
"end": {
|
||||
"column": 6,
|
||||
"line": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
"severity": "major"
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ exit_code: 1
|
||||
"rules": [
|
||||
{
|
||||
"fullDescription": {
|
||||
"text": "## What it does\nChecks for unused imports.\n\n## Why is this bad?\nUnused imports add a performance overhead at runtime, and risk creating\nimport cycles. They also increase the cognitive load of reading the code.\n\nIf an import statement is used to check for the availability or existence\nof a module, consider using `importlib.util.find_spec` instead.\n\nIf an import statement is used to re-export a symbol as part of a module's\npublic interface, consider using a \"redundant\" import alias, which\ninstructs Ruff (and other tools) to respect the re-export, and avoid\nmarking it as unused, as in:\n\n```python\nfrom module import member as member\n```\n\nAlternatively, you can use `__all__` to declare a symbol as part of the module's\ninterface, as in:\n\n```python\n# __init__.py\nimport some_module\n\n__all__ = [\"some_module\"]\n```\n\n## Fix safety\n\nFixes to remove unused imports are safe, except in `__init__.py` files.\n\nApplying fixes to `__init__.py` files is currently in preview. The fix offered depends on the\ntype of the unused import. Ruff will suggest a safe fix to export first-party imports with\neither a redundant alias or, if already present in the file, an `__all__` entry. If multiple\n`__all__` declarations are present, Ruff will not offer a fix. Ruff will suggest an unsafe fix\nto remove third-party and standard library imports -- the fix is unsafe because the module's\ninterface changes.\n\n## Example\n\n```python\nimport numpy as np # unused import\n\n\ndef area(radius):\n return 3.14 * radius**2\n```\n\nUse instead:\n\n```python\ndef area(radius):\n return 3.14 * radius**2\n```\n\nTo check the availability of a module, use `importlib.util.find_spec`:\n\n```python\nfrom importlib.util import find_spec\n\nif find_spec(\"numpy\") is not None:\n print(\"numpy is installed\")\nelse:\n print(\"numpy is not installed\")\n```\n\n## Preview\nWhen [preview](https://docs.astral.sh/ruff/preview/) is enabled,\nthe criterion for determining whether an import is first-party\nis stricter, which could affect the suggested fix. See [this FAQ section](https://docs.astral.sh/ruff/faq/#how-does-ruff-determine-which-of-my-imports-are-first-party-third-party-etc) for more details.\n\n## Options\n- `lint.ignore-init-module-imports`\n- `lint.pyflakes.allowed-unused-imports`\n\n## References\n- [Python documentation: `import`](https://docs.python.org/3/reference/simple_stmts.html#the-import-statement)\n- [Python documentation: `importlib.util.find_spec`](https://docs.python.org/3/library/importlib.html#importlib.util.find_spec)\n- [Typing documentation: interface conventions](https://typing.python.org/en/latest/source/libraries.html#library-interface-public-and-private-symbols)\n"
|
||||
"text": "## What it does\nChecks for unused imports.\n\n## Why is this bad?\nUnused imports add a performance overhead at runtime, and risk creating\nimport cycles. They also increase the cognitive load of reading the code.\n\nIf an import statement is used to check for the availability or existence\nof a module, consider using `importlib.util.find_spec` instead.\n\nIf an import statement is used to re-export a symbol as part of a module's\npublic interface, consider using a \"redundant\" import alias, which\ninstructs Ruff (and other tools) to respect the re-export, and avoid\nmarking it as unused, as in:\n\n```python\nfrom module import member as member\n```\n\nAlternatively, you can use `__all__` to declare a symbol as part of the module's\ninterface, as in:\n\n```python\n# __init__.py\nimport some_module\n\n__all__ = [\"some_module\"]\n```\n\n## Fix safety\n\nFixes to remove unused imports are safe, except in `__init__.py` files.\n\nApplying fixes to `__init__.py` files is currently in preview. The fix offered depends on the\ntype of the unused import. Ruff will suggest a safe fix to export first-party imports with\neither a redundant alias or, if already present in the file, an `__all__` entry. If multiple\n`__all__` declarations are present, Ruff will not offer a fix. Ruff will suggest an unsafe fix\nto remove third-party and standard library imports -- the fix is unsafe because the module's\ninterface changes.\n\n## Example\n\n```python\nimport numpy as np # unused import\n\n\ndef area(radius):\n return 3.14 * radius**2\n```\n\nUse instead:\n\n```python\ndef area(radius):\n return 3.14 * radius**2\n```\n\nTo check the availability of a module, use `importlib.util.find_spec`:\n\n```python\nfrom importlib.util import find_spec\n\nif find_spec(\"numpy\") is not None:\n print(\"numpy is installed\")\nelse:\n print(\"numpy is not installed\")\n```\n\n## Preview\nWhen [preview](https://docs.astral.sh/ruff/preview/) is enabled,\nthe criterion for determining whether an import is first-party\nis stricter, which could affect the suggested fix. See [this FAQ section](https://docs.astral.sh/ruff/faq/#how-does-ruff-determine-which-of-my-imports-are-first-party-third-party-etc) for more details.\n\n## Options\n- `lint.ignore-init-module-imports`\n- `lint.pyflakes.allowed-unused-imports`\n\n## References\n- [Python documentation: `import`](https://docs.python.org/3/reference/simple_stmts.html#the-import-statement)\n- [Python documentation: `importlib.util.find_spec`](https://docs.python.org/3/library/importlib.html#importlib.util.find_spec)\n- [Typing documentation: interface conventions](https://typing.python.org/en/latest/spec/distributing.html#library-interface-public-and-private-symbols)\n"
|
||||
},
|
||||
"help": {
|
||||
"text": "`{name}` imported but unused; consider using `importlib.util.find_spec` to test for availability"
|
||||
|
||||
@@ -351,6 +351,41 @@ fn benchmark_many_tuple_assignments(criterion: &mut Criterion) {
|
||||
});
|
||||
}
|
||||
|
||||
fn benchmark_tuple_implicit_instance_attributes(criterion: &mut Criterion) {
|
||||
setup_rayon();
|
||||
|
||||
criterion.bench_function("ty_micro[many_tuple_assignments]", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| {
|
||||
// This is a regression benchmark for a case that used to hang:
|
||||
// https://github.com/astral-sh/ty/issues/765
|
||||
setup_micro_case(
|
||||
r#"
|
||||
from typing import Any
|
||||
|
||||
class A:
|
||||
foo: tuple[Any, ...]
|
||||
|
||||
class B(A):
|
||||
def __init__(self, parent: "C", x: tuple[Any]):
|
||||
self.foo = parent.foo + x
|
||||
|
||||
class C(A):
|
||||
def __init__(self, parent: B, x: tuple[Any]):
|
||||
self.foo = parent.foo + x
|
||||
"#,
|
||||
)
|
||||
},
|
||||
|case| {
|
||||
let Case { db, .. } = case;
|
||||
let result = db.check();
|
||||
assert_eq!(result.len(), 0);
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn benchmark_complex_constrained_attributes_1(criterion: &mut Criterion) {
|
||||
setup_rayon();
|
||||
|
||||
@@ -630,6 +665,7 @@ criterion_group!(
|
||||
micro,
|
||||
benchmark_many_string_assignments,
|
||||
benchmark_many_tuple_assignments,
|
||||
benchmark_tuple_implicit_instance_attributes,
|
||||
benchmark_complex_constrained_attributes_1,
|
||||
benchmark_complex_constrained_attributes_2,
|
||||
benchmark_many_enum_members,
|
||||
|
||||
@@ -42,7 +42,6 @@ serde_json = { workspace = true, optional = true }
|
||||
thiserror = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tracing-subscriber = { workspace = true, optional = true }
|
||||
unicode-width = { workspace = true }
|
||||
zip = { workspace = true }
|
||||
|
||||
[target.'cfg(target_arch="wasm32")'.dependencies]
|
||||
|
||||
@@ -21,7 +21,7 @@ mod stylesheet;
|
||||
/// characteristics in the inputs given to the tool. Typically, but not always,
|
||||
/// a characteristic is a deficiency. An example of a characteristic that is
|
||||
/// _not_ a deficiency is the `reveal_type` diagnostic for our type checker.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, get_size2::GetSize)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, get_size2::GetSize)]
|
||||
pub struct Diagnostic {
|
||||
/// The actual diagnostic.
|
||||
///
|
||||
@@ -479,7 +479,7 @@ impl Diagnostic {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, get_size2::GetSize)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, get_size2::GetSize)]
|
||||
struct DiagnosticInner {
|
||||
id: DiagnosticId,
|
||||
severity: Severity,
|
||||
@@ -555,7 +555,7 @@ impl Eq for RenderingSortKey<'_> {}
|
||||
/// Currently, the order in which sub-diagnostics are rendered relative to one
|
||||
/// another (for a single parent diagnostic) is the order in which they were
|
||||
/// attached to the diagnostic.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, get_size2::GetSize)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, get_size2::GetSize)]
|
||||
pub struct SubDiagnostic {
|
||||
/// Like with `Diagnostic`, we box the `SubDiagnostic` to make it
|
||||
/// pointer-sized.
|
||||
@@ -659,7 +659,7 @@ impl SubDiagnostic {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, get_size2::GetSize)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, get_size2::GetSize)]
|
||||
struct SubDiagnosticInner {
|
||||
severity: SubDiagnosticSeverity,
|
||||
message: DiagnosticMessage,
|
||||
@@ -687,7 +687,7 @@ struct SubDiagnosticInner {
|
||||
///
|
||||
/// Messages attached to annotations should also be as brief and specific as
|
||||
/// possible. Long messages could negative impact the quality of rendering.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, get_size2::GetSize)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, get_size2::GetSize)]
|
||||
pub struct Annotation {
|
||||
/// The span of this annotation, corresponding to some subsequence of the
|
||||
/// user's input that we want to highlight.
|
||||
@@ -807,7 +807,7 @@ impl Annotation {
|
||||
///
|
||||
/// These tags are used to provide additional information about the annotation.
|
||||
/// and are passed through to the language server protocol.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, get_size2::GetSize)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, get_size2::GetSize)]
|
||||
pub enum DiagnosticTag {
|
||||
/// Unused or unnecessary code. Used for unused parameters, unreachable code, etc.
|
||||
Unnecessary,
|
||||
@@ -1016,7 +1016,7 @@ impl std::fmt::Display for DiagnosticId {
|
||||
///
|
||||
/// This enum presents a unified interface to these two types for the sake of creating [`Span`]s and
|
||||
/// emitting diagnostics from both ty and ruff.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, get_size2::GetSize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize)]
|
||||
pub enum UnifiedFile {
|
||||
Ty(File),
|
||||
Ruff(SourceFile),
|
||||
@@ -1080,7 +1080,7 @@ impl DiagnosticSource {
|
||||
/// It consists of a `File` and an optional range into that file. When the
|
||||
/// range isn't present, it semantically implies that the diagnostic refers to
|
||||
/// the entire file. For example, when the file should be executable but isn't.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, get_size2::GetSize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize)]
|
||||
pub struct Span {
|
||||
file: UnifiedFile,
|
||||
range: Option<TextRange>,
|
||||
@@ -1158,7 +1158,7 @@ impl From<crate::files::FileRange> for Span {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, get_size2::GetSize)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash, get_size2::GetSize)]
|
||||
pub enum Severity {
|
||||
Info,
|
||||
Warning,
|
||||
@@ -1193,7 +1193,7 @@ impl Severity {
|
||||
/// This type only exists to add an additional `Help` severity that isn't present in `Severity` or
|
||||
/// used for main diagnostics. If we want to add `Severity::Help` in the future, this type could be
|
||||
/// deleted and the two combined again.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, get_size2::GetSize)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash, get_size2::GetSize)]
|
||||
pub enum SubDiagnosticSeverity {
|
||||
Help,
|
||||
Info,
|
||||
@@ -1428,7 +1428,7 @@ impl std::fmt::Display for ConciseMessage<'_> {
|
||||
/// In most cases, callers shouldn't need to use this. Instead, there is
|
||||
/// a blanket trait implementation for `IntoDiagnosticMessage` for
|
||||
/// anything that implements `std::fmt::Display`.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, get_size2::GetSize)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash, get_size2::GetSize)]
|
||||
pub struct DiagnosticMessage(Box<str>);
|
||||
|
||||
impl DiagnosticMessage {
|
||||
|
||||
@@ -585,8 +585,7 @@ impl<'r> RenderableSnippet<'r> {
|
||||
let EscapedSourceCode {
|
||||
text: snippet,
|
||||
annotations,
|
||||
} = replace_whitespace_and_unprintable(snippet, annotations)
|
||||
.fix_up_empty_spans_after_line_terminator();
|
||||
} = replace_unprintable(snippet, annotations).fix_up_empty_spans_after_line_terminator();
|
||||
|
||||
RenderableSnippet {
|
||||
snippet,
|
||||
@@ -828,13 +827,18 @@ fn relativize_path<'p>(cwd: &SystemPath, path: &'p str) -> &'p str {
|
||||
path
|
||||
}
|
||||
|
||||
/// Given some source code and annotation ranges, this routine replaces tabs
|
||||
/// with ASCII whitespace, and unprintable characters with printable
|
||||
/// representations of them.
|
||||
/// Given some source code and annotation ranges, this routine replaces
|
||||
/// unprintable characters with printable representations of them.
|
||||
///
|
||||
/// The source code and annotations returned are updated to reflect changes made
|
||||
/// to the source code (if any).
|
||||
fn replace_whitespace_and_unprintable<'r>(
|
||||
///
|
||||
/// We don't need to normalize whitespace, such as converting tabs to spaces,
|
||||
/// because `annotate-snippets` handles that internally. Similarly, it's safe to
|
||||
/// modify the annotation ranges by inserting 3-byte Unicode replacements
|
||||
/// because `annotate-snippets` will account for their actual width when
|
||||
/// rendering and displaying the column to the user.
|
||||
fn replace_unprintable<'r>(
|
||||
source: &'r str,
|
||||
mut annotations: Vec<RenderableAnnotation<'r>>,
|
||||
) -> EscapedSourceCode<'r> {
|
||||
@@ -866,48 +870,17 @@ fn replace_whitespace_and_unprintable<'r>(
|
||||
}
|
||||
};
|
||||
|
||||
const TAB_SIZE: usize = 4;
|
||||
let mut width = 0;
|
||||
let mut column = 0;
|
||||
let mut last_end = 0;
|
||||
let mut result = String::new();
|
||||
for (index, c) in source.char_indices() {
|
||||
let old_width = width;
|
||||
match c {
|
||||
'\n' | '\r' => {
|
||||
width = 0;
|
||||
column = 0;
|
||||
}
|
||||
'\t' => {
|
||||
let tab_offset = TAB_SIZE - (column % TAB_SIZE);
|
||||
width += tab_offset;
|
||||
column += tab_offset;
|
||||
if let Some(printable) = unprintable_replacement(c) {
|
||||
result.push_str(&source[last_end..index]);
|
||||
|
||||
let tab_width =
|
||||
u32::try_from(width - old_width).expect("small width because of tab size");
|
||||
result.push_str(&source[last_end..index]);
|
||||
let len = printable.text_len().to_u32();
|
||||
update_ranges(result.text_len().to_usize(), len);
|
||||
|
||||
update_ranges(result.text_len().to_usize(), tab_width);
|
||||
|
||||
for _ in 0..tab_width {
|
||||
result.push(' ');
|
||||
}
|
||||
last_end = index + 1;
|
||||
}
|
||||
_ => {
|
||||
width += unicode_width::UnicodeWidthChar::width(c).unwrap_or(0);
|
||||
column += 1;
|
||||
|
||||
if let Some(printable) = unprintable_replacement(c) {
|
||||
result.push_str(&source[last_end..index]);
|
||||
|
||||
let len = printable.text_len().to_u32();
|
||||
update_ranges(result.text_len().to_usize(), len);
|
||||
|
||||
result.push(printable);
|
||||
last_end = index + 1;
|
||||
}
|
||||
}
|
||||
result.push(printable);
|
||||
last_end = index + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -177,4 +177,25 @@ print()
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Ensure that the header column matches the column in the user's input, even if we've replaced
|
||||
/// tabs with spaces for rendering purposes.
|
||||
#[test]
|
||||
fn tab_replacement() {
|
||||
let mut env = TestEnvironment::new();
|
||||
env.add("example.py", "def foo():\n\treturn 1");
|
||||
env.format(DiagnosticFormat::Full);
|
||||
|
||||
let diagnostic = env.err().primary("example.py", "2:1", "2:9", "").build();
|
||||
|
||||
insta::assert_snapshot!(env.render(&diagnostic), @r"
|
||||
error[test-diagnostic]: main diagnostic message
|
||||
--> example.py:2:2
|
||||
|
|
||||
1 | def foo():
|
||||
2 | return 1
|
||||
| ^^^^^^^^
|
||||
|
|
||||
");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ use crate::source::source_text;
|
||||
/// reflected in the changed AST offsets.
|
||||
/// The other reason is that Ruff's AST doesn't implement `Eq` which Salsa requires
|
||||
/// for determining if a query result is unchanged.
|
||||
#[salsa::tracked(returns(ref), no_eq, heap_size=get_size2::GetSize::get_heap_size)]
|
||||
#[salsa::tracked(returns(ref), no_eq, heap_size=get_size2::heap_size)]
|
||||
pub fn parsed_module(db: &dyn Db, file: File) -> ParsedModule {
|
||||
let _span = tracing::trace_span!("parsed_module", ?file).entered();
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::Db;
|
||||
use crate::files::{File, FilePath};
|
||||
|
||||
/// Reads the source text of a python text file (must be valid UTF8) or notebook.
|
||||
#[salsa::tracked(heap_size=get_size2::GetSize::get_heap_size)]
|
||||
#[salsa::tracked(heap_size=get_size2::heap_size)]
|
||||
pub fn source_text(db: &dyn Db, file: File) -> SourceText {
|
||||
let path = file.path(db);
|
||||
let _span = tracing::trace_span!("source_text", file = %path).entered();
|
||||
@@ -69,21 +69,21 @@ impl SourceText {
|
||||
pub fn as_str(&self) -> &str {
|
||||
match &self.inner.kind {
|
||||
SourceTextKind::Text(source) => source,
|
||||
SourceTextKind::Notebook(notebook) => notebook.source_code(),
|
||||
SourceTextKind::Notebook { notebook } => notebook.source_code(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the underlying notebook if this is a notebook file.
|
||||
pub fn as_notebook(&self) -> Option<&Notebook> {
|
||||
match &self.inner.kind {
|
||||
SourceTextKind::Notebook(notebook) => Some(notebook),
|
||||
SourceTextKind::Notebook { notebook } => Some(notebook),
|
||||
SourceTextKind::Text(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if this is a notebook source file.
|
||||
pub fn is_notebook(&self) -> bool {
|
||||
matches!(&self.inner.kind, SourceTextKind::Notebook(_))
|
||||
matches!(&self.inner.kind, SourceTextKind::Notebook { .. })
|
||||
}
|
||||
|
||||
/// Returns `true` if there was an error when reading the content of the file.
|
||||
@@ -108,7 +108,7 @@ impl std::fmt::Debug for SourceText {
|
||||
SourceTextKind::Text(text) => {
|
||||
dbg.field(text);
|
||||
}
|
||||
SourceTextKind::Notebook(notebook) => {
|
||||
SourceTextKind::Notebook { notebook } => {
|
||||
dbg.field(notebook);
|
||||
}
|
||||
}
|
||||
@@ -123,23 +123,15 @@ struct SourceTextInner {
|
||||
read_error: Option<SourceTextError>,
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
#[derive(Eq, PartialEq, get_size2::GetSize)]
|
||||
enum SourceTextKind {
|
||||
Text(String),
|
||||
Notebook(Box<Notebook>),
|
||||
}
|
||||
|
||||
impl get_size2::GetSize for SourceTextKind {
|
||||
fn get_heap_size(&self) -> usize {
|
||||
match self {
|
||||
SourceTextKind::Text(text) => text.get_heap_size(),
|
||||
// TODO: The `get-size` derive does not support ignoring enum variants.
|
||||
//
|
||||
// Jupyter notebooks are not very relevant for memory profiling, and contain
|
||||
// arbitrary JSON values that do not implement the `GetSize` trait.
|
||||
SourceTextKind::Notebook(_) => 0,
|
||||
}
|
||||
}
|
||||
Notebook {
|
||||
// Jupyter notebooks are not very relevant for memory profiling, and contain
|
||||
// arbitrary JSON values that do not implement the `GetSize` trait.
|
||||
#[get_size(ignore)]
|
||||
notebook: Box<Notebook>,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<String> for SourceTextKind {
|
||||
@@ -150,7 +142,9 @@ impl From<String> for SourceTextKind {
|
||||
|
||||
impl From<Notebook> for SourceTextKind {
|
||||
fn from(notebook: Notebook) -> Self {
|
||||
SourceTextKind::Notebook(Box::new(notebook))
|
||||
SourceTextKind::Notebook {
|
||||
notebook: Box::new(notebook),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +157,7 @@ pub enum SourceTextError {
|
||||
}
|
||||
|
||||
/// Computes the [`LineIndex`] for `file`.
|
||||
#[salsa::tracked(heap_size=get_size2::GetSize::get_heap_size)]
|
||||
#[salsa::tracked(heap_size=get_size2::heap_size)]
|
||||
pub fn line_index(db: &dyn Db, file: File) -> LineIndex {
|
||||
let _span = tracing::trace_span!("line_index", ?file).entered();
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ pub enum IsolationLevel {
|
||||
}
|
||||
|
||||
/// A collection of [`Edit`] elements to be applied to a source file.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, get_size2::GetSize)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash, get_size2::GetSize)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Fix {
|
||||
/// The [`Edit`] elements to be applied, sorted by [`Edit::start`] in ascending order.
|
||||
|
||||
@@ -69,7 +69,7 @@ impl<'a> Resolver<'a> {
|
||||
}
|
||||
|
||||
/// Resolves a module name to a module.
|
||||
fn resolve_module(&self, module_name: &ModuleName) -> Option<&'a FilePath> {
|
||||
pub(crate) fn resolve_module(&self, module_name: &ModuleName) -> Option<&'a FilePath> {
|
||||
let module = resolve_module(self.db, module_name)?;
|
||||
Some(module.file(self.db)?.path(self.db))
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_linter"
|
||||
version = "0.12.5"
|
||||
version = "0.12.7"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
|
||||
@@ -89,3 +89,14 @@ print(1)
|
||||
# ///
|
||||
#
|
||||
# Foobar
|
||||
|
||||
|
||||
# Regression tests for https://github.com/astral-sh/ruff/issues/19713
|
||||
|
||||
# mypy: ignore-errors
|
||||
# pyright: ignore-errors
|
||||
# pyrefly: ignore-errors
|
||||
# ty: ignore[unresolved-import]
|
||||
# pyrefly: ignore[unused-import]
|
||||
|
||||
print(1)
|
||||
|
||||
@@ -162,3 +162,86 @@ except Exception:
|
||||
exception("An error occurred")
|
||||
else:
|
||||
exception("An error occurred")
|
||||
|
||||
# Test tuple exceptions
|
||||
try:
|
||||
pass
|
||||
except (Exception,):
|
||||
pass
|
||||
|
||||
try:
|
||||
pass
|
||||
except (Exception, ValueError):
|
||||
pass
|
||||
|
||||
try:
|
||||
pass
|
||||
except (ValueError, Exception):
|
||||
pass
|
||||
|
||||
try:
|
||||
pass
|
||||
except (ValueError, Exception) as e:
|
||||
print(e)
|
||||
|
||||
try:
|
||||
pass
|
||||
except (BaseException, TypeError):
|
||||
pass
|
||||
|
||||
try:
|
||||
pass
|
||||
except (TypeError, BaseException):
|
||||
pass
|
||||
|
||||
try:
|
||||
pass
|
||||
except (Exception, BaseException):
|
||||
pass
|
||||
|
||||
try:
|
||||
pass
|
||||
except (BaseException, Exception):
|
||||
pass
|
||||
|
||||
# Test nested tuples
|
||||
try:
|
||||
pass
|
||||
except ((Exception, ValueError), TypeError):
|
||||
pass
|
||||
|
||||
try:
|
||||
pass
|
||||
except (ValueError, (BaseException, TypeError)):
|
||||
pass
|
||||
|
||||
# Test valid tuple exceptions (should not trigger)
|
||||
try:
|
||||
pass
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
try:
|
||||
pass
|
||||
except (OSError, FileNotFoundError):
|
||||
pass
|
||||
|
||||
try:
|
||||
pass
|
||||
except (OSError, FileNotFoundError) as e:
|
||||
print(e)
|
||||
|
||||
try:
|
||||
pass
|
||||
except (Exception, ValueError):
|
||||
critical("...", exc_info=True)
|
||||
|
||||
try:
|
||||
pass
|
||||
except (Exception, ValueError):
|
||||
raise
|
||||
|
||||
try:
|
||||
pass
|
||||
except (Exception, ValueError) as e:
|
||||
raise e
|
||||
|
||||
@@ -88,3 +88,25 @@ def f_multi_line_string2():
|
||||
example="example"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def raise_typing_cast_exception():
|
||||
import typing
|
||||
raise typing.cast("Exception", None)
|
||||
|
||||
|
||||
def f_typing_cast_excluded():
|
||||
from typing import cast
|
||||
raise cast(RuntimeError, "This should not trigger EM101")
|
||||
|
||||
|
||||
def f_typing_cast_excluded_import():
|
||||
import typing
|
||||
raise typing.cast(RuntimeError, "This should not trigger EM101")
|
||||
|
||||
|
||||
def f_typing_cast_excluded_aliased():
|
||||
from typing import cast as my_cast
|
||||
raise my_cast(RuntimeError, "This should not trigger EM101")
|
||||
|
||||
|
||||
|
||||
@@ -39,6 +39,11 @@ class NonEmptyWithInit:
|
||||
pass
|
||||
|
||||
|
||||
class NonEmptyChildWithInlineComment:
|
||||
value: int
|
||||
... # preserve me
|
||||
|
||||
|
||||
class EmptyClass:
|
||||
...
|
||||
|
||||
|
||||
@@ -38,6 +38,10 @@ class NonEmptyWithInit:
|
||||
def __init__():
|
||||
pass
|
||||
|
||||
class NonEmptyChildWithInlineComment:
|
||||
value: int
|
||||
... # preserve me
|
||||
|
||||
# Not violations
|
||||
|
||||
class EmptyClass: ...
|
||||
|
||||
@@ -129,4 +129,35 @@ print(" x ".rsplit(maxsplit=0))
|
||||
print(" x ".rsplit(maxsplit=0))
|
||||
print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
print(" x ".rsplit(maxsplit=0))
|
||||
print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/19581 - embedded quotes in raw strings
|
||||
r"""simple@example.com
|
||||
very.common@example.com
|
||||
FirstName.LastName@EasierReading.org
|
||||
x@example.com
|
||||
long.email-address-with-hyphens@and.subdomains.example.com
|
||||
user.name+tag+sorting@example.com
|
||||
name/surname@example.com
|
||||
xample@s.example
|
||||
" "@example.org
|
||||
"john..doe"@example.org
|
||||
mailhost!username@example.org
|
||||
"very.(),:;<>[]\".VERY.\"very@\\ \"very\".unusual"@strange.example.com
|
||||
user%example.com@example.org
|
||||
user-@example.org
|
||||
I❤️CHOCOLATE@example.com
|
||||
this\ still\"not\\allowed@example.com
|
||||
stellyamburrr985@example.com
|
||||
Abc.123@example.com
|
||||
user+mailbox/department=shipping@example.com
|
||||
!#$%&'*+-/=?^_`.{|}~@example.com
|
||||
"Abc@def"@example.com
|
||||
"Fred\ Bloggs"@example.com
|
||||
"Joe.\\Blow"@example.com""".split("\n")
|
||||
|
||||
|
||||
r"""first
|
||||
'no need' to escape
|
||||
"swap" quote style
|
||||
"use' ugly triple quotes""".split("\n")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from pathlib import Path, PurePath
|
||||
from pathlib import Path, PurePath, PosixPath, PurePosixPath, WindowsPath, PureWindowsPath
|
||||
from pathlib import Path as pth
|
||||
|
||||
|
||||
@@ -68,3 +68,11 @@ Path(".", "folder")
|
||||
PurePath(".", "folder")
|
||||
|
||||
Path()
|
||||
|
||||
from importlib.metadata import PackagePath
|
||||
|
||||
_ = PosixPath(".")
|
||||
_ = PurePosixPath(".")
|
||||
_ = WindowsPath(".")
|
||||
_ = PureWindowsPath(".")
|
||||
_ = PackagePath(".")
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
"""Hello, world!"""\
|
||||
|
||||
x = 1; y = 2
|
||||
@@ -59,3 +59,7 @@ kwargs = {x: x for x in range(10)}
|
||||
"{1}_{0}".format(1, 2, *args)
|
||||
|
||||
"{1}_{0}".format(1, 2)
|
||||
|
||||
r"\d{{1,2}} {0}".format(42)
|
||||
|
||||
"{{{0}}}".format(123)
|
||||
|
||||
@@ -52,3 +52,7 @@ f"{repr(lambda: 1)}"
|
||||
f"{repr(x := 2)}"
|
||||
|
||||
f"{str(object=3)}"
|
||||
|
||||
f"{str(x for x in [])}"
|
||||
|
||||
f"{str((x for x in []))}"
|
||||
|
||||
@@ -590,6 +590,16 @@ impl<'a> Checker<'a> {
|
||||
member,
|
||||
})
|
||||
}
|
||||
|
||||
/// Return the [`LintContext`] for the current analysis.
|
||||
///
|
||||
/// Note that you should always prefer calling methods like `settings`, `report_diagnostic`, or
|
||||
/// `is_rule_enabled` directly on [`Checker`] when possible. This method exists only for the
|
||||
/// rare cases where rules or helper functions need to be accessed by both a `Checker` and a
|
||||
/// `LintContext` in different analysis phases.
|
||||
pub(crate) const fn context(&self) -> &'a LintContext<'a> {
|
||||
self.context
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct TypingImporter<'a, 'b> {
|
||||
|
||||
@@ -56,13 +56,19 @@ impl<'a> Insertion<'a> {
|
||||
stylist: &Stylist,
|
||||
) -> Insertion<'static> {
|
||||
// Skip over any docstrings.
|
||||
let mut location = if let Some(location) = match_docstring_end(body) {
|
||||
let mut location = if let Some(mut location) = match_docstring_end(body) {
|
||||
// If the first token after the docstring is a semicolon, insert after the semicolon as
|
||||
// an inline statement.
|
||||
if let Some(offset) = match_semicolon(locator.after(location)) {
|
||||
return Insertion::inline(" ", location.add(offset).add(TextSize::of(';')), ";");
|
||||
}
|
||||
|
||||
// If the first token after the docstring is a continuation character (i.e. "\"), advance
|
||||
// an additional row to prevent inserting in the same logical line.
|
||||
if match_continuation(locator.after(location)).is_some() {
|
||||
location = locator.full_line_end(location);
|
||||
}
|
||||
|
||||
// Otherwise, advance to the next row.
|
||||
locator.full_line_end(location)
|
||||
} else {
|
||||
@@ -363,6 +369,16 @@ mod tests {
|
||||
Insertion::own_line("", TextSize::from(20), "\n")
|
||||
);
|
||||
|
||||
let contents = r#"
|
||||
"""Hello, world!"""\
|
||||
|
||||
"#
|
||||
.trim_start();
|
||||
assert_eq!(
|
||||
insert(contents)?,
|
||||
Insertion::own_line("", TextSize::from(22), "\n")
|
||||
);
|
||||
|
||||
let contents = r"
|
||||
x = 1
|
||||
"
|
||||
|
||||
@@ -61,22 +61,17 @@ impl Serialize for SerializedMessages<'_> {
|
||||
let mut fingerprints = HashSet::<u64>::with_capacity(self.diagnostics.len());
|
||||
|
||||
for diagnostic in self.diagnostics {
|
||||
let start_location = diagnostic.expect_ruff_start_location();
|
||||
let end_location = diagnostic.expect_ruff_end_location();
|
||||
|
||||
let filename = diagnostic.expect_ruff_filename();
|
||||
let lines = if self.context.is_notebook(&filename) {
|
||||
|
||||
let (start_location, end_location) = if self.context.is_notebook(&filename) {
|
||||
// We can't give a reasonable location for the structured formats,
|
||||
// so we show one that's clearly a fallback
|
||||
json!({
|
||||
"begin": 1,
|
||||
"end": 1
|
||||
})
|
||||
Default::default()
|
||||
} else {
|
||||
json!({
|
||||
"begin": start_location.line,
|
||||
"end": end_location.line
|
||||
})
|
||||
(
|
||||
diagnostic.expect_ruff_start_location(),
|
||||
diagnostic.expect_ruff_end_location(),
|
||||
)
|
||||
};
|
||||
|
||||
let path = self.project_dir.as_ref().map_or_else(
|
||||
@@ -111,8 +106,11 @@ impl Serialize for SerializedMessages<'_> {
|
||||
"fingerprint": format!("{:x}", message_fingerprint),
|
||||
"location": {
|
||||
"path": path,
|
||||
"lines": lines
|
||||
}
|
||||
"positions": {
|
||||
"begin": start_location,
|
||||
"end": end_location,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
s.serialize_element(&value)?;
|
||||
|
||||
@@ -8,11 +8,17 @@ expression: redact_fingerprint(&content)
|
||||
"description": "`os` imported but unused",
|
||||
"fingerprint": "<redacted>",
|
||||
"location": {
|
||||
"lines": {
|
||||
"begin": 1,
|
||||
"end": 1
|
||||
},
|
||||
"path": "fib.py"
|
||||
"path": "fib.py",
|
||||
"positions": {
|
||||
"begin": {
|
||||
"column": 8,
|
||||
"line": 1
|
||||
},
|
||||
"end": {
|
||||
"column": 10,
|
||||
"line": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"severity": "major"
|
||||
},
|
||||
@@ -21,11 +27,17 @@ expression: redact_fingerprint(&content)
|
||||
"description": "Local variable `x` is assigned to but never used",
|
||||
"fingerprint": "<redacted>",
|
||||
"location": {
|
||||
"lines": {
|
||||
"begin": 6,
|
||||
"end": 6
|
||||
},
|
||||
"path": "fib.py"
|
||||
"path": "fib.py",
|
||||
"positions": {
|
||||
"begin": {
|
||||
"column": 5,
|
||||
"line": 6
|
||||
},
|
||||
"end": {
|
||||
"column": 6,
|
||||
"line": 6
|
||||
}
|
||||
}
|
||||
},
|
||||
"severity": "major"
|
||||
},
|
||||
@@ -34,11 +46,17 @@ expression: redact_fingerprint(&content)
|
||||
"description": "Undefined name `a`",
|
||||
"fingerprint": "<redacted>",
|
||||
"location": {
|
||||
"lines": {
|
||||
"begin": 1,
|
||||
"end": 1
|
||||
},
|
||||
"path": "undef.py"
|
||||
"path": "undef.py",
|
||||
"positions": {
|
||||
"begin": {
|
||||
"column": 4,
|
||||
"line": 1
|
||||
},
|
||||
"end": {
|
||||
"column": 5,
|
||||
"line": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"severity": "major"
|
||||
}
|
||||
|
||||
@@ -8,11 +8,17 @@ expression: redact_fingerprint(&content)
|
||||
"description": "Expected one or more symbol names after import",
|
||||
"fingerprint": "<redacted>",
|
||||
"location": {
|
||||
"lines": {
|
||||
"begin": 1,
|
||||
"end": 2
|
||||
},
|
||||
"path": "syntax_errors.py"
|
||||
"path": "syntax_errors.py",
|
||||
"positions": {
|
||||
"begin": {
|
||||
"column": 15,
|
||||
"line": 1
|
||||
},
|
||||
"end": {
|
||||
"column": 1,
|
||||
"line": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
"severity": "major"
|
||||
},
|
||||
@@ -21,11 +27,17 @@ expression: redact_fingerprint(&content)
|
||||
"description": "Expected ')', found newline",
|
||||
"fingerprint": "<redacted>",
|
||||
"location": {
|
||||
"lines": {
|
||||
"begin": 3,
|
||||
"end": 4
|
||||
},
|
||||
"path": "syntax_errors.py"
|
||||
"path": "syntax_errors.py",
|
||||
"positions": {
|
||||
"begin": {
|
||||
"column": 12,
|
||||
"line": 3
|
||||
},
|
||||
"end": {
|
||||
"column": 1,
|
||||
"line": 4
|
||||
}
|
||||
}
|
||||
},
|
||||
"severity": "major"
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ expression: value
|
||||
"rules": [
|
||||
{
|
||||
"fullDescription": {
|
||||
"text": "## What it does\nChecks for unused imports.\n\n## Why is this bad?\nUnused imports add a performance overhead at runtime, and risk creating\nimport cycles. They also increase the cognitive load of reading the code.\n\nIf an import statement is used to check for the availability or existence\nof a module, consider using `importlib.util.find_spec` instead.\n\nIf an import statement is used to re-export a symbol as part of a module's\npublic interface, consider using a \"redundant\" import alias, which\ninstructs Ruff (and other tools) to respect the re-export, and avoid\nmarking it as unused, as in:\n\n```python\nfrom module import member as member\n```\n\nAlternatively, you can use `__all__` to declare a symbol as part of the module's\ninterface, as in:\n\n```python\n# __init__.py\nimport some_module\n\n__all__ = [\"some_module\"]\n```\n\n## Fix safety\n\nFixes to remove unused imports are safe, except in `__init__.py` files.\n\nApplying fixes to `__init__.py` files is currently in preview. The fix offered depends on the\ntype of the unused import. Ruff will suggest a safe fix to export first-party imports with\neither a redundant alias or, if already present in the file, an `__all__` entry. If multiple\n`__all__` declarations are present, Ruff will not offer a fix. Ruff will suggest an unsafe fix\nto remove third-party and standard library imports -- the fix is unsafe because the module's\ninterface changes.\n\n## Example\n\n```python\nimport numpy as np # unused import\n\n\ndef area(radius):\n return 3.14 * radius**2\n```\n\nUse instead:\n\n```python\ndef area(radius):\n return 3.14 * radius**2\n```\n\nTo check the availability of a module, use `importlib.util.find_spec`:\n\n```python\nfrom importlib.util import find_spec\n\nif find_spec(\"numpy\") is not None:\n print(\"numpy is installed\")\nelse:\n print(\"numpy is not installed\")\n```\n\n## Preview\nWhen [preview](https://docs.astral.sh/ruff/preview/) is enabled,\nthe criterion for determining whether an import is first-party\nis stricter, which could affect the suggested fix. See [this FAQ section](https://docs.astral.sh/ruff/faq/#how-does-ruff-determine-which-of-my-imports-are-first-party-third-party-etc) for more details.\n\n## Options\n- `lint.ignore-init-module-imports`\n- `lint.pyflakes.allowed-unused-imports`\n\n## References\n- [Python documentation: `import`](https://docs.python.org/3/reference/simple_stmts.html#the-import-statement)\n- [Python documentation: `importlib.util.find_spec`](https://docs.python.org/3/library/importlib.html#importlib.util.find_spec)\n- [Typing documentation: interface conventions](https://typing.python.org/en/latest/source/libraries.html#library-interface-public-and-private-symbols)\n"
|
||||
"text": "## What it does\nChecks for unused imports.\n\n## Why is this bad?\nUnused imports add a performance overhead at runtime, and risk creating\nimport cycles. They also increase the cognitive load of reading the code.\n\nIf an import statement is used to check for the availability or existence\nof a module, consider using `importlib.util.find_spec` instead.\n\nIf an import statement is used to re-export a symbol as part of a module's\npublic interface, consider using a \"redundant\" import alias, which\ninstructs Ruff (and other tools) to respect the re-export, and avoid\nmarking it as unused, as in:\n\n```python\nfrom module import member as member\n```\n\nAlternatively, you can use `__all__` to declare a symbol as part of the module's\ninterface, as in:\n\n```python\n# __init__.py\nimport some_module\n\n__all__ = [\"some_module\"]\n```\n\n## Fix safety\n\nFixes to remove unused imports are safe, except in `__init__.py` files.\n\nApplying fixes to `__init__.py` files is currently in preview. The fix offered depends on the\ntype of the unused import. Ruff will suggest a safe fix to export first-party imports with\neither a redundant alias or, if already present in the file, an `__all__` entry. If multiple\n`__all__` declarations are present, Ruff will not offer a fix. Ruff will suggest an unsafe fix\nto remove third-party and standard library imports -- the fix is unsafe because the module's\ninterface changes.\n\n## Example\n\n```python\nimport numpy as np # unused import\n\n\ndef area(radius):\n return 3.14 * radius**2\n```\n\nUse instead:\n\n```python\ndef area(radius):\n return 3.14 * radius**2\n```\n\nTo check the availability of a module, use `importlib.util.find_spec`:\n\n```python\nfrom importlib.util import find_spec\n\nif find_spec(\"numpy\") is not None:\n print(\"numpy is installed\")\nelse:\n print(\"numpy is not installed\")\n```\n\n## Preview\nWhen [preview](https://docs.astral.sh/ruff/preview/) is enabled,\nthe criterion for determining whether an import is first-party\nis stricter, which could affect the suggested fix. See [this FAQ section](https://docs.astral.sh/ruff/faq/#how-does-ruff-determine-which-of-my-imports-are-first-party-third-party-etc) for more details.\n\n## Options\n- `lint.ignore-init-module-imports`\n- `lint.pyflakes.allowed-unused-imports`\n\n## References\n- [Python documentation: `import`](https://docs.python.org/3/reference/simple_stmts.html#the-import-statement)\n- [Python documentation: `importlib.util.find_spec`](https://docs.python.org/3/library/importlib.html#importlib.util.find_spec)\n- [Typing documentation: interface conventions](https://typing.python.org/en/latest/spec/distributing.html#library-interface-public-and-private-symbols)\n"
|
||||
},
|
||||
"help": {
|
||||
"text": "`{name}` imported but unused; consider using `importlib.util.find_spec` to test for availability"
|
||||
|
||||
@@ -13,7 +13,6 @@ use ruff_notebook::NotebookIndex;
|
||||
use ruff_source_file::OneIndexed;
|
||||
use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||
|
||||
use crate::line_width::{IndentWidth, LineWidthBuilder};
|
||||
use crate::message::diff::Diff;
|
||||
use crate::message::{Emitter, EmitterContext};
|
||||
use crate::settings::types::UnsafeFixes;
|
||||
@@ -229,7 +228,7 @@ impl Display for MessageCodeFrame<'_> {
|
||||
let start_offset = source_code.line_start(start_index);
|
||||
let end_offset = source_code.line_end(end_index);
|
||||
|
||||
let source = replace_whitespace_and_unprintable(
|
||||
let source = replace_unprintable(
|
||||
source_code.slice(TextRange::new(start_offset, end_offset)),
|
||||
self.message.expect_range() - start_offset,
|
||||
)
|
||||
@@ -272,16 +271,20 @@ impl Display for MessageCodeFrame<'_> {
|
||||
}
|
||||
|
||||
/// Given some source code and an annotation range, this routine replaces
|
||||
/// tabs with ASCII whitespace, and unprintable characters with printable
|
||||
/// representations of them.
|
||||
/// unprintable characters with printable representations of them.
|
||||
///
|
||||
/// The source code returned has an annotation that is updated to reflect
|
||||
/// changes made to the source code (if any).
|
||||
fn replace_whitespace_and_unprintable(source: &str, annotation_range: TextRange) -> SourceCode {
|
||||
///
|
||||
/// We don't need to normalize whitespace, such as converting tabs to spaces,
|
||||
/// because `annotate-snippets` handles that internally. Similarly, it's safe to
|
||||
/// modify the annotation ranges by inserting 3-byte Unicode replacements
|
||||
/// because `annotate-snippets` will account for their actual width when
|
||||
/// rendering and displaying the column to the user.
|
||||
fn replace_unprintable(source: &str, annotation_range: TextRange) -> SourceCode {
|
||||
let mut result = String::new();
|
||||
let mut last_end = 0;
|
||||
let mut range = annotation_range;
|
||||
let mut line_width = LineWidthBuilder::new(IndentWidth::default());
|
||||
|
||||
// Updates the range given by the caller whenever a single byte (at
|
||||
// `index` in `source`) is replaced with `len` bytes.
|
||||
@@ -310,19 +313,7 @@ fn replace_whitespace_and_unprintable(source: &str, annotation_range: TextRange)
|
||||
};
|
||||
|
||||
for (index, c) in source.char_indices() {
|
||||
let old_width = line_width.get();
|
||||
line_width = line_width.add_char(c);
|
||||
|
||||
if matches!(c, '\t') {
|
||||
let tab_width = u32::try_from(line_width.get() - old_width)
|
||||
.expect("small width because of tab size");
|
||||
result.push_str(&source[last_end..index]);
|
||||
for _ in 0..tab_width {
|
||||
result.push(' ');
|
||||
}
|
||||
last_end = index + 1;
|
||||
update_range(index, tab_width);
|
||||
} else if let Some(printable) = unprintable_replacement(c) {
|
||||
if let Some(printable) = unprintable_replacement(c) {
|
||||
result.push_str(&source[last_end..index]);
|
||||
result.push(printable);
|
||||
last_end = index + 1;
|
||||
|
||||
@@ -21,6 +21,7 @@ static ALLOWLIST_REGEX: LazyLock<Regex> = LazyLock::new(|| {
|
||||
(?:
|
||||
# Case-sensitive
|
||||
pyright
|
||||
| pyrefly
|
||||
| mypy:
|
||||
| type:\s*ignore
|
||||
| SPDX-License-Identifier:
|
||||
|
||||
@@ -75,6 +75,22 @@ impl Violation for BlindExcept {
|
||||
}
|
||||
}
|
||||
|
||||
fn contains_blind_exception<'a>(
|
||||
semantic: &'a SemanticModel,
|
||||
expr: &'a Expr,
|
||||
) -> Option<(&'a str, ruff_text_size::TextRange)> {
|
||||
match expr {
|
||||
Expr::Tuple(ast::ExprTuple { elts, .. }) => elts
|
||||
.iter()
|
||||
.find_map(|elt| contains_blind_exception(semantic, elt)),
|
||||
_ => {
|
||||
let builtin_exception_type = semantic.resolve_builtin_symbol(expr)?;
|
||||
matches!(builtin_exception_type, "BaseException" | "Exception")
|
||||
.then(|| (builtin_exception_type, expr.range()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// BLE001
|
||||
pub(crate) fn blind_except(
|
||||
checker: &Checker,
|
||||
@@ -87,12 +103,9 @@ pub(crate) fn blind_except(
|
||||
};
|
||||
|
||||
let semantic = checker.semantic();
|
||||
let Some(builtin_exception_type) = semantic.resolve_builtin_symbol(type_) else {
|
||||
let Some((builtin_exception_type, range)) = contains_blind_exception(semantic, type_) else {
|
||||
return;
|
||||
};
|
||||
if !matches!(builtin_exception_type, "BaseException" | "Exception") {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the exception is re-raised, don't flag an error.
|
||||
let mut visitor = ReraiseVisitor::new(name);
|
||||
@@ -110,9 +123,9 @@ pub(crate) fn blind_except(
|
||||
|
||||
checker.report_diagnostic(
|
||||
BlindExcept {
|
||||
name: builtin_exception_type.to_string(),
|
||||
name: builtin_exception_type.into(),
|
||||
},
|
||||
type_.range(),
|
||||
range,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -147,3 +147,93 @@ BLE.py:131:8: BLE001 Do not catch blind exception: `Exception`
|
||||
| ^^^^^^^^^ BLE001
|
||||
132 | critical("...", exc_info=None)
|
||||
|
|
||||
|
||||
BLE.py:169:9: BLE001 Do not catch blind exception: `Exception`
|
||||
|
|
||||
167 | try:
|
||||
168 | pass
|
||||
169 | except (Exception,):
|
||||
| ^^^^^^^^^ BLE001
|
||||
170 | pass
|
||||
|
|
||||
|
||||
BLE.py:174:9: BLE001 Do not catch blind exception: `Exception`
|
||||
|
|
||||
172 | try:
|
||||
173 | pass
|
||||
174 | except (Exception, ValueError):
|
||||
| ^^^^^^^^^ BLE001
|
||||
175 | pass
|
||||
|
|
||||
|
||||
BLE.py:179:21: BLE001 Do not catch blind exception: `Exception`
|
||||
|
|
||||
177 | try:
|
||||
178 | pass
|
||||
179 | except (ValueError, Exception):
|
||||
| ^^^^^^^^^ BLE001
|
||||
180 | pass
|
||||
|
|
||||
|
||||
BLE.py:184:21: BLE001 Do not catch blind exception: `Exception`
|
||||
|
|
||||
182 | try:
|
||||
183 | pass
|
||||
184 | except (ValueError, Exception) as e:
|
||||
| ^^^^^^^^^ BLE001
|
||||
185 | print(e)
|
||||
|
|
||||
|
||||
BLE.py:189:9: BLE001 Do not catch blind exception: `BaseException`
|
||||
|
|
||||
187 | try:
|
||||
188 | pass
|
||||
189 | except (BaseException, TypeError):
|
||||
| ^^^^^^^^^^^^^ BLE001
|
||||
190 | pass
|
||||
|
|
||||
|
||||
BLE.py:194:20: BLE001 Do not catch blind exception: `BaseException`
|
||||
|
|
||||
192 | try:
|
||||
193 | pass
|
||||
194 | except (TypeError, BaseException):
|
||||
| ^^^^^^^^^^^^^ BLE001
|
||||
195 | pass
|
||||
|
|
||||
|
||||
BLE.py:199:9: BLE001 Do not catch blind exception: `Exception`
|
||||
|
|
||||
197 | try:
|
||||
198 | pass
|
||||
199 | except (Exception, BaseException):
|
||||
| ^^^^^^^^^ BLE001
|
||||
200 | pass
|
||||
|
|
||||
|
||||
BLE.py:204:9: BLE001 Do not catch blind exception: `BaseException`
|
||||
|
|
||||
202 | try:
|
||||
203 | pass
|
||||
204 | except (BaseException, Exception):
|
||||
| ^^^^^^^^^^^^^ BLE001
|
||||
205 | pass
|
||||
|
|
||||
|
||||
BLE.py:210:10: BLE001 Do not catch blind exception: `Exception`
|
||||
|
|
||||
208 | try:
|
||||
209 | pass
|
||||
210 | except ((Exception, ValueError), TypeError):
|
||||
| ^^^^^^^^^ BLE001
|
||||
211 | pass
|
||||
|
|
||||
|
||||
BLE.py:215:22: BLE001 Do not catch blind exception: `BaseException`
|
||||
|
|
||||
213 | try:
|
||||
214 | pass
|
||||
215 | except (ValueError, (BaseException, TypeError)):
|
||||
| ^^^^^^^^^^^^^ BLE001
|
||||
216 | pass
|
||||
|
|
||||
|
||||
@@ -182,60 +182,27 @@ impl Violation for DotFormatInException {
|
||||
|
||||
/// EM101, EM102, EM103
|
||||
pub(crate) fn string_in_exception(checker: &Checker, stmt: &Stmt, exc: &Expr) {
|
||||
if let Expr::Call(ast::ExprCall {
|
||||
let Expr::Call(ast::ExprCall {
|
||||
func,
|
||||
arguments: Arguments { args, .. },
|
||||
..
|
||||
}) = exc
|
||||
{
|
||||
if let Some(first) = args.first() {
|
||||
match first {
|
||||
// Check for string literals.
|
||||
Expr::StringLiteral(ast::ExprStringLiteral { value: string, .. }) => {
|
||||
if checker.is_rule_enabled(Rule::RawStringInException) {
|
||||
if string.len() >= checker.settings().flake8_errmsg.max_string_length {
|
||||
let mut diagnostic =
|
||||
checker.report_diagnostic(RawStringInException, first.range());
|
||||
if let Some(indentation) =
|
||||
whitespace::indentation(checker.source(), stmt)
|
||||
{
|
||||
diagnostic.set_fix(generate_fix(
|
||||
stmt,
|
||||
first,
|
||||
indentation,
|
||||
checker.stylist(),
|
||||
checker.locator(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check for byte string literals.
|
||||
Expr::BytesLiteral(ast::ExprBytesLiteral { value: bytes, .. }) => {
|
||||
if checker.settings().rules.enabled(Rule::RawStringInException) {
|
||||
if bytes.len() >= checker.settings().flake8_errmsg.max_string_length
|
||||
&& is_raise_exception_byte_string_enabled(checker.settings())
|
||||
{
|
||||
let mut diagnostic =
|
||||
checker.report_diagnostic(RawStringInException, first.range());
|
||||
if let Some(indentation) =
|
||||
whitespace::indentation(checker.source(), stmt)
|
||||
{
|
||||
diagnostic.set_fix(generate_fix(
|
||||
stmt,
|
||||
first,
|
||||
indentation,
|
||||
checker.stylist(),
|
||||
checker.locator(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check for f-strings.
|
||||
Expr::FString(_) => {
|
||||
if checker.is_rule_enabled(Rule::FStringInException) {
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
if checker.semantic().match_typing_expr(func, "cast") {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(first) = args.first() {
|
||||
match first {
|
||||
// Check for string literals.
|
||||
Expr::StringLiteral(ast::ExprStringLiteral { value: string, .. }) => {
|
||||
if checker.is_rule_enabled(Rule::RawStringInException) {
|
||||
if string.len() >= checker.settings().flake8_errmsg.max_string_length {
|
||||
let mut diagnostic =
|
||||
checker.report_diagnostic(FStringInException, first.range());
|
||||
checker.report_diagnostic(RawStringInException, first.range());
|
||||
if let Some(indentation) = whitespace::indentation(checker.source(), stmt) {
|
||||
diagnostic.set_fix(generate_fix(
|
||||
stmt,
|
||||
@@ -247,32 +214,66 @@ pub(crate) fn string_in_exception(checker: &Checker, stmt: &Stmt, exc: &Expr) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check for .format() calls.
|
||||
Expr::Call(ast::ExprCall { func, .. }) => {
|
||||
if checker.is_rule_enabled(Rule::DotFormatInException) {
|
||||
if let Expr::Attribute(ast::ExprAttribute { value, attr, .. }) =
|
||||
func.as_ref()
|
||||
{
|
||||
if attr == "format" && value.is_literal_expr() {
|
||||
let mut diagnostic =
|
||||
checker.report_diagnostic(DotFormatInException, first.range());
|
||||
if let Some(indentation) =
|
||||
whitespace::indentation(checker.source(), stmt)
|
||||
{
|
||||
diagnostic.set_fix(generate_fix(
|
||||
stmt,
|
||||
first,
|
||||
indentation,
|
||||
checker.stylist(),
|
||||
checker.locator(),
|
||||
));
|
||||
}
|
||||
}
|
||||
// Check for byte string literals.
|
||||
Expr::BytesLiteral(ast::ExprBytesLiteral { value: bytes, .. }) => {
|
||||
if checker.settings().rules.enabled(Rule::RawStringInException) {
|
||||
if bytes.len() >= checker.settings().flake8_errmsg.max_string_length
|
||||
&& is_raise_exception_byte_string_enabled(checker.settings())
|
||||
{
|
||||
let mut diagnostic =
|
||||
checker.report_diagnostic(RawStringInException, first.range());
|
||||
if let Some(indentation) = whitespace::indentation(checker.source(), stmt) {
|
||||
diagnostic.set_fix(generate_fix(
|
||||
stmt,
|
||||
first,
|
||||
indentation,
|
||||
checker.stylist(),
|
||||
checker.locator(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check for f-strings.
|
||||
Expr::FString(_) => {
|
||||
if checker.is_rule_enabled(Rule::FStringInException) {
|
||||
let mut diagnostic =
|
||||
checker.report_diagnostic(FStringInException, first.range());
|
||||
if let Some(indentation) = whitespace::indentation(checker.source(), stmt) {
|
||||
diagnostic.set_fix(generate_fix(
|
||||
stmt,
|
||||
first,
|
||||
indentation,
|
||||
checker.stylist(),
|
||||
checker.locator(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check for .format() calls.
|
||||
Expr::Call(ast::ExprCall { func, .. }) => {
|
||||
if checker.is_rule_enabled(Rule::DotFormatInException) {
|
||||
if let Expr::Attribute(ast::ExprAttribute { value, attr, .. }) = func.as_ref() {
|
||||
if attr == "format" && value.is_literal_expr() {
|
||||
let mut diagnostic =
|
||||
checker.report_diagnostic(DotFormatInException, first.range());
|
||||
if let Some(indentation) =
|
||||
whitespace::indentation(checker.source(), stmt)
|
||||
{
|
||||
diagnostic.set_fix(generate_fix(
|
||||
stmt,
|
||||
first,
|
||||
indentation,
|
||||
checker.stylist(),
|
||||
checker.locator(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,3 +278,6 @@ EM.py:84:9: EM103 [*] Exception must not use a `.format()` string directly, assi
|
||||
91 |+ raise RuntimeError(
|
||||
92 |+ msg
|
||||
93 |+ )
|
||||
91 94 |
|
||||
92 95 |
|
||||
93 96 | def raise_typing_cast_exception():
|
||||
|
||||
@@ -343,3 +343,6 @@ EM.py:84:9: EM103 [*] Exception must not use a `.format()` string directly, assi
|
||||
91 |+ raise RuntimeError(
|
||||
92 |+ msg
|
||||
93 |+ )
|
||||
91 94 |
|
||||
92 95 |
|
||||
93 96 | def raise_typing_cast_exception():
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::whitespace::trailing_comment_start_offset;
|
||||
use ruff_python_ast::{Stmt, StmtExpr};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix;
|
||||
use crate::{Fix, FixAvailability, Violation};
|
||||
use crate::{Edit, Fix, FixAvailability, Violation};
|
||||
|
||||
/// ## What it does
|
||||
/// Removes ellipses (`...`) in otherwise non-empty class bodies.
|
||||
@@ -50,15 +51,21 @@ pub(crate) fn ellipsis_in_non_empty_class_body(checker: &Checker, body: &[Stmt])
|
||||
}
|
||||
|
||||
for stmt in body {
|
||||
let Stmt::Expr(StmtExpr { value, .. }) = &stmt else {
|
||||
let Stmt::Expr(StmtExpr { value, .. }) = stmt else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if value.is_ellipsis_literal_expr() {
|
||||
let mut diagnostic =
|
||||
checker.report_diagnostic(EllipsisInNonEmptyClassBody, stmt.range());
|
||||
let edit =
|
||||
fix::edits::delete_stmt(stmt, Some(stmt), checker.locator(), checker.indexer());
|
||||
|
||||
// Try to preserve trailing comment if it exists
|
||||
let edit = if let Some(index) = trailing_comment_start_offset(stmt, checker.source()) {
|
||||
Edit::range_deletion(stmt.range().add_end(index))
|
||||
} else {
|
||||
fix::edits::delete_stmt(stmt, Some(stmt), checker.locator(), checker.indexer())
|
||||
};
|
||||
|
||||
diagnostic.set_fix(Fix::safe_edit(edit).isolate(Checker::isolation(
|
||||
checker.semantic().current_statement_id(),
|
||||
)));
|
||||
|
||||
@@ -145,3 +145,22 @@ PYI013.py:36:5: PYI013 [*] Non-empty class body must not contain `...`
|
||||
37 36 |
|
||||
38 37 | def __init__():
|
||||
39 38 | pass
|
||||
|
||||
PYI013.py:44:5: PYI013 [*] Non-empty class body must not contain `...`
|
||||
|
|
||||
42 | class NonEmptyChildWithInlineComment:
|
||||
43 | value: int
|
||||
44 | ... # preserve me
|
||||
| ^^^ PYI013
|
||||
|
|
||||
= help: Remove unnecessary `...`
|
||||
|
||||
ℹ Safe fix
|
||||
41 41 |
|
||||
42 42 | class NonEmptyChildWithInlineComment:
|
||||
43 43 | value: int
|
||||
44 |- ... # preserve me
|
||||
44 |+ # preserve me
|
||||
45 45 |
|
||||
46 46 |
|
||||
47 47 | class EmptyClass:
|
||||
|
||||
@@ -17,9 +17,10 @@ PYI013.pyi:5:5: PYI013 [*] Non-empty class body must not contain `...`
|
||||
3 3 | class OneAttributeClass:
|
||||
4 4 | value: int
|
||||
5 |- ... # Error
|
||||
6 5 |
|
||||
7 6 | class OneAttributeClass2:
|
||||
8 7 | ... # Error
|
||||
5 |+ # Error
|
||||
6 6 |
|
||||
7 7 | class OneAttributeClass2:
|
||||
8 8 | ... # Error
|
||||
|
||||
PYI013.pyi:8:5: PYI013 [*] Non-empty class body must not contain `...`
|
||||
|
|
||||
@@ -35,9 +36,10 @@ PYI013.pyi:8:5: PYI013 [*] Non-empty class body must not contain `...`
|
||||
6 6 |
|
||||
7 7 | class OneAttributeClass2:
|
||||
8 |- ... # Error
|
||||
9 8 | value: int
|
||||
10 9 |
|
||||
11 10 | class MyClass:
|
||||
8 |+ # Error
|
||||
9 9 | value: int
|
||||
10 10 |
|
||||
11 11 | class MyClass:
|
||||
|
||||
PYI013.pyi:12:5: PYI013 [*] Non-empty class body must not contain `...`
|
||||
|
|
||||
@@ -91,9 +93,10 @@ PYI013.pyi:17:5: PYI013 [*] Non-empty class body must not contain `...`
|
||||
15 15 | class TwoEllipsesClass:
|
||||
16 16 | ...
|
||||
17 |- ... # Error
|
||||
18 17 |
|
||||
19 18 | class DocstringClass:
|
||||
20 19 | """
|
||||
17 |+ # Error
|
||||
18 18 |
|
||||
19 19 | class DocstringClass:
|
||||
20 20 | """
|
||||
|
||||
PYI013.pyi:24:5: PYI013 [*] Non-empty class body must not contain `...`
|
||||
|
|
||||
@@ -111,9 +114,10 @@ PYI013.pyi:24:5: PYI013 [*] Non-empty class body must not contain `...`
|
||||
22 22 | """
|
||||
23 23 |
|
||||
24 |- ... # Error
|
||||
25 24 |
|
||||
26 25 | class NonEmptyChild(Exception):
|
||||
27 26 | value: int
|
||||
24 |+ # Error
|
||||
25 25 |
|
||||
26 26 | class NonEmptyChild(Exception):
|
||||
27 27 | value: int
|
||||
|
||||
PYI013.pyi:28:5: PYI013 [*] Non-empty class body must not contain `...`
|
||||
|
|
||||
@@ -131,9 +135,10 @@ PYI013.pyi:28:5: PYI013 [*] Non-empty class body must not contain `...`
|
||||
26 26 | class NonEmptyChild(Exception):
|
||||
27 27 | value: int
|
||||
28 |- ... # Error
|
||||
29 28 |
|
||||
30 29 | class NonEmptyChild2(Exception):
|
||||
31 30 | ... # Error
|
||||
28 |+ # Error
|
||||
29 29 |
|
||||
30 30 | class NonEmptyChild2(Exception):
|
||||
31 31 | ... # Error
|
||||
|
||||
PYI013.pyi:31:5: PYI013 [*] Non-empty class body must not contain `...`
|
||||
|
|
||||
@@ -149,9 +154,10 @@ PYI013.pyi:31:5: PYI013 [*] Non-empty class body must not contain `...`
|
||||
29 29 |
|
||||
30 30 | class NonEmptyChild2(Exception):
|
||||
31 |- ... # Error
|
||||
32 31 | value: int
|
||||
33 32 |
|
||||
34 33 | class NonEmptyWithInit:
|
||||
31 |+ # Error
|
||||
32 32 | value: int
|
||||
33 33 |
|
||||
34 34 | class NonEmptyWithInit:
|
||||
|
||||
PYI013.pyi:36:5: PYI013 [*] Non-empty class body must not contain `...`
|
||||
|
|
||||
@@ -169,6 +175,28 @@ PYI013.pyi:36:5: PYI013 [*] Non-empty class body must not contain `...`
|
||||
34 34 | class NonEmptyWithInit:
|
||||
35 35 | value: int
|
||||
36 |- ... # Error
|
||||
37 36 |
|
||||
38 37 | def __init__():
|
||||
39 38 | pass
|
||||
36 |+ # Error
|
||||
37 37 |
|
||||
38 38 | def __init__():
|
||||
39 39 | pass
|
||||
|
||||
PYI013.pyi:43:5: PYI013 [*] Non-empty class body must not contain `...`
|
||||
|
|
||||
41 | class NonEmptyChildWithInlineComment:
|
||||
42 | value: int
|
||||
43 | ... # preserve me
|
||||
| ^^^ PYI013
|
||||
44 |
|
||||
45 | # Not violations
|
||||
|
|
||||
= help: Remove unnecessary `...`
|
||||
|
||||
ℹ Safe fix
|
||||
40 40 |
|
||||
41 41 | class NonEmptyChildWithInlineComment:
|
||||
42 42 | value: int
|
||||
43 |- ... # preserve me
|
||||
43 |+ # preserve me
|
||||
44 44 |
|
||||
45 45 | # Not violations
|
||||
46 46 |
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::StringFlags;
|
||||
use ruff_python_ast::{
|
||||
Expr, ExprCall, ExprContext, ExprList, ExprUnaryOp, StringLiteral, StringLiteralFlags,
|
||||
StringLiteralValue, UnaryOp, str::TripleQuotes,
|
||||
@@ -116,26 +117,50 @@ pub(crate) fn split_static_string(
|
||||
}
|
||||
}
|
||||
|
||||
fn replace_flags(elt: &str, flags: StringLiteralFlags) -> StringLiteralFlags {
|
||||
// In the ideal case we can wrap the element in _single_ quotes of the same
|
||||
// style. For example, both of these are okay:
|
||||
//
|
||||
// ```python
|
||||
// """itemA
|
||||
// itemB
|
||||
// itemC""".split() # -> ["itemA", "itemB", "itemC"]
|
||||
// ```
|
||||
//
|
||||
// ```python
|
||||
// r"""itemA
|
||||
// 'single'quoted
|
||||
// """.split() # -> [r"itemA",r"'single'quoted'"]
|
||||
// ```
|
||||
if !flags.prefix().is_raw() || !elt.contains(flags.quote_style().as_char()) {
|
||||
flags.with_triple_quotes(TripleQuotes::No)
|
||||
}
|
||||
// If we have a raw string containing a quotation mark of the same style,
|
||||
// then we have to swap the style of quotation marks used
|
||||
else if !elt.contains(flags.quote_style().opposite().as_char()) {
|
||||
flags
|
||||
.with_quote_style(flags.quote_style().opposite())
|
||||
.with_triple_quotes(TripleQuotes::No)
|
||||
} else
|
||||
// If both types of quotes are used in the raw, triple-quoted string, then
|
||||
// we are forced to either add escapes or keep the triple quotes. We opt for
|
||||
// the latter.
|
||||
{
|
||||
flags
|
||||
}
|
||||
}
|
||||
|
||||
fn construct_replacement(elts: &[&str], flags: StringLiteralFlags) -> Expr {
|
||||
Expr::List(ExprList {
|
||||
elts: elts
|
||||
.iter()
|
||||
.map(|elt| {
|
||||
let element_flags = replace_flags(elt, flags);
|
||||
Expr::from(StringLiteral {
|
||||
value: Box::from(*elt),
|
||||
range: TextRange::default(),
|
||||
node_index: ruff_python_ast::AtomicNodeIndex::dummy(),
|
||||
// intentionally omit the triple quote flag, if set, to avoid strange
|
||||
// replacements like
|
||||
//
|
||||
// ```python
|
||||
// """
|
||||
// itemA
|
||||
// itemB
|
||||
// itemC
|
||||
// """.split() # -> ["""itemA""", """itemB""", """itemC"""]
|
||||
// ```
|
||||
flags: flags.with_triple_quotes(TripleQuotes::No),
|
||||
flags: element_flags,
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
|
||||
@@ -1226,6 +1226,7 @@ SIM905.py:130:7: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
130 |+print([" x"])
|
||||
131 131 | print(" x ".rsplit(maxsplit=0))
|
||||
132 132 | print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
133 133 |
|
||||
|
||||
SIM905.py:131:7: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
|
|
||||
@@ -1244,6 +1245,8 @@ SIM905.py:131:7: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
131 |-print(" x ".rsplit(maxsplit=0))
|
||||
131 |+print([" x"])
|
||||
132 132 | print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
133 133 |
|
||||
134 134 | # https://github.com/astral-sh/ruff/issues/19581 - embedded quotes in raw strings
|
||||
|
||||
SIM905.py:132:7: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
|
|
||||
@@ -1251,6 +1254,8 @@ SIM905.py:132:7: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
131 | print(" x ".rsplit(maxsplit=0))
|
||||
132 | print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM905
|
||||
133 |
|
||||
134 | # https://github.com/astral-sh/ruff/issues/19581 - embedded quotes in raw strings
|
||||
|
|
||||
= help: Replace with list literal
|
||||
|
||||
@@ -1260,3 +1265,88 @@ SIM905.py:132:7: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
131 131 | print(" x ".rsplit(maxsplit=0))
|
||||
132 |-print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
132 |+print([" x"])
|
||||
133 133 |
|
||||
134 134 | # https://github.com/astral-sh/ruff/issues/19581 - embedded quotes in raw strings
|
||||
135 135 | r"""simple@example.com
|
||||
|
||||
SIM905.py:135:1: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
|
|
||||
134 | # https://github.com/astral-sh/ruff/issues/19581 - embedded quotes in raw strings
|
||||
135 | / r"""simple@example.com
|
||||
136 | | very.common@example.com
|
||||
137 | | FirstName.LastName@EasierReading.org
|
||||
138 | | x@example.com
|
||||
139 | | long.email-address-with-hyphens@and.subdomains.example.com
|
||||
140 | | user.name+tag+sorting@example.com
|
||||
141 | | name/surname@example.com
|
||||
142 | | xample@s.example
|
||||
143 | | " "@example.org
|
||||
144 | | "john..doe"@example.org
|
||||
145 | | mailhost!username@example.org
|
||||
146 | | "very.(),:;<>[]\".VERY.\"very@\\ \"very\".unusual"@strange.example.com
|
||||
147 | | user%example.com@example.org
|
||||
148 | | user-@example.org
|
||||
149 | | I❤️CHOCOLATE@example.com
|
||||
150 | | this\ still\"not\\allowed@example.com
|
||||
151 | | stellyamburrr985@example.com
|
||||
152 | | Abc.123@example.com
|
||||
153 | | user+mailbox/department=shipping@example.com
|
||||
154 | | !#$%&'*+-/=?^_`.{|}~@example.com
|
||||
155 | | "Abc@def"@example.com
|
||||
156 | | "Fred\ Bloggs"@example.com
|
||||
157 | | "Joe.\\Blow"@example.com""".split("\n")
|
||||
| |_______________________________________^ SIM905
|
||||
|
|
||||
= help: Replace with list literal
|
||||
|
||||
ℹ Safe fix
|
||||
132 132 | print(" x ".rsplit(sep=None, maxsplit=0))
|
||||
133 133 |
|
||||
134 134 | # https://github.com/astral-sh/ruff/issues/19581 - embedded quotes in raw strings
|
||||
135 |-r"""simple@example.com
|
||||
136 |-very.common@example.com
|
||||
137 |-FirstName.LastName@EasierReading.org
|
||||
138 |-x@example.com
|
||||
139 |-long.email-address-with-hyphens@and.subdomains.example.com
|
||||
140 |-user.name+tag+sorting@example.com
|
||||
141 |-name/surname@example.com
|
||||
142 |-xample@s.example
|
||||
143 |-" "@example.org
|
||||
144 |-"john..doe"@example.org
|
||||
145 |-mailhost!username@example.org
|
||||
146 |-"very.(),:;<>[]\".VERY.\"very@\\ \"very\".unusual"@strange.example.com
|
||||
147 |-user%example.com@example.org
|
||||
148 |-user-@example.org
|
||||
149 |-I❤️CHOCOLATE@example.com
|
||||
150 |-this\ still\"not\\allowed@example.com
|
||||
151 |-stellyamburrr985@example.com
|
||||
152 |-Abc.123@example.com
|
||||
153 |-user+mailbox/department=shipping@example.com
|
||||
154 |-!#$%&'*+-/=?^_`.{|}~@example.com
|
||||
155 |-"Abc@def"@example.com
|
||||
156 |-"Fred\ Bloggs"@example.com
|
||||
157 |-"Joe.\\Blow"@example.com""".split("\n")
|
||||
135 |+[r"simple@example.com", r"very.common@example.com", r"FirstName.LastName@EasierReading.org", r"x@example.com", r"long.email-address-with-hyphens@and.subdomains.example.com", r"user.name+tag+sorting@example.com", r"name/surname@example.com", r"xample@s.example", r'" "@example.org', r'"john..doe"@example.org', r"mailhost!username@example.org", r'"very.(),:;<>[]\".VERY.\"very@\\ \"very\".unusual"@strange.example.com', r"user%example.com@example.org", r"user-@example.org", r"I❤️CHOCOLATE@example.com", r'this\ still\"not\\allowed@example.com', r"stellyamburrr985@example.com", r"Abc.123@example.com", r"user+mailbox/department=shipping@example.com", r"!#$%&'*+-/=?^_`.{|}~@example.com", r'"Abc@def"@example.com', r'"Fred\ Bloggs"@example.com', r'"Joe.\\Blow"@example.com']
|
||||
158 136 |
|
||||
159 137 |
|
||||
160 138 | r"""first
|
||||
|
||||
SIM905.py:160:1: SIM905 [*] Consider using a list literal instead of `str.split`
|
||||
|
|
||||
160 | / r"""first
|
||||
161 | | 'no need' to escape
|
||||
162 | | "swap" quote style
|
||||
163 | | "use' ugly triple quotes""".split("\n")
|
||||
| |_______________________________________^ SIM905
|
||||
|
|
||||
= help: Replace with list literal
|
||||
|
||||
ℹ Safe fix
|
||||
157 157 | "Joe.\\Blow"@example.com""".split("\n")
|
||||
158 158 |
|
||||
159 159 |
|
||||
160 |-r"""first
|
||||
161 |-'no need' to escape
|
||||
162 |-"swap" quote style
|
||||
163 |-"use' ugly triple quotes""".split("\n")
|
||||
160 |+[r"first", r"'no need' to escape", r'"swap" quote style', r""""use' ugly triple quotes"""]
|
||||
|
||||
@@ -20,6 +20,35 @@ pub(crate) fn is_pathlib_path_call(checker: &Checker, expr: &Expr) -> bool {
|
||||
})
|
||||
}
|
||||
|
||||
/// Check if the given segments represent a pathlib Path subclass or `PackagePath` with preview mode support.
|
||||
/// In stable mode, only checks for `Path` and `PurePath`. In preview mode, also checks for
|
||||
/// `PosixPath`, `PurePosixPath`, `WindowsPath`, `PureWindowsPath`, and `PackagePath`.
|
||||
pub(crate) fn is_pure_path_subclass_with_preview(
|
||||
checker: &crate::checkers::ast::Checker,
|
||||
segments: &[&str],
|
||||
) -> bool {
|
||||
let is_core_pathlib = matches!(segments, ["pathlib", "Path" | "PurePath"]);
|
||||
|
||||
if is_core_pathlib {
|
||||
return true;
|
||||
}
|
||||
|
||||
if checker.settings().preview.is_enabled() {
|
||||
let is_expanded_pathlib = matches!(
|
||||
segments,
|
||||
[
|
||||
"pathlib",
|
||||
"PosixPath" | "PurePosixPath" | "WindowsPath" | "PureWindowsPath"
|
||||
]
|
||||
);
|
||||
let is_packagepath = matches!(segments, ["importlib", "metadata", "PackagePath"]);
|
||||
|
||||
return is_expanded_pathlib || is_packagepath;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// We check functions that take only 1 argument, this does not apply to functions
|
||||
/// with `dir_fd` argument, because `dir_fd` is not supported by pathlib,
|
||||
/// so check if it's set to non-default values
|
||||
|
||||
@@ -123,6 +123,7 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(Rule::PathConstructorCurrentDirectory, Path::new("PTH201.py"))]
|
||||
#[test_case(Rule::OsPathGetsize, Path::new("PTH202.py"))]
|
||||
#[test_case(Rule::OsPathGetsize, Path::new("PTH202_2.py"))]
|
||||
#[test_case(Rule::OsPathGetatime, Path::new("PTH203.py"))]
|
||||
|
||||
@@ -9,6 +9,7 @@ use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::edits::{Parentheses, remove_argument};
|
||||
use crate::rules::flake8_use_pathlib::helpers::is_pure_path_subclass_with_preview;
|
||||
use crate::{AlwaysFixableViolation, Applicability, Edit, Fix};
|
||||
|
||||
/// ## What it does
|
||||
@@ -69,7 +70,7 @@ pub(crate) fn path_constructor_current_directory(
|
||||
|
||||
let arguments = &call.arguments;
|
||||
|
||||
if !matches!(segments, ["pathlib", "Path" | "PurePath"]) {
|
||||
if !is_pure_path_subclass_with_preview(checker, segments) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,423 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs
|
||||
assertion_line: 144
|
||||
---
|
||||
PTH201.py:6:10: PTH201 [*] Do not pass the current directory explicitly to `Path`
|
||||
|
|
||||
5 | # match
|
||||
6 | _ = Path(".")
|
||||
| ^^^ PTH201
|
||||
7 | _ = pth(".")
|
||||
8 | _ = PurePath(".")
|
||||
|
|
||||
= help: Remove the current directory argument
|
||||
|
||||
ℹ Safe fix
|
||||
3 3 |
|
||||
4 4 |
|
||||
5 5 | # match
|
||||
6 |-_ = Path(".")
|
||||
6 |+_ = Path()
|
||||
7 7 | _ = pth(".")
|
||||
8 8 | _ = PurePath(".")
|
||||
9 9 | _ = Path("")
|
||||
|
||||
PTH201.py:7:9: PTH201 [*] Do not pass the current directory explicitly to `Path`
|
||||
|
|
||||
5 | # match
|
||||
6 | _ = Path(".")
|
||||
7 | _ = pth(".")
|
||||
| ^^^ PTH201
|
||||
8 | _ = PurePath(".")
|
||||
9 | _ = Path("")
|
||||
|
|
||||
= help: Remove the current directory argument
|
||||
|
||||
ℹ Safe fix
|
||||
4 4 |
|
||||
5 5 | # match
|
||||
6 6 | _ = Path(".")
|
||||
7 |-_ = pth(".")
|
||||
7 |+_ = pth()
|
||||
8 8 | _ = PurePath(".")
|
||||
9 9 | _ = Path("")
|
||||
10 10 |
|
||||
|
||||
PTH201.py:8:14: PTH201 [*] Do not pass the current directory explicitly to `Path`
|
||||
|
|
||||
6 | _ = Path(".")
|
||||
7 | _ = pth(".")
|
||||
8 | _ = PurePath(".")
|
||||
| ^^^ PTH201
|
||||
9 | _ = Path("")
|
||||
|
|
||||
= help: Remove the current directory argument
|
||||
|
||||
ℹ Safe fix
|
||||
5 5 | # match
|
||||
6 6 | _ = Path(".")
|
||||
7 7 | _ = pth(".")
|
||||
8 |-_ = PurePath(".")
|
||||
8 |+_ = PurePath()
|
||||
9 9 | _ = Path("")
|
||||
10 10 |
|
||||
11 11 | Path('', )
|
||||
|
||||
PTH201.py:9:10: PTH201 [*] Do not pass the current directory explicitly to `Path`
|
||||
|
|
||||
7 | _ = pth(".")
|
||||
8 | _ = PurePath(".")
|
||||
9 | _ = Path("")
|
||||
| ^^ PTH201
|
||||
10 |
|
||||
11 | Path('', )
|
||||
|
|
||||
= help: Remove the current directory argument
|
||||
|
||||
ℹ Safe fix
|
||||
6 6 | _ = Path(".")
|
||||
7 7 | _ = pth(".")
|
||||
8 8 | _ = PurePath(".")
|
||||
9 |-_ = Path("")
|
||||
9 |+_ = Path()
|
||||
10 10 |
|
||||
11 11 | Path('', )
|
||||
12 12 |
|
||||
|
||||
PTH201.py:11:6: PTH201 [*] Do not pass the current directory explicitly to `Path`
|
||||
|
|
||||
9 | _ = Path("")
|
||||
10 |
|
||||
11 | Path('', )
|
||||
| ^^ PTH201
|
||||
12 |
|
||||
13 | Path(
|
||||
|
|
||||
= help: Remove the current directory argument
|
||||
|
||||
ℹ Safe fix
|
||||
8 8 | _ = PurePath(".")
|
||||
9 9 | _ = Path("")
|
||||
10 10 |
|
||||
11 |-Path('', )
|
||||
11 |+Path()
|
||||
12 12 |
|
||||
13 13 | Path(
|
||||
14 14 | '',
|
||||
|
||||
PTH201.py:14:5: PTH201 [*] Do not pass the current directory explicitly to `Path`
|
||||
|
|
||||
13 | Path(
|
||||
14 | '',
|
||||
| ^^ PTH201
|
||||
15 | )
|
||||
|
|
||||
= help: Remove the current directory argument
|
||||
|
||||
ℹ Safe fix
|
||||
10 10 |
|
||||
11 11 | Path('', )
|
||||
12 12 |
|
||||
13 |-Path(
|
||||
14 |- '',
|
||||
15 |-)
|
||||
13 |+Path()
|
||||
16 14 |
|
||||
17 15 | Path( # Comment before argument
|
||||
18 16 | '',
|
||||
|
||||
PTH201.py:18:5: PTH201 [*] Do not pass the current directory explicitly to `Path`
|
||||
|
|
||||
17 | Path( # Comment before argument
|
||||
18 | '',
|
||||
| ^^ PTH201
|
||||
19 | )
|
||||
|
|
||||
= help: Remove the current directory argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
14 14 | '',
|
||||
15 15 | )
|
||||
16 16 |
|
||||
17 |-Path( # Comment before argument
|
||||
18 |- '',
|
||||
19 |-)
|
||||
17 |+Path()
|
||||
20 18 |
|
||||
21 19 | Path(
|
||||
22 20 | '', # EOL comment
|
||||
|
||||
PTH201.py:22:5: PTH201 [*] Do not pass the current directory explicitly to `Path`
|
||||
|
|
||||
21 | Path(
|
||||
22 | '', # EOL comment
|
||||
| ^^ PTH201
|
||||
23 | )
|
||||
|
|
||||
= help: Remove the current directory argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
18 18 | '',
|
||||
19 19 | )
|
||||
20 20 |
|
||||
21 |-Path(
|
||||
22 |- '', # EOL comment
|
||||
23 |-)
|
||||
21 |+Path()
|
||||
24 22 |
|
||||
25 23 | Path(
|
||||
26 24 | '' # Comment in the middle of implicitly concatenated string
|
||||
|
||||
PTH201.py:26:5: PTH201 [*] Do not pass the current directory explicitly to `Path`
|
||||
|
|
||||
25 | Path(
|
||||
26 | / '' # Comment in the middle of implicitly concatenated string
|
||||
27 | | ".",
|
||||
| |_______^ PTH201
|
||||
28 | )
|
||||
|
|
||||
= help: Remove the current directory argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
22 22 | '', # EOL comment
|
||||
23 23 | )
|
||||
24 24 |
|
||||
25 |-Path(
|
||||
26 |- '' # Comment in the middle of implicitly concatenated string
|
||||
27 |- ".",
|
||||
28 |-)
|
||||
25 |+Path()
|
||||
29 26 |
|
||||
30 27 | Path(
|
||||
31 28 | '' # Comment before comma
|
||||
|
||||
PTH201.py:31:5: PTH201 [*] Do not pass the current directory explicitly to `Path`
|
||||
|
|
||||
30 | Path(
|
||||
31 | '' # Comment before comma
|
||||
| ^^ PTH201
|
||||
32 | ,
|
||||
33 | )
|
||||
|
|
||||
= help: Remove the current directory argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
27 27 | ".",
|
||||
28 28 | )
|
||||
29 29 |
|
||||
30 |-Path(
|
||||
31 |- '' # Comment before comma
|
||||
32 |- ,
|
||||
33 |-)
|
||||
30 |+Path()
|
||||
34 31 |
|
||||
35 32 | Path(
|
||||
36 33 | '',
|
||||
|
||||
PTH201.py:36:5: PTH201 [*] Do not pass the current directory explicitly to `Path`
|
||||
|
|
||||
35 | Path(
|
||||
36 | '',
|
||||
| ^^ PTH201
|
||||
37 | ) / "bare"
|
||||
|
|
||||
= help: Remove the current directory argument
|
||||
|
||||
ℹ Safe fix
|
||||
33 33 | )
|
||||
34 34 |
|
||||
35 35 | Path(
|
||||
36 |- '',
|
||||
37 |-) / "bare"
|
||||
36 |+ "bare",
|
||||
37 |+)
|
||||
38 38 |
|
||||
39 39 | Path( # Comment before argument
|
||||
40 40 | '',
|
||||
|
||||
PTH201.py:40:5: PTH201 [*] Do not pass the current directory explicitly to `Path`
|
||||
|
|
||||
39 | Path( # Comment before argument
|
||||
40 | '',
|
||||
| ^^ PTH201
|
||||
41 | ) / ("parenthesized")
|
||||
|
|
||||
= help: Remove the current directory argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
37 37 | ) / "bare"
|
||||
38 38 |
|
||||
39 39 | Path( # Comment before argument
|
||||
40 |- '',
|
||||
41 |-) / ("parenthesized")
|
||||
40 |+ ("parenthesized"),
|
||||
41 |+)
|
||||
42 42 |
|
||||
43 43 | Path(
|
||||
44 44 | '', # EOL comment
|
||||
|
||||
PTH201.py:44:5: PTH201 [*] Do not pass the current directory explicitly to `Path`
|
||||
|
|
||||
43 | Path(
|
||||
44 | '', # EOL comment
|
||||
| ^^ PTH201
|
||||
45 | ) / ( ("double parenthesized" ) )
|
||||
|
|
||||
= help: Remove the current directory argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
41 41 | ) / ("parenthesized")
|
||||
42 42 |
|
||||
43 43 | Path(
|
||||
44 |- '', # EOL comment
|
||||
45 |-) / ( ("double parenthesized" ) )
|
||||
44 |+ ( ("double parenthesized" ) ), # EOL comment
|
||||
45 |+)
|
||||
46 46 |
|
||||
47 47 | ( Path(
|
||||
48 48 | '' # Comment in the middle of implicitly concatenated string
|
||||
|
||||
PTH201.py:48:5: PTH201 [*] Do not pass the current directory explicitly to `Path`
|
||||
|
|
||||
47 | ( Path(
|
||||
48 | / '' # Comment in the middle of implicitly concatenated string
|
||||
49 | | ".",
|
||||
| |_______^ PTH201
|
||||
50 | ) )/ (("parenthesized path call")
|
||||
51 | # Comment between closing parentheses
|
||||
|
|
||||
= help: Remove the current directory argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
44 44 | '', # EOL comment
|
||||
45 45 | ) / ( ("double parenthesized" ) )
|
||||
46 46 |
|
||||
47 |-( Path(
|
||||
48 |- '' # Comment in the middle of implicitly concatenated string
|
||||
49 |- ".",
|
||||
50 |-) )/ (("parenthesized path call")
|
||||
47 |+Path(
|
||||
48 |+ (("parenthesized path call")
|
||||
51 49 | # Comment between closing parentheses
|
||||
50 |+),
|
||||
52 51 | )
|
||||
53 52 |
|
||||
54 53 | Path(
|
||||
|
||||
PTH201.py:55:5: PTH201 [*] Do not pass the current directory explicitly to `Path`
|
||||
|
|
||||
54 | Path(
|
||||
55 | '' # Comment before comma
|
||||
| ^^ PTH201
|
||||
56 | ,
|
||||
57 | ) / "multiple" / (
|
||||
|
|
||||
= help: Remove the current directory argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
52 52 | )
|
||||
53 53 |
|
||||
54 54 | Path(
|
||||
55 |- '' # Comment before comma
|
||||
55 |+ "multiple" # Comment before comma
|
||||
56 56 | ,
|
||||
57 |-) / "multiple" / (
|
||||
57 |+) / (
|
||||
58 58 | "frag" # Comment
|
||||
59 59 | 'ment'
|
||||
60 60 | )
|
||||
|
||||
PTH201.py:74:15: PTH201 [*] Do not pass the current directory explicitly to `Path`
|
||||
|
|
||||
72 | from importlib.metadata import PackagePath
|
||||
73 |
|
||||
74 | _ = PosixPath(".")
|
||||
| ^^^ PTH201
|
||||
75 | _ = PurePosixPath(".")
|
||||
76 | _ = WindowsPath(".")
|
||||
|
|
||||
= help: Remove the current directory argument
|
||||
|
||||
ℹ Safe fix
|
||||
71 71 |
|
||||
72 72 | from importlib.metadata import PackagePath
|
||||
73 73 |
|
||||
74 |-_ = PosixPath(".")
|
||||
74 |+_ = PosixPath()
|
||||
75 75 | _ = PurePosixPath(".")
|
||||
76 76 | _ = WindowsPath(".")
|
||||
77 77 | _ = PureWindowsPath(".")
|
||||
|
||||
PTH201.py:75:19: PTH201 [*] Do not pass the current directory explicitly to `Path`
|
||||
|
|
||||
74 | _ = PosixPath(".")
|
||||
75 | _ = PurePosixPath(".")
|
||||
| ^^^ PTH201
|
||||
76 | _ = WindowsPath(".")
|
||||
77 | _ = PureWindowsPath(".")
|
||||
|
|
||||
= help: Remove the current directory argument
|
||||
|
||||
ℹ Safe fix
|
||||
72 72 | from importlib.metadata import PackagePath
|
||||
73 73 |
|
||||
74 74 | _ = PosixPath(".")
|
||||
75 |-_ = PurePosixPath(".")
|
||||
75 |+_ = PurePosixPath()
|
||||
76 76 | _ = WindowsPath(".")
|
||||
77 77 | _ = PureWindowsPath(".")
|
||||
78 78 | _ = PackagePath(".")
|
||||
|
||||
PTH201.py:76:17: PTH201 [*] Do not pass the current directory explicitly to `Path`
|
||||
|
|
||||
74 | _ = PosixPath(".")
|
||||
75 | _ = PurePosixPath(".")
|
||||
76 | _ = WindowsPath(".")
|
||||
| ^^^ PTH201
|
||||
77 | _ = PureWindowsPath(".")
|
||||
78 | _ = PackagePath(".")
|
||||
|
|
||||
= help: Remove the current directory argument
|
||||
|
||||
ℹ Safe fix
|
||||
73 73 |
|
||||
74 74 | _ = PosixPath(".")
|
||||
75 75 | _ = PurePosixPath(".")
|
||||
76 |-_ = WindowsPath(".")
|
||||
76 |+_ = WindowsPath()
|
||||
77 77 | _ = PureWindowsPath(".")
|
||||
78 78 | _ = PackagePath(".")
|
||||
|
||||
PTH201.py:77:21: PTH201 [*] Do not pass the current directory explicitly to `Path`
|
||||
|
|
||||
75 | _ = PurePosixPath(".")
|
||||
76 | _ = WindowsPath(".")
|
||||
77 | _ = PureWindowsPath(".")
|
||||
| ^^^ PTH201
|
||||
78 | _ = PackagePath(".")
|
||||
|
|
||||
= help: Remove the current directory argument
|
||||
|
||||
ℹ Safe fix
|
||||
74 74 | _ = PosixPath(".")
|
||||
75 75 | _ = PurePosixPath(".")
|
||||
76 76 | _ = WindowsPath(".")
|
||||
77 |-_ = PureWindowsPath(".")
|
||||
77 |+_ = PureWindowsPath()
|
||||
78 78 | _ = PackagePath(".")
|
||||
|
||||
PTH201.py:78:17: PTH201 [*] Do not pass the current directory explicitly to `Path`
|
||||
|
|
||||
76 | _ = WindowsPath(".")
|
||||
77 | _ = PureWindowsPath(".")
|
||||
78 | _ = PackagePath(".")
|
||||
| ^^^ PTH201
|
||||
|
|
||||
= help: Remove the current directory argument
|
||||
|
||||
ℹ Safe fix
|
||||
75 75 | _ = PurePosixPath(".")
|
||||
76 76 | _ = WindowsPath(".")
|
||||
77 77 | _ = PureWindowsPath(".")
|
||||
78 |-_ = PackagePath(".")
|
||||
78 |+_ = PackagePath()
|
||||
@@ -794,6 +794,7 @@ mod tests {
|
||||
#[test_case(Path::new("comments_and_newlines.py"))]
|
||||
#[test_case(Path::new("docstring.py"))]
|
||||
#[test_case(Path::new("docstring.pyi"))]
|
||||
#[test_case(Path::new("docstring_followed_by_continuation.py"))]
|
||||
#[test_case(Path::new("docstring_only.py"))]
|
||||
#[test_case(Path::new("docstring_with_continuation.py"))]
|
||||
#[test_case(Path::new("docstring_with_semicolon.py"))]
|
||||
@@ -828,6 +829,7 @@ mod tests {
|
||||
#[test_case(Path::new("comments_and_newlines.py"))]
|
||||
#[test_case(Path::new("docstring.py"))]
|
||||
#[test_case(Path::new("docstring.pyi"))]
|
||||
#[test_case(Path::new("docstring_followed_by_continuation.py"))]
|
||||
#[test_case(Path::new("docstring_only.py"))]
|
||||
#[test_case(Path::new("docstring_with_continuation.py"))]
|
||||
#[test_case(Path::new("docstring_with_semicolon.py"))]
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/isort/mod.rs
|
||||
---
|
||||
docstring_followed_by_continuation.py:1:1: I002 [*] Missing required import: `from __future__ import annotations`
|
||||
ℹ Safe fix
|
||||
1 1 | """Hello, world!"""\
|
||||
2 2 |
|
||||
3 |+from __future__ import annotations
|
||||
3 4 | x = 1; y = 2
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/isort/mod.rs
|
||||
---
|
||||
docstring_followed_by_continuation.py:1:1: I002 [*] Missing required import: `from __future__ import annotations as _annotations`
|
||||
ℹ Safe fix
|
||||
1 1 | """Hello, world!"""\
|
||||
2 2 |
|
||||
3 |+from __future__ import annotations as _annotations
|
||||
3 4 | x = 1; y = 2
|
||||
@@ -15,8 +15,8 @@ E101.py:15:1: E101 Indentation contains mixed spaces and tabs
|
||||
|
|
||||
13 | def func_mixed_start_with_space():
|
||||
14 | # E101
|
||||
15 | print("mixed starts with space")
|
||||
| ^^^^^^^^^^^^^^^ E101
|
||||
15 | print("mixed starts with space")
|
||||
| ^^^^^^^^^^^^^^^^^^^^ E101
|
||||
16 |
|
||||
17 | def xyz():
|
||||
|
|
||||
@@ -25,6 +25,6 @@ E101.py:19:1: E101 Indentation contains mixed spaces and tabs
|
||||
|
|
||||
17 | def xyz():
|
||||
18 | # E101
|
||||
19 | print("xyz");
|
||||
| ^^^^ E101
|
||||
19 | print("xyz");
|
||||
| ^^^^^^^ E101
|
||||
|
|
||||
|
||||
@@ -47,7 +47,7 @@ E20.py:6:15: E201 [*] Whitespace after '{'
|
||||
6 | spam(ham[1], { eggs: 2})
|
||||
| ^ E201
|
||||
7 | #: E201:1:6
|
||||
8 | spam( ham[1], {eggs: 2})
|
||||
8 | spam( ham[1], {eggs: 2})
|
||||
|
|
||||
= help: Remove whitespace before '{'
|
||||
|
||||
@@ -65,10 +65,10 @@ E20.py:8:6: E201 [*] Whitespace after '('
|
||||
|
|
||||
6 | spam(ham[1], { eggs: 2})
|
||||
7 | #: E201:1:6
|
||||
8 | spam( ham[1], {eggs: 2})
|
||||
| ^^^ E201
|
||||
8 | spam( ham[1], {eggs: 2})
|
||||
| ^^^^ E201
|
||||
9 | #: E201:1:10
|
||||
10 | spam(ham[ 1], {eggs: 2})
|
||||
10 | spam(ham[ 1], {eggs: 2})
|
||||
|
|
||||
= help: Remove whitespace before '('
|
||||
|
||||
@@ -84,12 +84,12 @@ E20.py:8:6: E201 [*] Whitespace after '('
|
||||
|
||||
E20.py:10:10: E201 [*] Whitespace after '['
|
||||
|
|
||||
8 | spam( ham[1], {eggs: 2})
|
||||
8 | spam( ham[1], {eggs: 2})
|
||||
9 | #: E201:1:10
|
||||
10 | spam(ham[ 1], {eggs: 2})
|
||||
| ^^^ E201
|
||||
10 | spam(ham[ 1], {eggs: 2})
|
||||
| ^^^^ E201
|
||||
11 | #: E201:1:15
|
||||
12 | spam(ham[1], { eggs: 2})
|
||||
12 | spam(ham[1], { eggs: 2})
|
||||
|
|
||||
= help: Remove whitespace before '['
|
||||
|
||||
@@ -105,10 +105,10 @@ E20.py:10:10: E201 [*] Whitespace after '['
|
||||
|
||||
E20.py:12:15: E201 [*] Whitespace after '{'
|
||||
|
|
||||
10 | spam(ham[ 1], {eggs: 2})
|
||||
10 | spam(ham[ 1], {eggs: 2})
|
||||
11 | #: E201:1:15
|
||||
12 | spam(ham[1], { eggs: 2})
|
||||
| ^^ E201
|
||||
12 | spam(ham[1], { eggs: 2})
|
||||
| ^^^^ E201
|
||||
13 | #: Okay
|
||||
14 | spam(ham[1], {eggs: 2})
|
||||
|
|
||||
|
||||
@@ -49,7 +49,7 @@ E20.py:23:11: E202 [*] Whitespace before ']'
|
||||
23 | spam(ham[1 ], {eggs: 2})
|
||||
| ^ E202
|
||||
24 | #: E202:1:23
|
||||
25 | spam(ham[1], {eggs: 2} )
|
||||
25 | spam(ham[1], {eggs: 2} )
|
||||
|
|
||||
= help: Remove whitespace before ']'
|
||||
|
||||
@@ -67,10 +67,10 @@ E20.py:25:23: E202 [*] Whitespace before ')'
|
||||
|
|
||||
23 | spam(ham[1 ], {eggs: 2})
|
||||
24 | #: E202:1:23
|
||||
25 | spam(ham[1], {eggs: 2} )
|
||||
| ^^ E202
|
||||
25 | spam(ham[1], {eggs: 2} )
|
||||
| ^^^^ E202
|
||||
26 | #: E202:1:22
|
||||
27 | spam(ham[1], {eggs: 2 })
|
||||
27 | spam(ham[1], {eggs: 2 })
|
||||
|
|
||||
= help: Remove whitespace before ')'
|
||||
|
||||
@@ -86,12 +86,12 @@ E20.py:25:23: E202 [*] Whitespace before ')'
|
||||
|
||||
E20.py:27:22: E202 [*] Whitespace before '}'
|
||||
|
|
||||
25 | spam(ham[1], {eggs: 2} )
|
||||
25 | spam(ham[1], {eggs: 2} )
|
||||
26 | #: E202:1:22
|
||||
27 | spam(ham[1], {eggs: 2 })
|
||||
| ^^^ E202
|
||||
27 | spam(ham[1], {eggs: 2 })
|
||||
| ^^^^ E202
|
||||
28 | #: E202:1:11
|
||||
29 | spam(ham[1 ], {eggs: 2})
|
||||
29 | spam(ham[1 ], {eggs: 2})
|
||||
|
|
||||
= help: Remove whitespace before '}'
|
||||
|
||||
@@ -107,10 +107,10 @@ E20.py:27:22: E202 [*] Whitespace before '}'
|
||||
|
||||
E20.py:29:11: E202 [*] Whitespace before ']'
|
||||
|
|
||||
27 | spam(ham[1], {eggs: 2 })
|
||||
27 | spam(ham[1], {eggs: 2 })
|
||||
28 | #: E202:1:11
|
||||
29 | spam(ham[1 ], {eggs: 2})
|
||||
| ^^ E202
|
||||
29 | spam(ham[1 ], {eggs: 2})
|
||||
| ^^^^ E202
|
||||
30 | #: Okay
|
||||
31 | spam(ham[1], {eggs: 2})
|
||||
|
|
||||
|
||||
@@ -25,8 +25,8 @@ E20.py:55:10: E203 [*] Whitespace before ':'
|
||||
|
|
||||
53 | x, y = y, x
|
||||
54 | #: E203:1:10
|
||||
55 | if x == 4 :
|
||||
| ^^^ E203
|
||||
55 | if x == 4 :
|
||||
| ^^^^ E203
|
||||
56 | print(x, y)
|
||||
57 | x, y = y, x
|
||||
|
|
||||
@@ -67,8 +67,8 @@ E20.py:63:16: E203 [*] Whitespace before ';'
|
||||
|
|
||||
61 | #: E203:2:15 E702:2:16
|
||||
62 | if x == 4:
|
||||
63 | print(x, y) ; x, y = y, x
|
||||
| ^ E203
|
||||
63 | print(x, y) ; x, y = y, x
|
||||
| ^^^^ E203
|
||||
64 | #: E203:3:13
|
||||
65 | if x == 4:
|
||||
|
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
E22.py:43:2: E223 [*] Tab before operator
|
||||
|
|
||||
41 | #: E223
|
||||
42 | foobart = 4
|
||||
43 | a = 3 # aligned with tab
|
||||
| ^^^ E223
|
||||
43 | a = 3 # aligned with tab
|
||||
| ^^^^ E223
|
||||
44 | #:
|
||||
|
|
||||
= help: Replace with single space
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
E24.py:6:8: E242 [*] Tab after comma
|
||||
|
|
||||
4 | b = (1, 20)
|
||||
5 | #: E242
|
||||
6 | a = (1, 2) # tab before 2
|
||||
| ^ E242
|
||||
6 | a = (1, 2) # tab before 2
|
||||
| ^^^^ E242
|
||||
7 | #: Okay
|
||||
8 | b = (1, 20) # space before 20
|
||||
|
|
||||
|
||||
@@ -66,7 +66,7 @@ E27.py:8:3: E271 [*] Multiple spaces after keyword
|
||||
|
||||
E27.py:15:6: E271 [*] Multiple spaces after keyword
|
||||
|
|
||||
13 | True and False
|
||||
13 | True and False
|
||||
14 | #: E271
|
||||
15 | a and b
|
||||
| ^^ E271
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
E27.py:21:2: E272 [*] Multiple spaces before keyword
|
||||
|
|
||||
@@ -51,7 +50,7 @@ E27.py:25:5: E272 [*] Multiple spaces before keyword
|
||||
25 | this and False
|
||||
| ^^ E272
|
||||
26 | #: E273
|
||||
27 | a and b
|
||||
27 | a and b
|
||||
|
|
||||
= help: Replace with single space
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ E27.py:11:9: E273 [*] Tab after keyword
|
||||
11 | True and False
|
||||
| ^^^^^^^^ E273
|
||||
12 | #: E273 E274
|
||||
13 | True and False
|
||||
13 | True and False
|
||||
|
|
||||
= help: Replace with single space
|
||||
|
||||
@@ -26,7 +26,7 @@ E27.py:13:5: E273 [*] Tab after keyword
|
||||
|
|
||||
11 | True and False
|
||||
12 | #: E273 E274
|
||||
13 | True and False
|
||||
13 | True and False
|
||||
| ^^^^^^^^ E273
|
||||
14 | #: E271
|
||||
15 | a and b
|
||||
@@ -47,8 +47,8 @@ E27.py:13:10: E273 [*] Tab after keyword
|
||||
|
|
||||
11 | True and False
|
||||
12 | #: E273 E274
|
||||
13 | True and False
|
||||
| ^ E273
|
||||
13 | True and False
|
||||
| ^^^^ E273
|
||||
14 | #: E271
|
||||
15 | a and b
|
||||
|
|
||||
@@ -68,10 +68,10 @@ E27.py:27:6: E273 [*] Tab after keyword
|
||||
|
|
||||
25 | this and False
|
||||
26 | #: E273
|
||||
27 | a and b
|
||||
| ^^^ E273
|
||||
27 | a and b
|
||||
| ^^^^ E273
|
||||
28 | #: E274
|
||||
29 | a and b
|
||||
29 | a and b
|
||||
|
|
||||
= help: Replace with single space
|
||||
|
||||
@@ -87,10 +87,10 @@ E27.py:27:6: E273 [*] Tab after keyword
|
||||
|
||||
E27.py:31:10: E273 [*] Tab after keyword
|
||||
|
|
||||
29 | a and b
|
||||
29 | a and b
|
||||
30 | #: E273 E274
|
||||
31 | this and False
|
||||
| ^ E273
|
||||
31 | this and False
|
||||
| ^^^^ E273
|
||||
32 | #: Okay
|
||||
33 | from u import (a, b)
|
||||
|
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
E27.py:29:2: E274 [*] Tab before keyword
|
||||
|
|
||||
27 | a and b
|
||||
27 | a and b
|
||||
28 | #: E274
|
||||
29 | a and b
|
||||
| ^^^^^^^ E274
|
||||
29 | a and b
|
||||
| ^^^^^^^^ E274
|
||||
30 | #: E273 E274
|
||||
31 | this and False
|
||||
31 | this and False
|
||||
|
|
||||
= help: Replace with single space
|
||||
|
||||
@@ -25,9 +24,9 @@ E27.py:29:2: E274 [*] Tab before keyword
|
||||
|
||||
E27.py:31:5: E274 [*] Tab before keyword
|
||||
|
|
||||
29 | a and b
|
||||
29 | a and b
|
||||
30 | #: E273 E274
|
||||
31 | this and False
|
||||
31 | this and False
|
||||
| ^^^^^^^^ E274
|
||||
32 | #: Okay
|
||||
33 | from u import (a, b)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
W19.py:1:1: W191 Indentation contains tabs
|
||||
|
|
||||
@@ -371,7 +370,7 @@ W19.py:157:1: W191 Indentation contains tabs
|
||||
156 | f"test{
|
||||
157 | tab_indented_should_be_flagged
|
||||
| ^^^^ W191
|
||||
158 | } <- this tab is fine"
|
||||
158 | } <- this tab is fine"
|
||||
|
|
||||
|
||||
W19.py:161:1: W191 Indentation contains tabs
|
||||
@@ -379,5 +378,5 @@ W19.py:161:1: W191 Indentation contains tabs
|
||||
160 | f"""test{
|
||||
161 | tab_indented_should_be_flagged
|
||||
| ^^^^ W191
|
||||
162 | } <- this tab is fine"""
|
||||
162 | } <- this tab is fine"""
|
||||
|
|
||||
|
||||
@@ -103,7 +103,7 @@ use crate::{Applicability, Fix, FixAvailability, Violation};
|
||||
/// ## References
|
||||
/// - [Python documentation: `import`](https://docs.python.org/3/reference/simple_stmts.html#the-import-statement)
|
||||
/// - [Python documentation: `importlib.util.find_spec`](https://docs.python.org/3/library/importlib.html#importlib.util.find_spec)
|
||||
/// - [Typing documentation: interface conventions](https://typing.python.org/en/latest/source/libraries.html#library-interface-public-and-private-symbols)
|
||||
/// - [Typing documentation: interface conventions](https://typing.python.org/en/latest/spec/distributing.html#library-interface-public-and-private-symbols)
|
||||
#[derive(ViolationMetadata)]
|
||||
pub(crate) struct UnusedImport {
|
||||
/// Qualified name of the import
|
||||
@@ -255,7 +255,7 @@ fn is_first_party(import: &AnyImport, checker: &Checker) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the `Expr` for top level `__all__` bindings.
|
||||
/// Find the `Expr` for top-level `__all__` bindings.
|
||||
fn find_dunder_all_exprs<'a>(semantic: &'a SemanticModel) -> Vec<&'a ast::Expr> {
|
||||
semantic
|
||||
.global_scope()
|
||||
@@ -541,7 +541,7 @@ fn fix_by_removing_imports<'a>(
|
||||
checker.indexer(),
|
||||
)?;
|
||||
|
||||
// It's unsafe to remove things from `__init__.py` because it can break public interfaces
|
||||
// It's unsafe to remove things from `__init__.py` because it can break public interfaces.
|
||||
let applicability = if in_init {
|
||||
Applicability::Unsafe
|
||||
} else {
|
||||
@@ -556,7 +556,7 @@ fn fix_by_removing_imports<'a>(
|
||||
}
|
||||
|
||||
/// Generate a [`Fix`] to make bindings in a statement explicit, either by adding them to `__all__`
|
||||
/// or changing them from `import a` to `import a as a`.
|
||||
/// or by changing them from `import a` to `import a as a`.
|
||||
fn fix_by_reexporting<'a>(
|
||||
checker: &Checker,
|
||||
node_id: NodeId,
|
||||
@@ -585,7 +585,7 @@ fn fix_by_reexporting<'a>(
|
||||
_ => bail!("Cannot offer a fix when there are multiple __all__ definitions"),
|
||||
};
|
||||
|
||||
// Only emit a fix if there are edits
|
||||
// Only emit a fix if there are edits.
|
||||
let mut tail = edits.into_iter();
|
||||
let head = tail.next().ok_or(anyhow!("No edits to make"))?;
|
||||
|
||||
|
||||
@@ -7,16 +7,18 @@ pub(crate) mod types;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::BTreeSet;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use ruff_python_ast::PythonVersion;
|
||||
use ruff_python_semantic::{MemberNameImport, NameImport};
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::registry::Rule;
|
||||
use crate::rules::pyupgrade;
|
||||
use crate::rules::{isort, pyupgrade};
|
||||
use crate::settings::types::PreviewMode;
|
||||
use crate::test::test_path;
|
||||
use crate::test::{test_path, test_snippet};
|
||||
use crate::{assert_diagnostics, settings};
|
||||
|
||||
#[test_case(Rule::ConvertNamedTupleFunctionalToClass, Path::new("UP014.py"))]
|
||||
@@ -294,4 +296,63 @@ mod tests {
|
||||
assert_diagnostics!(diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i002_conflict() {
|
||||
let diagnostics = test_snippet(
|
||||
"from pipes import quote, Template",
|
||||
&settings::LinterSettings {
|
||||
isort: isort::settings::Settings {
|
||||
required_imports: BTreeSet::from_iter([
|
||||
// https://github.com/astral-sh/ruff/issues/18729
|
||||
NameImport::ImportFrom(MemberNameImport::member(
|
||||
"__future__".to_string(),
|
||||
"generator_stop".to_string(),
|
||||
)),
|
||||
// https://github.com/astral-sh/ruff/issues/16802
|
||||
NameImport::ImportFrom(MemberNameImport::member(
|
||||
"collections".to_string(),
|
||||
"Sequence".to_string(),
|
||||
)),
|
||||
// Only bail out if _all_ the names in UP035 are required. `pipes.Template`
|
||||
// isn't flagged by UP035, so requiring it shouldn't prevent `pipes.quote`
|
||||
// from getting a diagnostic.
|
||||
NameImport::ImportFrom(MemberNameImport::member(
|
||||
"pipes".to_string(),
|
||||
"Template".to_string(),
|
||||
)),
|
||||
]),
|
||||
..Default::default()
|
||||
},
|
||||
..settings::LinterSettings::for_rules([
|
||||
Rule::MissingRequiredImport,
|
||||
Rule::UnnecessaryFutureImport,
|
||||
Rule::DeprecatedImport,
|
||||
])
|
||||
},
|
||||
);
|
||||
assert_diagnostics!(diagnostics, @r"
|
||||
<filename>:1:1: UP035 [*] Import from `shlex` instead: `quote`
|
||||
|
|
||||
1 | from pipes import quote, Template
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UP035
|
||||
|
|
||||
= help: Import from `shlex`
|
||||
|
||||
ℹ Safe fix
|
||||
1 |-from pipes import quote, Template
|
||||
1 |+from pipes import Template
|
||||
2 |+from shlex import quote
|
||||
|
||||
<filename>:1:1: I002 [*] Missing required import: `from __future__ import generator_stop`
|
||||
ℹ Safe fix
|
||||
1 |+from __future__ import generator_stop
|
||||
1 2 | from pipes import quote, Template
|
||||
|
||||
<filename>:1:1: I002 [*] Missing required import: `from collections import Sequence`
|
||||
ℹ Safe fix
|
||||
1 |+from collections import Sequence
|
||||
1 2 | from pipes import quote, Template
|
||||
");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use itertools::Itertools;
|
||||
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::whitespace::indentation;
|
||||
use ruff_python_ast::{Alias, StmtImportFrom};
|
||||
use ruff_python_ast::{Alias, StmtImportFrom, StmtRef};
|
||||
use ruff_python_codegen::Stylist;
|
||||
use ruff_python_parser::Tokens;
|
||||
use ruff_text_size::Ranged;
|
||||
@@ -10,9 +10,12 @@ use ruff_text_size::Ranged;
|
||||
use crate::Locator;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::rules::pyupgrade::fixes;
|
||||
use crate::rules::pyupgrade::rules::unnecessary_future_import::is_import_required_by_isort;
|
||||
use crate::{Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_python_ast::PythonVersion;
|
||||
|
||||
use super::RequiredImports;
|
||||
|
||||
/// An import was moved and renamed as part of a deprecation.
|
||||
/// For example, `typing.AbstractSet` was moved to `collections.abc.Set`.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
@@ -410,6 +413,7 @@ struct ImportReplacer<'a> {
|
||||
stylist: &'a Stylist<'a>,
|
||||
tokens: &'a Tokens,
|
||||
version: PythonVersion,
|
||||
required_imports: &'a RequiredImports,
|
||||
}
|
||||
|
||||
impl<'a> ImportReplacer<'a> {
|
||||
@@ -420,6 +424,7 @@ impl<'a> ImportReplacer<'a> {
|
||||
stylist: &'a Stylist<'a>,
|
||||
tokens: &'a Tokens,
|
||||
version: PythonVersion,
|
||||
required_imports: &'a RequiredImports,
|
||||
) -> Self {
|
||||
Self {
|
||||
import_from_stmt,
|
||||
@@ -428,6 +433,7 @@ impl<'a> ImportReplacer<'a> {
|
||||
stylist,
|
||||
tokens,
|
||||
version,
|
||||
required_imports,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,6 +443,13 @@ impl<'a> ImportReplacer<'a> {
|
||||
if self.module == "typing" {
|
||||
if self.version >= PythonVersion::PY39 {
|
||||
for member in &self.import_from_stmt.names {
|
||||
if is_import_required_by_isort(
|
||||
self.required_imports,
|
||||
StmtRef::ImportFrom(self.import_from_stmt),
|
||||
member,
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
if let Some(target) = TYPING_TO_RENAME_PY39.iter().find_map(|(name, target)| {
|
||||
if &member.name == *name {
|
||||
Some(*target)
|
||||
@@ -673,7 +686,13 @@ impl<'a> ImportReplacer<'a> {
|
||||
let mut matched_names = vec![];
|
||||
let mut unmatched_names = vec![];
|
||||
for name in &self.import_from_stmt.names {
|
||||
if candidates.contains(&name.name.as_str()) {
|
||||
if is_import_required_by_isort(
|
||||
self.required_imports,
|
||||
StmtRef::ImportFrom(self.import_from_stmt),
|
||||
name,
|
||||
) {
|
||||
unmatched_names.push(name);
|
||||
} else if candidates.contains(&name.name.as_str()) {
|
||||
matched_names.push(name);
|
||||
} else {
|
||||
unmatched_names.push(name);
|
||||
@@ -726,6 +745,7 @@ pub(crate) fn deprecated_import(checker: &Checker, import_from_stmt: &StmtImport
|
||||
checker.stylist(),
|
||||
checker.tokens(),
|
||||
checker.target_version(),
|
||||
&checker.settings().isort.required_imports,
|
||||
);
|
||||
|
||||
for (operation, fix) in fixer.without_renames() {
|
||||
|
||||
@@ -124,10 +124,20 @@ fn is_sequential(indices: &[usize]) -> bool {
|
||||
indices.iter().enumerate().all(|(idx, value)| idx == *value)
|
||||
}
|
||||
|
||||
// An opening curly brace, followed by any integer, followed by any text,
|
||||
// followed by a closing brace.
|
||||
static FORMAT_SPECIFIER: LazyLock<Regex> =
|
||||
LazyLock::new(|| Regex::new(r"\{(?P<int>\d+)(?P<fmt>.*?)}").unwrap());
|
||||
static FORMAT_SPECIFIER: LazyLock<Regex> = LazyLock::new(|| {
|
||||
Regex::new(
|
||||
r"(?x)
|
||||
(?P<prefix>
|
||||
^|[^{]|(?:\{{2})+ # preceded by nothing, a non-brace, or an even number of braces
|
||||
)
|
||||
\{ # opening curly brace
|
||||
(?P<int>\d+) # followed by any integer
|
||||
(?P<fmt>.*?) # followed by any text
|
||||
} # followed by a closing brace
|
||||
",
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
/// Remove the explicit positional indices from a format string.
|
||||
fn remove_specifiers<'a>(value: &mut Expression<'a>, arena: &'a typed_arena::Arena<String>) {
|
||||
@@ -135,7 +145,7 @@ fn remove_specifiers<'a>(value: &mut Expression<'a>, arena: &'a typed_arena::Are
|
||||
Expression::SimpleString(expr) => {
|
||||
expr.value = arena.alloc(
|
||||
FORMAT_SPECIFIER
|
||||
.replace_all(expr.value, "{$fmt}")
|
||||
.replace_all(expr.value, "$prefix{$fmt}")
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
@@ -146,7 +156,7 @@ fn remove_specifiers<'a>(value: &mut Expression<'a>, arena: &'a typed_arena::Are
|
||||
libcst_native::String::Simple(string) => {
|
||||
string.value = arena.alloc(
|
||||
FORMAT_SPECIFIER
|
||||
.replace_all(string.value, "{$fmt}")
|
||||
.replace_all(string.value, "$prefix{$fmt}")
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use itertools::Itertools;
|
||||
use ruff_python_ast::{Alias, Stmt};
|
||||
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::{self as ast, Alias, Stmt, StmtRef};
|
||||
use ruff_python_semantic::NameImport;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
@@ -84,6 +87,29 @@ const PY37_PLUS_REMOVE_FUTURES: &[&str] = &[
|
||||
"generator_stop",
|
||||
];
|
||||
|
||||
pub(crate) type RequiredImports = BTreeSet<NameImport>;
|
||||
|
||||
pub(crate) fn is_import_required_by_isort(
|
||||
required_imports: &RequiredImports,
|
||||
stmt: StmtRef,
|
||||
alias: &Alias,
|
||||
) -> bool {
|
||||
let segments: &[&str] = match stmt {
|
||||
StmtRef::ImportFrom(ast::StmtImportFrom {
|
||||
module: Some(module),
|
||||
..
|
||||
}) => &[module.as_str(), alias.name.as_str()],
|
||||
StmtRef::ImportFrom(ast::StmtImportFrom { module: None, .. }) | StmtRef::Import(_) => {
|
||||
&[alias.name.as_str()]
|
||||
}
|
||||
_ => return false,
|
||||
};
|
||||
|
||||
required_imports
|
||||
.iter()
|
||||
.any(|required_import| required_import.qualified_name().segments() == segments)
|
||||
}
|
||||
|
||||
/// UP010
|
||||
pub(crate) fn unnecessary_future_import(checker: &Checker, stmt: &Stmt, names: &[Alias]) {
|
||||
let mut unused_imports: Vec<&Alias> = vec![];
|
||||
@@ -91,6 +117,15 @@ pub(crate) fn unnecessary_future_import(checker: &Checker, stmt: &Stmt, names: &
|
||||
if alias.asname.is_some() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if is_import_required_by_isort(
|
||||
&checker.settings().isort.required_imports,
|
||||
stmt.into(),
|
||||
alias,
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if PY33_PLUS_REMOVE_FUTURES.contains(&alias.name.as_str())
|
||||
|| PY37_PLUS_REMOVE_FUTURES.contains(&alias.name.as_str())
|
||||
{
|
||||
@@ -119,7 +154,7 @@ pub(crate) fn unnecessary_future_import(checker: &Checker, stmt: &Stmt, names: &
|
||||
unused_imports
|
||||
.iter()
|
||||
.map(|alias| &alias.name)
|
||||
.map(ruff_python_ast::Identifier::as_str),
|
||||
.map(ast::Identifier::as_str),
|
||||
statement,
|
||||
parent,
|
||||
checker.locator(),
|
||||
|
||||
@@ -481,6 +481,7 @@ UP030_0.py:59:1: UP030 [*] Use implicit references for positional format fields
|
||||
59 |+"{}_{}".format(2, 1, )
|
||||
60 60 |
|
||||
61 61 | "{1}_{0}".format(1, 2)
|
||||
62 62 |
|
||||
|
||||
UP030_0.py:61:1: UP030 [*] Use implicit references for positional format fields
|
||||
|
|
||||
@@ -488,6 +489,8 @@ UP030_0.py:61:1: UP030 [*] Use implicit references for positional format fields
|
||||
60 |
|
||||
61 | "{1}_{0}".format(1, 2)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ UP030
|
||||
62 |
|
||||
63 | r"\d{{1,2}} {0}".format(42)
|
||||
|
|
||||
= help: Remove explicit positional indices
|
||||
|
||||
@@ -497,3 +500,42 @@ UP030_0.py:61:1: UP030 [*] Use implicit references for positional format fields
|
||||
60 60 |
|
||||
61 |-"{1}_{0}".format(1, 2)
|
||||
61 |+"{}_{}".format(2, 1)
|
||||
62 62 |
|
||||
63 63 | r"\d{{1,2}} {0}".format(42)
|
||||
64 64 |
|
||||
|
||||
UP030_0.py:63:1: UP030 [*] Use implicit references for positional format fields
|
||||
|
|
||||
61 | "{1}_{0}".format(1, 2)
|
||||
62 |
|
||||
63 | r"\d{{1,2}} {0}".format(42)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ UP030
|
||||
64 |
|
||||
65 | "{{{0}}}".format(123)
|
||||
|
|
||||
= help: Remove explicit positional indices
|
||||
|
||||
ℹ Unsafe fix
|
||||
60 60 |
|
||||
61 61 | "{1}_{0}".format(1, 2)
|
||||
62 62 |
|
||||
63 |-r"\d{{1,2}} {0}".format(42)
|
||||
63 |+r"\d{{1,2}} {}".format(42)
|
||||
64 64 |
|
||||
65 65 | "{{{0}}}".format(123)
|
||||
|
||||
UP030_0.py:65:1: UP030 [*] Use implicit references for positional format fields
|
||||
|
|
||||
63 | r"\d{{1,2}} {0}".format(42)
|
||||
64 |
|
||||
65 | "{{{0}}}".format(123)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ UP030
|
||||
|
|
||||
= help: Remove explicit positional indices
|
||||
|
||||
ℹ Unsafe fix
|
||||
62 62 |
|
||||
63 63 | r"\d{{1,2}} {0}".format(42)
|
||||
64 64 |
|
||||
65 |-"{{{0}}}".format(123)
|
||||
65 |+"{{{}}}".format(123)
|
||||
|
||||
@@ -22,13 +22,19 @@ use crate::{AlwaysFixableViolation, Edit, Fix};
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// class C(metaclass=ABCMeta):
|
||||
/// import abc
|
||||
///
|
||||
///
|
||||
/// class C(metaclass=abc.ABCMeta):
|
||||
/// pass
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// class C(ABC):
|
||||
/// import abc
|
||||
///
|
||||
///
|
||||
/// class C(abc.ABC):
|
||||
/// pass
|
||||
/// ```
|
||||
///
|
||||
|
||||
@@ -26,6 +26,9 @@ use crate::{Applicability, Edit, Fix, FixAvailability, Violation};
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// from decimal import Decimal
|
||||
/// from fractions import Fraction
|
||||
///
|
||||
/// Decimal.from_float(4.2)
|
||||
/// Decimal.from_float(float("inf"))
|
||||
/// Fraction.from_float(4.2)
|
||||
@@ -34,10 +37,13 @@ use crate::{Applicability, Edit, Fix, FixAvailability, Violation};
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// from decimal import Decimal
|
||||
/// from fractions import Fraction
|
||||
///
|
||||
/// Decimal(4.2)
|
||||
/// Decimal("inf")
|
||||
/// Fraction(4.2)
|
||||
/// Fraction(Decimal(4.2))
|
||||
/// Fraction(Decimal("4.2"))
|
||||
/// ```
|
||||
///
|
||||
/// ## Fix safety
|
||||
|
||||
@@ -30,12 +30,16 @@ use crate::{Edit, Fix, FixAvailability, Violation};
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// from decimal import Decimal
|
||||
///
|
||||
/// Decimal("0")
|
||||
/// Decimal(float("Infinity"))
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// from decimal import Decimal
|
||||
///
|
||||
/// Decimal(0)
|
||||
/// Decimal("Infinity")
|
||||
/// ```
|
||||
|
||||
@@ -12,7 +12,6 @@ use crate::checkers::ast::{Checker, LintContext};
|
||||
use crate::preview::is_unicode_to_unicode_confusables_enabled;
|
||||
use crate::rules::ruff::rules::Context;
|
||||
use crate::rules::ruff::rules::confusables::confusable;
|
||||
use crate::settings::LinterSettings;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for ambiguous Unicode characters in strings.
|
||||
@@ -180,9 +179,7 @@ pub(crate) fn ambiguous_unicode_character_comment(
|
||||
range: TextRange,
|
||||
) {
|
||||
let text = locator.slice(range);
|
||||
for candidate in ambiguous_unicode_character(text, range, context.settings()) {
|
||||
candidate.into_diagnostic(Context::Comment, context);
|
||||
}
|
||||
ambiguous_unicode_character(text, range, Context::Comment, context);
|
||||
}
|
||||
|
||||
/// RUF001, RUF002
|
||||
@@ -203,22 +200,19 @@ pub(crate) fn ambiguous_unicode_character_string(checker: &Checker, string_like:
|
||||
match part {
|
||||
ast::StringLikePart::String(string_literal) => {
|
||||
let text = checker.locator().slice(string_literal);
|
||||
for candidate in
|
||||
ambiguous_unicode_character(text, string_literal.range(), checker.settings())
|
||||
{
|
||||
candidate.report_diagnostic(checker, context);
|
||||
}
|
||||
ambiguous_unicode_character(
|
||||
text,
|
||||
string_literal.range(),
|
||||
context,
|
||||
checker.context(),
|
||||
);
|
||||
}
|
||||
ast::StringLikePart::Bytes(_) => {}
|
||||
ast::StringLikePart::FString(FString { elements, .. })
|
||||
| ast::StringLikePart::TString(TString { elements, .. }) => {
|
||||
for literal in elements.literals() {
|
||||
let text = checker.locator().slice(literal);
|
||||
for candidate in
|
||||
ambiguous_unicode_character(text, literal.range(), checker.settings())
|
||||
{
|
||||
candidate.report_diagnostic(checker, context);
|
||||
}
|
||||
ambiguous_unicode_character(text, literal.range(), context, checker.context());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -228,13 +222,12 @@ pub(crate) fn ambiguous_unicode_character_string(checker: &Checker, string_like:
|
||||
fn ambiguous_unicode_character(
|
||||
text: &str,
|
||||
range: TextRange,
|
||||
settings: &LinterSettings,
|
||||
) -> Vec<Candidate> {
|
||||
let mut candidates = Vec::new();
|
||||
|
||||
context: Context,
|
||||
lint_context: &LintContext,
|
||||
) {
|
||||
// Most of the time, we don't need to check for ambiguous unicode characters at all.
|
||||
if text.is_ascii() {
|
||||
return candidates;
|
||||
return;
|
||||
}
|
||||
|
||||
// Iterate over the "words" in the text.
|
||||
@@ -246,7 +239,7 @@ fn ambiguous_unicode_character(
|
||||
if !word_candidates.is_empty() {
|
||||
if word_flags.is_candidate_word() {
|
||||
for candidate in word_candidates.drain(..) {
|
||||
candidates.push(candidate);
|
||||
candidate.into_diagnostic(context, lint_context);
|
||||
}
|
||||
}
|
||||
word_candidates.clear();
|
||||
@@ -257,21 +250,23 @@ fn ambiguous_unicode_character(
|
||||
// case, it's always included as a diagnostic.
|
||||
if !current_char.is_ascii() {
|
||||
if let Some(representant) = confusable(current_char as u32).filter(|representant| {
|
||||
is_unicode_to_unicode_confusables_enabled(settings) || representant.is_ascii()
|
||||
is_unicode_to_unicode_confusables_enabled(lint_context.settings())
|
||||
|| representant.is_ascii()
|
||||
}) {
|
||||
let candidate = Candidate::new(
|
||||
TextSize::try_from(relative_offset).unwrap() + range.start(),
|
||||
current_char,
|
||||
representant,
|
||||
);
|
||||
candidates.push(candidate);
|
||||
candidate.into_diagnostic(context, lint_context);
|
||||
}
|
||||
}
|
||||
} else if current_char.is_ascii() {
|
||||
// The current word contains at least one ASCII character.
|
||||
word_flags |= WordFlags::ASCII;
|
||||
} else if let Some(representant) = confusable(current_char as u32).filter(|representant| {
|
||||
is_unicode_to_unicode_confusables_enabled(settings) || representant.is_ascii()
|
||||
is_unicode_to_unicode_confusables_enabled(lint_context.settings())
|
||||
|| representant.is_ascii()
|
||||
}) {
|
||||
// The current word contains an ambiguous unicode character.
|
||||
word_candidates.push(Candidate::new(
|
||||
@@ -289,13 +284,11 @@ fn ambiguous_unicode_character(
|
||||
if !word_candidates.is_empty() {
|
||||
if word_flags.is_candidate_word() {
|
||||
for candidate in word_candidates.drain(..) {
|
||||
candidates.push(candidate);
|
||||
candidate.into_diagnostic(context, lint_context);
|
||||
}
|
||||
}
|
||||
word_candidates.clear();
|
||||
}
|
||||
|
||||
candidates
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
@@ -373,39 +366,6 @@ impl Candidate {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn report_diagnostic(self, checker: &Checker, context: Context) {
|
||||
if !checker
|
||||
.settings()
|
||||
.allowed_confusables
|
||||
.contains(&self.confusable)
|
||||
{
|
||||
let char_range = TextRange::at(self.offset, self.confusable.text_len());
|
||||
match context {
|
||||
Context::String => checker.report_diagnostic_if_enabled(
|
||||
AmbiguousUnicodeCharacterString {
|
||||
confusable: self.confusable,
|
||||
representant: self.representant,
|
||||
},
|
||||
char_range,
|
||||
),
|
||||
Context::Docstring => checker.report_diagnostic_if_enabled(
|
||||
AmbiguousUnicodeCharacterDocstring {
|
||||
confusable: self.confusable,
|
||||
representant: self.representant,
|
||||
},
|
||||
char_range,
|
||||
),
|
||||
Context::Comment => checker.report_diagnostic_if_enabled(
|
||||
AmbiguousUnicodeCharacterComment {
|
||||
confusable: self.confusable,
|
||||
representant: self.representant,
|
||||
},
|
||||
char_range,
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct NamedUnicode(char);
|
||||
|
||||
@@ -149,8 +149,7 @@ fn convert_call_to_conversion_flag(
|
||||
formatted_string_expression.whitespace_before_expression = space();
|
||||
}
|
||||
|
||||
formatted_string_expression.expression = if needs_paren(OperatorPrecedence::from_expr(arg))
|
||||
{
|
||||
formatted_string_expression.expression = if needs_paren_expr(arg) {
|
||||
call.args[0]
|
||||
.value
|
||||
.clone()
|
||||
@@ -178,6 +177,16 @@ fn needs_paren(precedence: OperatorPrecedence) -> bool {
|
||||
precedence <= OperatorPrecedence::Lambda
|
||||
}
|
||||
|
||||
fn needs_paren_expr(arg: &Expr) -> bool {
|
||||
// Generator expressions need to be parenthesized in f-string expressions
|
||||
if let Some(generator) = arg.as_generator_expr() {
|
||||
return !generator.parenthesized;
|
||||
}
|
||||
|
||||
// Check precedence for other expressions
|
||||
needs_paren(OperatorPrecedence::from_expr(arg))
|
||||
}
|
||||
|
||||
/// Represents the three built-in Python conversion functions that can be replaced
|
||||
/// with f-string conversion flags.
|
||||
#[derive(Copy, Clone)]
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::checkers::ast::Checker;
|
||||
/// Checks for type annotations where `None` is not at the end of an union.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Type annotation unions are associative, meaning that the order of the elements
|
||||
/// Type annotation unions are commutative, meaning that the order of the elements
|
||||
/// does not matter. The `None` literal represents the absence of a value. For
|
||||
/// readability, it's preferred to write the more informative type expressions first.
|
||||
///
|
||||
|
||||
@@ -359,6 +359,7 @@ RUF010.py:52:4: RUF010 [*] Use explicit conversion flag
|
||||
52 |+f"{(x := 2)!r}"
|
||||
53 53 |
|
||||
54 54 | f"{str(object=3)}"
|
||||
55 55 |
|
||||
|
||||
RUF010.py:54:4: RUF010 [*] Use explicit conversion flag
|
||||
|
|
||||
@@ -366,6 +367,8 @@ RUF010.py:54:4: RUF010 [*] Use explicit conversion flag
|
||||
53 |
|
||||
54 | f"{str(object=3)}"
|
||||
| ^^^^^^^^^^^^^ RUF010
|
||||
55 |
|
||||
56 | f"{str(x for x in [])}"
|
||||
|
|
||||
= help: Replace with conversion flag
|
||||
|
||||
@@ -375,3 +378,42 @@ RUF010.py:54:4: RUF010 [*] Use explicit conversion flag
|
||||
53 53 |
|
||||
54 |-f"{str(object=3)}"
|
||||
54 |+f"{3!s}"
|
||||
55 55 |
|
||||
56 56 | f"{str(x for x in [])}"
|
||||
57 57 |
|
||||
|
||||
RUF010.py:56:4: RUF010 [*] Use explicit conversion flag
|
||||
|
|
||||
54 | f"{str(object=3)}"
|
||||
55 |
|
||||
56 | f"{str(x for x in [])}"
|
||||
| ^^^^^^^^^^^^^^^^^^ RUF010
|
||||
57 |
|
||||
58 | f"{str((x for x in []))}"
|
||||
|
|
||||
= help: Replace with conversion flag
|
||||
|
||||
ℹ Safe fix
|
||||
53 53 |
|
||||
54 54 | f"{str(object=3)}"
|
||||
55 55 |
|
||||
56 |-f"{str(x for x in [])}"
|
||||
56 |+f"{(x for x in [])!s}"
|
||||
57 57 |
|
||||
58 58 | f"{str((x for x in []))}"
|
||||
|
||||
RUF010.py:58:4: RUF010 [*] Use explicit conversion flag
|
||||
|
|
||||
56 | f"{str(x for x in [])}"
|
||||
57 |
|
||||
58 | f"{str((x for x in []))}"
|
||||
| ^^^^^^^^^^^^^^^^^^^^ RUF010
|
||||
|
|
||||
= help: Replace with conversion flag
|
||||
|
||||
ℹ Safe fix
|
||||
55 55 |
|
||||
56 56 | f"{str(x for x in [])}"
|
||||
57 57 |
|
||||
58 |-f"{str((x for x in []))}"
|
||||
58 |+f"{(x for x in [])!s}"
|
||||
|
||||
@@ -22,7 +22,7 @@ RUF054.py:10:3: RUF054 Indented form feed
|
||||
RUF054.py:13:2: RUF054 Indented form feed
|
||||
|
|
||||
12 | def _():
|
||||
13 | pass
|
||||
13 | pass
|
||||
| ^ RUF054
|
||||
14 |
|
||||
15 | if False:
|
||||
|
||||
@@ -380,7 +380,7 @@ macro_rules! assert_diagnostics {
|
||||
}};
|
||||
($value:expr, @$snapshot:literal) => {{
|
||||
insta::with_settings!({ omit_expression => true }, {
|
||||
insta::assert_snapshot!($crate::test::print_messages(&$value), $snapshot);
|
||||
insta::assert_snapshot!($crate::test::print_messages(&$value), @$snapshot);
|
||||
});
|
||||
}};
|
||||
($name:expr, $value:expr) => {{
|
||||
|
||||
@@ -33,6 +33,7 @@ pub(in crate::server) enum BackgroundSchedule {
|
||||
/// while local tasks have exclusive access and can modify it as they please. Keep in mind that
|
||||
/// local tasks will **block** the main event loop, so only use local tasks if you **need**
|
||||
/// mutable state access or you need the absolute lowest latency possible.
|
||||
#[must_use]
|
||||
pub(in crate::server) enum Task {
|
||||
Background(BackgroundTaskBuilder),
|
||||
Sync(SyncTask),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use std::hash::Hash;
|
||||
use std::sync::{Arc, OnceLock};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
@@ -162,7 +163,7 @@ impl SourceFileBuilder {
|
||||
/// A source file that is identified by its name. Optionally stores the source code and [`LineIndex`].
|
||||
///
|
||||
/// Cloning a [`SourceFile`] is cheap, because it only requires bumping a reference count.
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
||||
#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))]
|
||||
pub struct SourceFile {
|
||||
inner: Arc<SourceFileInner>,
|
||||
@@ -241,6 +242,13 @@ impl PartialEq for SourceFileInner {
|
||||
|
||||
impl Eq for SourceFileInner {}
|
||||
|
||||
impl Hash for SourceFileInner {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.name.hash(state);
|
||||
self.code.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
/// The line and column of an offset in a source file.
|
||||
///
|
||||
/// See [`LineIndex::line_column`] for more information.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_wasm"
|
||||
version = "0.12.5"
|
||||
version = "0.12.7"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
|
||||
@@ -25,6 +25,7 @@ anyhow = { workspace = true }
|
||||
argfile = { workspace = true }
|
||||
clap = { workspace = true, features = ["wrap_help", "string", "env"] }
|
||||
clap_complete_command = { workspace = true }
|
||||
clearscreen = { workspace = true }
|
||||
colored = { workspace = true }
|
||||
crossbeam = { workspace = true }
|
||||
ctrlc = { version = "3.4.4" }
|
||||
|
||||
118
crates/ty/docs/rules.md
generated
118
crates/ty/docs/rules.md
generated
@@ -36,7 +36,7 @@ def test(): -> "int":
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20call-non-callable) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L100)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L99)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -58,7 +58,7 @@ Calling a non-callable object will raise a `TypeError` at runtime.
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-argument-forms) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L144)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L143)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -88,7 +88,7 @@ f(int) # error
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-declarations) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L170)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L169)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -117,7 +117,7 @@ a = 1
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-metaclass) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L195)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L194)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -147,7 +147,7 @@ class C(A, B): ...
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20cyclic-class-definition) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L221)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L220)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -177,7 +177,7 @@ class B(A): ...
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20duplicate-base) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L286)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L285)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -202,7 +202,7 @@ class B(A, A): ...
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20duplicate-kw-only) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L307)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L306)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -306,7 +306,7 @@ def test(): -> "Literal[5]":
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20inconsistent-mro) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L449)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L448)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -334,7 +334,7 @@ class C(A, B): ...
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20index-out-of-bounds) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L473)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L472)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -358,7 +358,7 @@ t[3] # IndexError: tuple index out of range
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20instance-layout-conflict) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L339)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L338)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -445,7 +445,7 @@ an atypical memory layout.
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-argument-type) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L493)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L492)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -470,7 +470,7 @@ func("foo") # error: [invalid-argument-type]
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-assignment) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L533)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L532)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -496,7 +496,7 @@ a: int = ''
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-attribute-access) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1537)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1536)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -528,7 +528,7 @@ C.instance_var = 3 # error: Cannot assign to instance variable
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-base) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L555)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L554)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -550,7 +550,7 @@ class A(42): ... # error: [invalid-base]
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-context-manager) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L606)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L605)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -575,7 +575,7 @@ with 1:
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-declaration) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L627)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L626)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -602,7 +602,7 @@ a: str
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-exception-caught) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L650)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L649)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -644,7 +644,7 @@ except ZeroDivisionError:
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-generic-class) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L686)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L685)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -675,7 +675,7 @@ class C[U](Generic[T]): ...
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-legacy-type-variable) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L712)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L711)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -708,7 +708,7 @@ def f(t: TypeVar("U")): ...
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-metaclass) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L761)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L760)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -740,7 +740,7 @@ class B(metaclass=f): ...
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-overload) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L788)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L787)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -788,7 +788,7 @@ def foo(x: int) -> int: ...
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-parameter-default) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L831)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L830)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -812,7 +812,7 @@ def f(a: int = ''): ...
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-protocol) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L421)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L420)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -844,7 +844,7 @@ TypeError: Protocols can only inherit from other protocols, got <class 'int'>
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-raise) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L851)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L850)
|
||||
</small>
|
||||
|
||||
Checks for `raise` statements that raise non-exceptions or use invalid
|
||||
@@ -891,7 +891,7 @@ def g():
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-return-type) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L514)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L513)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -914,7 +914,7 @@ def func() -> int:
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-super-argument) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L894)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L893)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -968,7 +968,7 @@ TODO #14889
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-alias-type) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L740)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L739)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -993,7 +993,7 @@ NewAlias = TypeAliasType(get_name(), int) # error: TypeAliasType name mus
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-checking-constant) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L933)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L932)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1021,7 +1021,7 @@ TYPE_CHECKING = ''
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-form) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L957)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L956)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1049,7 +1049,7 @@ b: Annotated[int] # `Annotated` expects at least two arguments
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-guard-call) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1009)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1008)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1081,7 +1081,7 @@ f(10) # Error
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-guard-definition) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L981)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L980)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1113,7 +1113,7 @@ class C:
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-variable-constraints) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1037)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1036)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1146,7 +1146,7 @@ T = TypeVar('T', bound=str) # valid bound TypeVar
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20missing-argument) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1066)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1065)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1169,7 +1169,7 @@ func() # TypeError: func() missing 1 required positional argument: 'x'
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20no-matching-overload) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1085)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1084)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1196,7 +1196,7 @@ func("string") # error: [no-matching-overload]
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20non-subscriptable) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1108)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1107)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1218,7 +1218,7 @@ Subscripting an object that does not support it will raise a `TypeError` at runt
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20not-iterable) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1126)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1125)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1242,7 +1242,7 @@ for i in 34: # TypeError: 'int' object is not iterable
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20parameter-already-assigned) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1177)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1176)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1296,7 +1296,7 @@ def test(): -> "int":
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20static-assert-error) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1513)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1512)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1324,7 +1324,7 @@ static_assert(int(2.0 * 3.0) == 6) # error: does not have a statically known tr
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20subclass-of-final-class) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1268)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1267)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1351,7 +1351,7 @@ class B(A): ... # Error raised here
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20too-many-positional-arguments) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1313)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1312)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1376,7 +1376,7 @@ f("foo") # Error raised here
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20type-assertion-failure) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1291)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1290)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1402,7 +1402,7 @@ def _(x: int):
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unavailable-implicit-super-arguments) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1334)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1333)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1446,7 +1446,7 @@ class A:
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unknown-argument) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1391)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1390)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1471,7 +1471,7 @@ f(x=1, y=2) # Error raised here
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-attribute) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1412)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1411)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1497,7 +1497,7 @@ A().foo # AttributeError: 'A' object has no attribute 'foo'
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-import) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1434)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1433)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1520,7 +1520,7 @@ import foo # ModuleNotFoundError: No module named 'foo'
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-reference) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1453)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1452)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1543,7 +1543,7 @@ print(x) # NameError: name 'x' is not defined
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-bool-conversion) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1146)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1145)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1578,7 +1578,7 @@ b1 < b2 < b1 # exception raised here
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-operator) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1472)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1471)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1604,7 +1604,7 @@ A() + A() # TypeError: unsupported operand type(s) for +: 'A' and 'A'
|
||||
<small>
|
||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20zero-stepsize-in-slice) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1494)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1493)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1627,7 +1627,7 @@ l[1:10:0] # ValueError: slice step cannot be zero
|
||||
<small>
|
||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20deprecated) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L265)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L264)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1680,7 +1680,7 @@ a = 20 / 0 # type: ignore
|
||||
<small>
|
||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unbound-attribute) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1198)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1197)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1706,7 +1706,7 @@ A.c # AttributeError: type object 'A' has no attribute 'c'
|
||||
<small>
|
||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unbound-implicit-call) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L118)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L117)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1736,7 +1736,7 @@ A()[0] # TypeError: 'A' object is not subscriptable
|
||||
<small>
|
||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unbound-import) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1220)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1219)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1766,7 +1766,7 @@ from module import a # ImportError: cannot import name 'a' from 'module'
|
||||
<small>
|
||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20redundant-cast) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1565)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1564)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1791,7 +1791,7 @@ cast(int, f()) # Redundant
|
||||
<small>
|
||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20undefined-reveal) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1373)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1372)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1842,7 +1842,7 @@ a = 20 / 0 # ty: ignore[division-by-zero]
|
||||
<small>
|
||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-global) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1586)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1585)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1896,7 +1896,7 @@ def g():
|
||||
<small>
|
||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-base) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L573)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L572)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1933,7 +1933,7 @@ class D(C): ... # error: [unsupported-base]
|
||||
<small>
|
||||
Default level: [`ignore`](../rules.md#rule-levels "This lint has a default level of 'ignore'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20division-by-zero) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L247)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L246)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
@@ -1955,7 +1955,7 @@ Dividing by zero raises a `ZeroDivisionError` at runtime.
|
||||
<small>
|
||||
Default level: [`ignore`](../rules.md#rule-levels "This lint has a default level of 'ignore'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unresolved-reference) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1246)
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1245)
|
||||
</small>
|
||||
|
||||
**What it does**
|
||||
|
||||
@@ -22,12 +22,13 @@ use colored::Colorize;
|
||||
use crossbeam::channel as crossbeam_channel;
|
||||
use rayon::ThreadPoolBuilder;
|
||||
use ruff_db::diagnostic::{Diagnostic, DisplayDiagnosticConfig, Severity};
|
||||
use ruff_db::files::File;
|
||||
use ruff_db::max_parallelism;
|
||||
use ruff_db::system::{OsSystem, SystemPath, SystemPathBuf};
|
||||
use salsa::plumbing::ZalsaDatabase;
|
||||
use salsa::Database;
|
||||
use ty_project::metadata::options::ProjectOptionsOverrides;
|
||||
use ty_project::watch::ProjectWatcher;
|
||||
use ty_project::{Db, watch};
|
||||
use ty_project::{CollectReporter, Db, watch};
|
||||
use ty_project::{ProjectDatabase, ProjectMetadata};
|
||||
use ty_server::run_server;
|
||||
|
||||
@@ -238,11 +239,6 @@ impl MainLoop {
|
||||
})?;
|
||||
|
||||
self.watcher = Some(ProjectWatcher::new(watcher, db));
|
||||
|
||||
// Do not show progress bars with `--watch`, indicatif does not seem to
|
||||
// handle cancelling independent progress bars very well.
|
||||
// TODO(zanieb): We can probably use `MultiProgress` to handle this case in the future.
|
||||
self.printer = self.printer.with_no_progress();
|
||||
self.run(db)?;
|
||||
|
||||
Ok(ExitStatus::Success)
|
||||
@@ -265,6 +261,9 @@ impl MainLoop {
|
||||
let mut revision = 0u64;
|
||||
|
||||
while let Ok(message) = self.receiver.recv() {
|
||||
if self.watcher.is_some() {
|
||||
Printer::clear_screen()?;
|
||||
}
|
||||
match message {
|
||||
MainLoopMessage::CheckWorkspace => {
|
||||
let db = db.clone();
|
||||
@@ -273,9 +272,13 @@ impl MainLoop {
|
||||
// Spawn a new task that checks the project. This needs to be done in a separate thread
|
||||
// to prevent blocking the main loop here.
|
||||
rayon::spawn(move || {
|
||||
let mut reporter = IndicatifReporter::from(self.printer);
|
||||
let bar = reporter.bar.clone();
|
||||
|
||||
match salsa::Cancelled::catch(|| {
|
||||
let mut reporter = IndicatifReporter::from(self.printer);
|
||||
db.check_with_reporter(&mut reporter)
|
||||
db.check_with_reporter(&mut reporter);
|
||||
reporter.bar.finish();
|
||||
reporter.collector.into_sorted(&db)
|
||||
}) {
|
||||
Ok(result) => {
|
||||
// Send the result back to the main loop for printing.
|
||||
@@ -284,6 +287,7 @@ impl MainLoop {
|
||||
.unwrap();
|
||||
}
|
||||
Err(cancelled) => {
|
||||
bar.finish_and_clear();
|
||||
tracing::debug!("Check has been cancelled: {cancelled:?}");
|
||||
}
|
||||
}
|
||||
@@ -380,9 +384,7 @@ impl MainLoop {
|
||||
}
|
||||
MainLoopMessage::Exit => {
|
||||
// Cancel any pending queries and wait for them to complete.
|
||||
// TODO: Don't use Salsa internal APIs
|
||||
// [Zulip-Thread](https://salsa.zulipchat.com/#narrow/stream/333573-salsa-3.2E0/topic/Expose.20an.20API.20to.20cancel.20other.20queries)
|
||||
let _ = db.zalsa_mut();
|
||||
db.trigger_cancellation();
|
||||
return Ok(ExitStatus::Success);
|
||||
}
|
||||
}
|
||||
@@ -395,54 +397,52 @@ impl MainLoop {
|
||||
}
|
||||
|
||||
/// A progress reporter for `ty check`.
|
||||
enum IndicatifReporter {
|
||||
/// A constructed reporter that is not yet ready, contains the target for the progress bar.
|
||||
Pending(indicatif::ProgressDrawTarget),
|
||||
struct IndicatifReporter {
|
||||
collector: CollectReporter,
|
||||
|
||||
/// A reporter that is ready, containing a progress bar to report to.
|
||||
///
|
||||
/// Initialization of the bar is deferred to [`ty_project::ProgressReporter::set_files`] so we
|
||||
/// do not initialize the bar too early as it may take a while to collect the number of files to
|
||||
/// process and we don't want to display an empty "0/0" bar.
|
||||
Initialized(indicatif::ProgressBar),
|
||||
bar: indicatif::ProgressBar,
|
||||
|
||||
printer: Printer,
|
||||
}
|
||||
|
||||
impl From<Printer> for IndicatifReporter {
|
||||
fn from(printer: Printer) -> Self {
|
||||
Self::Pending(printer.progress_target())
|
||||
Self {
|
||||
bar: indicatif::ProgressBar::hidden(),
|
||||
collector: CollectReporter::default(),
|
||||
printer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ty_project::ProgressReporter for IndicatifReporter {
|
||||
fn set_files(&mut self, files: usize) {
|
||||
let target = match std::mem::replace(
|
||||
self,
|
||||
IndicatifReporter::Pending(indicatif::ProgressDrawTarget::hidden()),
|
||||
) {
|
||||
Self::Pending(target) => target,
|
||||
Self::Initialized(_) => panic!("The progress reporter should only be initialized once"),
|
||||
};
|
||||
self.collector.set_files(files);
|
||||
|
||||
let bar = indicatif::ProgressBar::with_draw_target(Some(files as u64), target);
|
||||
bar.set_style(
|
||||
self.bar.set_length(files as u64);
|
||||
self.bar.set_message("Checking");
|
||||
self.bar.set_style(
|
||||
indicatif::ProgressStyle::with_template(
|
||||
"{msg:8.dim} {bar:60.green/dim} {pos}/{len} files",
|
||||
)
|
||||
.unwrap()
|
||||
.progress_chars("--"),
|
||||
);
|
||||
bar.set_message("Checking");
|
||||
*self = Self::Initialized(bar);
|
||||
self.bar.set_draw_target(self.printer.progress_target());
|
||||
}
|
||||
|
||||
fn report_file(&self, _file: &ruff_db::files::File) {
|
||||
match self {
|
||||
IndicatifReporter::Initialized(progress_bar) => {
|
||||
progress_bar.inc(1);
|
||||
}
|
||||
IndicatifReporter::Pending(_) => {
|
||||
panic!("`report_file` called before `set_files`")
|
||||
}
|
||||
}
|
||||
fn report_checked_file(&self, db: &dyn Db, file: File, diagnostics: &[Diagnostic]) {
|
||||
self.collector.report_checked_file(db, file, diagnostics);
|
||||
self.bar.inc(1);
|
||||
}
|
||||
|
||||
fn report_diagnostics(&mut self, db: &dyn Db, diagnostics: Vec<Diagnostic>) {
|
||||
self.collector.report_diagnostics(db, diagnostics);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::io::StdoutLock;
|
||||
|
||||
use anyhow::Result;
|
||||
use indicatif::ProgressDrawTarget;
|
||||
|
||||
use crate::logging::VerbosityLevel;
|
||||
@@ -11,14 +12,6 @@ pub(crate) struct Printer {
|
||||
}
|
||||
|
||||
impl Printer {
|
||||
#[must_use]
|
||||
pub(crate) fn with_no_progress(self) -> Self {
|
||||
Self {
|
||||
verbosity: self.verbosity,
|
||||
no_progress: true,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn with_verbosity(self, verbosity: VerbosityLevel) -> Self {
|
||||
Self {
|
||||
@@ -109,6 +102,11 @@ impl Printer {
|
||||
pub(crate) fn stream_for_details(self) -> Stdout {
|
||||
self.stdout_general()
|
||||
}
|
||||
|
||||
pub(crate) fn clear_screen() -> Result<()> {
|
||||
clearscreen::clear()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
|
||||
@@ -724,3 +724,231 @@ fn invalid_exclude_pattern() -> anyhow::Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test that ty works correctly with Bazel's symlinked file structure
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn bazel_symlinked_files() -> anyhow::Result<()> {
|
||||
let case = CliTest::with_files([
|
||||
// Original source files in the project
|
||||
(
|
||||
"main.py",
|
||||
r#"
|
||||
import library
|
||||
|
||||
result = library.process_data()
|
||||
print(undefined_var) # error: unresolved-reference
|
||||
"#,
|
||||
),
|
||||
(
|
||||
"library.py",
|
||||
r#"
|
||||
def process_data():
|
||||
return missing_value # error: unresolved-reference
|
||||
"#,
|
||||
),
|
||||
// Another source file that won't be symlinked
|
||||
(
|
||||
"other.py",
|
||||
r#"
|
||||
print(other_undefined) # error: unresolved-reference
|
||||
"#,
|
||||
),
|
||||
])?;
|
||||
|
||||
// Create Bazel-style symlinks pointing to the actual source files
|
||||
// Bazel typically creates symlinks in bazel-out/k8-fastbuild/bin/ that point to actual sources
|
||||
std::fs::create_dir_all(case.project_dir.join("bazel-out/k8-fastbuild/bin"))?;
|
||||
|
||||
// Use absolute paths to ensure the symlinks work correctly
|
||||
case.write_symlink(
|
||||
case.project_dir.join("main.py"),
|
||||
"bazel-out/k8-fastbuild/bin/main.py",
|
||||
)?;
|
||||
case.write_symlink(
|
||||
case.project_dir.join("library.py"),
|
||||
"bazel-out/k8-fastbuild/bin/library.py",
|
||||
)?;
|
||||
|
||||
// Change to the bazel-out directory and run ty from there
|
||||
// The symlinks should be followed and errors should be found
|
||||
assert_cmd_snapshot!(case.command().current_dir(case.project_dir.join("bazel-out/k8-fastbuild/bin")), @r"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
error[unresolved-reference]: Name `missing_value` used when not defined
|
||||
--> library.py:3:12
|
||||
|
|
||||
2 | def process_data():
|
||||
3 | return missing_value # error: unresolved-reference
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
info: rule `unresolved-reference` is enabled by default
|
||||
|
||||
error[unresolved-reference]: Name `undefined_var` used when not defined
|
||||
--> main.py:5:7
|
||||
|
|
||||
4 | result = library.process_data()
|
||||
5 | print(undefined_var) # error: unresolved-reference
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
info: rule `unresolved-reference` is enabled by default
|
||||
|
||||
Found 2 diagnostics
|
||||
|
||||
----- stderr -----
|
||||
WARN ty is pre-release software and not ready for production use. Expect to encounter bugs, missing features, and fatal errors.
|
||||
");
|
||||
|
||||
// Test that when checking a specific symlinked file from the bazel-out directory, it works correctly
|
||||
assert_cmd_snapshot!(case.command().current_dir(case.project_dir.join("bazel-out/k8-fastbuild/bin")).arg("main.py"), @r"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
error[unresolved-reference]: Name `undefined_var` used when not defined
|
||||
--> main.py:5:7
|
||||
|
|
||||
4 | result = library.process_data()
|
||||
5 | print(undefined_var) # error: unresolved-reference
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
info: rule `unresolved-reference` is enabled by default
|
||||
|
||||
Found 1 diagnostic
|
||||
|
||||
----- stderr -----
|
||||
WARN ty is pre-release software and not ready for production use. Expect to encounter bugs, missing features, and fatal errors.
|
||||
");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test that exclude patterns match on symlink source names, not target names
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn exclude_symlink_source_not_target() -> anyhow::Result<()> {
|
||||
let case = CliTest::with_files([
|
||||
// Target files with generic names
|
||||
(
|
||||
"src/module.py",
|
||||
r#"
|
||||
def process():
|
||||
return undefined_var # error: unresolved-reference
|
||||
"#,
|
||||
),
|
||||
(
|
||||
"src/utils.py",
|
||||
r#"
|
||||
def helper():
|
||||
return missing_value # error: unresolved-reference
|
||||
"#,
|
||||
),
|
||||
(
|
||||
"regular.py",
|
||||
r#"
|
||||
print(regular_undefined) # error: unresolved-reference
|
||||
"#,
|
||||
),
|
||||
])?;
|
||||
|
||||
// Create symlinks with names that differ from their targets
|
||||
// This simulates build systems that rename files during symlinking
|
||||
case.write_symlink("src/module.py", "generated_module.py")?;
|
||||
case.write_symlink("src/utils.py", "generated_utils.py")?;
|
||||
|
||||
// Exclude pattern should match on the symlink name (generated_*), not the target name
|
||||
assert_cmd_snapshot!(case.command().arg("--exclude").arg("generated_*.py"), @r"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
error[unresolved-reference]: Name `regular_undefined` used when not defined
|
||||
--> regular.py:2:7
|
||||
|
|
||||
2 | print(regular_undefined) # error: unresolved-reference
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
info: rule `unresolved-reference` is enabled by default
|
||||
|
||||
error[unresolved-reference]: Name `undefined_var` used when not defined
|
||||
--> src/module.py:3:12
|
||||
|
|
||||
2 | def process():
|
||||
3 | return undefined_var # error: unresolved-reference
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
info: rule `unresolved-reference` is enabled by default
|
||||
|
||||
error[unresolved-reference]: Name `missing_value` used when not defined
|
||||
--> src/utils.py:3:12
|
||||
|
|
||||
2 | def helper():
|
||||
3 | return missing_value # error: unresolved-reference
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
info: rule `unresolved-reference` is enabled by default
|
||||
|
||||
Found 3 diagnostics
|
||||
|
||||
----- stderr -----
|
||||
WARN ty is pre-release software and not ready for production use. Expect to encounter bugs, missing features, and fatal errors.
|
||||
");
|
||||
|
||||
// Exclude pattern on target path should not affect symlinks with different names
|
||||
assert_cmd_snapshot!(case.command().arg("--exclude").arg("src/*.py"), @r"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
error[unresolved-reference]: Name `undefined_var` used when not defined
|
||||
--> generated_module.py:3:12
|
||||
|
|
||||
2 | def process():
|
||||
3 | return undefined_var # error: unresolved-reference
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
info: rule `unresolved-reference` is enabled by default
|
||||
|
||||
error[unresolved-reference]: Name `missing_value` used when not defined
|
||||
--> generated_utils.py:3:12
|
||||
|
|
||||
2 | def helper():
|
||||
3 | return missing_value # error: unresolved-reference
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
info: rule `unresolved-reference` is enabled by default
|
||||
|
||||
error[unresolved-reference]: Name `regular_undefined` used when not defined
|
||||
--> regular.py:2:7
|
||||
|
|
||||
2 | print(regular_undefined) # error: unresolved-reference
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
info: rule `unresolved-reference` is enabled by default
|
||||
|
||||
Found 3 diagnostics
|
||||
|
||||
----- stderr -----
|
||||
WARN ty is pre-release software and not ready for production use. Expect to encounter bugs, missing features, and fatal errors.
|
||||
");
|
||||
|
||||
// Test that explicitly passing a symlink always checks it, even if excluded
|
||||
assert_cmd_snapshot!(case.command().arg("--exclude").arg("generated_*.py").arg("generated_module.py"), @r"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
error[unresolved-reference]: Name `undefined_var` used when not defined
|
||||
--> generated_module.py:3:12
|
||||
|
|
||||
2 | def process():
|
||||
3 | return undefined_var # error: unresolved-reference
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
info: rule `unresolved-reference` is enabled by default
|
||||
|
||||
Found 1 diagnostic
|
||||
|
||||
----- stderr -----
|
||||
WARN ty is pre-release software and not ready for production use. Expect to encounter bugs, missing features, and fatal errors.
|
||||
");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -863,10 +863,18 @@ fn overrides_unknown_rules() -> anyhow::Result<()> {
|
||||
),
|
||||
])?;
|
||||
|
||||
assert_cmd_snapshot!(case.command(), @r#"
|
||||
assert_cmd_snapshot!(case.command(), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
error[division-by-zero]: Cannot divide object of type `Literal[4]` by zero
|
||||
--> main.py:2:5
|
||||
|
|
||||
2 | y = 4 / 0
|
||||
| ^^^^^
|
||||
|
|
||||
info: rule `division-by-zero` was selected in the configuration file
|
||||
|
||||
warning[unknown-rule]: Unknown lint rule `division-by-zer`
|
||||
--> pyproject.toml:10:1
|
||||
|
|
||||
@@ -876,14 +884,6 @@ fn overrides_unknown_rules() -> anyhow::Result<()> {
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
|
||||
error[division-by-zero]: Cannot divide object of type `Literal[4]` by zero
|
||||
--> main.py:2:5
|
||||
|
|
||||
2 | y = 4 / 0
|
||||
| ^^^^^
|
||||
|
|
||||
info: rule `division-by-zero` was selected in the configuration file
|
||||
|
||||
warning[division-by-zero]: Cannot divide object of type `Literal[4]` by zero
|
||||
--> tests/test_main.py:2:5
|
||||
|
|
||||
@@ -896,7 +896,7 @@ fn overrides_unknown_rules() -> anyhow::Result<()> {
|
||||
|
||||
----- stderr -----
|
||||
WARN ty is pre-release software and not ready for production use. Expect to encounter bugs, missing features, and fatal errors.
|
||||
"#);
|
||||
"###);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -66,9 +66,9 @@ enum CompletionTargetTokens<'t> {
|
||||
/// A token was found under the cursor, but it didn't
|
||||
/// match any of our anticipated token patterns.
|
||||
Generic { token: &'t Token },
|
||||
/// No token was found, but we have the offset of the
|
||||
/// cursor.
|
||||
Unknown { offset: TextSize },
|
||||
/// No token was found. We generally treat this like
|
||||
/// `Generic` (i.e., offer scope based completions).
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl<'t> CompletionTargetTokens<'t> {
|
||||
@@ -78,7 +78,7 @@ impl<'t> CompletionTargetTokens<'t> {
|
||||
static OBJECT_DOT_NON_EMPTY: [TokenKind; 2] = [TokenKind::Dot, TokenKind::Name];
|
||||
|
||||
let offset = match parsed.tokens().at_offset(offset) {
|
||||
TokenAt::None => return Some(CompletionTargetTokens::Unknown { offset }),
|
||||
TokenAt::None => return Some(CompletionTargetTokens::Unknown),
|
||||
TokenAt::Single(tok) => tok.end(),
|
||||
TokenAt::Between(_, tok) => tok.start(),
|
||||
};
|
||||
@@ -122,7 +122,7 @@ impl<'t> CompletionTargetTokens<'t> {
|
||||
return None;
|
||||
} else {
|
||||
let Some(last) = before.last() else {
|
||||
return Some(CompletionTargetTokens::Unknown { offset });
|
||||
return Some(CompletionTargetTokens::Unknown);
|
||||
};
|
||||
CompletionTargetTokens::Generic { token: last }
|
||||
},
|
||||
@@ -171,7 +171,7 @@ impl<'t> CompletionTargetTokens<'t> {
|
||||
node: covering_node.node(),
|
||||
})
|
||||
}
|
||||
CompletionTargetTokens::Unknown { offset } => {
|
||||
CompletionTargetTokens::Unknown => {
|
||||
let range = TextRange::empty(offset);
|
||||
let covering_node = covering_node(parsed.syntax().into(), range);
|
||||
Some(CompletionTargetAst::Scoped {
|
||||
@@ -1233,28 +1233,28 @@ quux.<CURSOR>
|
||||
baz :: Unknown | Literal[3]
|
||||
foo :: Unknown | Literal[1]
|
||||
__annotations__ :: dict[str, Any]
|
||||
__class__ :: type
|
||||
__delattr__ :: bound method object.__delattr__(name: str, /) -> None
|
||||
__class__ :: type[Quux]
|
||||
__delattr__ :: bound method Quux.__delattr__(name: str, /) -> None
|
||||
__dict__ :: dict[str, Any]
|
||||
__dir__ :: bound method object.__dir__() -> Iterable[str]
|
||||
__dir__ :: bound method Quux.__dir__() -> Iterable[str]
|
||||
__doc__ :: str | None
|
||||
__eq__ :: bound method object.__eq__(value: object, /) -> bool
|
||||
__format__ :: bound method object.__format__(format_spec: str, /) -> str
|
||||
__getattribute__ :: bound method object.__getattribute__(name: str, /) -> Any
|
||||
__getstate__ :: bound method object.__getstate__() -> object
|
||||
__hash__ :: bound method object.__hash__() -> int
|
||||
__eq__ :: bound method Quux.__eq__(value: object, /) -> bool
|
||||
__format__ :: bound method Quux.__format__(format_spec: str, /) -> str
|
||||
__getattribute__ :: bound method Quux.__getattribute__(name: str, /) -> Any
|
||||
__getstate__ :: bound method Quux.__getstate__() -> object
|
||||
__hash__ :: bound method Quux.__hash__() -> int
|
||||
__init__ :: bound method Quux.__init__() -> Unknown
|
||||
__init_subclass__ :: bound method object.__init_subclass__() -> None
|
||||
__init_subclass__ :: bound method Quux.__init_subclass__() -> None
|
||||
__module__ :: str
|
||||
__ne__ :: bound method object.__ne__(value: object, /) -> bool
|
||||
__new__ :: bound method object.__new__() -> Self
|
||||
__reduce__ :: bound method object.__reduce__() -> str | tuple[Any, ...]
|
||||
__reduce_ex__ :: bound method object.__reduce_ex__(protocol: SupportsIndex, /) -> str | tuple[Any, ...]
|
||||
__repr__ :: bound method object.__repr__() -> str
|
||||
__setattr__ :: bound method object.__setattr__(name: str, value: Any, /) -> None
|
||||
__sizeof__ :: bound method object.__sizeof__() -> int
|
||||
__str__ :: bound method object.__str__() -> str
|
||||
__subclasshook__ :: bound method type.__subclasshook__(subclass: type, /) -> bool
|
||||
__ne__ :: bound method Quux.__ne__(value: object, /) -> bool
|
||||
__new__ :: bound method Quux.__new__() -> Self@object
|
||||
__reduce__ :: bound method Quux.__reduce__() -> str | tuple[Any, ...]
|
||||
__reduce_ex__ :: bound method Quux.__reduce_ex__(protocol: SupportsIndex, /) -> str | tuple[Any, ...]
|
||||
__repr__ :: bound method Quux.__repr__() -> str
|
||||
__setattr__ :: bound method Quux.__setattr__(name: str, value: Any, /) -> None
|
||||
__sizeof__ :: bound method Quux.__sizeof__() -> int
|
||||
__str__ :: bound method Quux.__str__() -> str
|
||||
__subclasshook__ :: bound method type[Quux].__subclasshook__(subclass: type, /) -> bool
|
||||
");
|
||||
}
|
||||
|
||||
@@ -1278,28 +1278,28 @@ quux.b<CURSOR>
|
||||
baz :: Unknown | Literal[3]
|
||||
foo :: Unknown | Literal[1]
|
||||
__annotations__ :: dict[str, Any]
|
||||
__class__ :: type
|
||||
__delattr__ :: bound method object.__delattr__(name: str, /) -> None
|
||||
__class__ :: type[Quux]
|
||||
__delattr__ :: bound method Quux.__delattr__(name: str, /) -> None
|
||||
__dict__ :: dict[str, Any]
|
||||
__dir__ :: bound method object.__dir__() -> Iterable[str]
|
||||
__dir__ :: bound method Quux.__dir__() -> Iterable[str]
|
||||
__doc__ :: str | None
|
||||
__eq__ :: bound method object.__eq__(value: object, /) -> bool
|
||||
__format__ :: bound method object.__format__(format_spec: str, /) -> str
|
||||
__getattribute__ :: bound method object.__getattribute__(name: str, /) -> Any
|
||||
__getstate__ :: bound method object.__getstate__() -> object
|
||||
__hash__ :: bound method object.__hash__() -> int
|
||||
__eq__ :: bound method Quux.__eq__(value: object, /) -> bool
|
||||
__format__ :: bound method Quux.__format__(format_spec: str, /) -> str
|
||||
__getattribute__ :: bound method Quux.__getattribute__(name: str, /) -> Any
|
||||
__getstate__ :: bound method Quux.__getstate__() -> object
|
||||
__hash__ :: bound method Quux.__hash__() -> int
|
||||
__init__ :: bound method Quux.__init__() -> Unknown
|
||||
__init_subclass__ :: bound method object.__init_subclass__() -> None
|
||||
__init_subclass__ :: bound method Quux.__init_subclass__() -> None
|
||||
__module__ :: str
|
||||
__ne__ :: bound method object.__ne__(value: object, /) -> bool
|
||||
__new__ :: bound method object.__new__() -> Self
|
||||
__reduce__ :: bound method object.__reduce__() -> str | tuple[Any, ...]
|
||||
__reduce_ex__ :: bound method object.__reduce_ex__(protocol: SupportsIndex, /) -> str | tuple[Any, ...]
|
||||
__repr__ :: bound method object.__repr__() -> str
|
||||
__setattr__ :: bound method object.__setattr__(name: str, value: Any, /) -> None
|
||||
__sizeof__ :: bound method object.__sizeof__() -> int
|
||||
__str__ :: bound method object.__str__() -> str
|
||||
__subclasshook__ :: bound method type.__subclasshook__(subclass: type, /) -> bool
|
||||
__ne__ :: bound method Quux.__ne__(value: object, /) -> bool
|
||||
__new__ :: bound method Quux.__new__() -> Self@object
|
||||
__reduce__ :: bound method Quux.__reduce__() -> str | tuple[Any, ...]
|
||||
__reduce_ex__ :: bound method Quux.__reduce_ex__(protocol: SupportsIndex, /) -> str | tuple[Any, ...]
|
||||
__repr__ :: bound method Quux.__repr__() -> str
|
||||
__setattr__ :: bound method Quux.__setattr__(name: str, value: Any, /) -> None
|
||||
__sizeof__ :: bound method Quux.__sizeof__() -> int
|
||||
__str__ :: bound method Quux.__str__() -> str
|
||||
__subclasshook__ :: bound method type[Quux].__subclasshook__(subclass: type, /) -> bool
|
||||
");
|
||||
}
|
||||
|
||||
@@ -1346,7 +1346,7 @@ C.<CURSOR>
|
||||
__mro__ :: tuple[<class 'C'>, <class 'object'>]
|
||||
__name__ :: str
|
||||
__ne__ :: def __ne__(self, value: object, /) -> bool
|
||||
__new__ :: def __new__(cls) -> Self
|
||||
__new__ :: def __new__(cls) -> Self@object
|
||||
__or__ :: bound method <class 'C'>.__or__(value: Any, /) -> UnionType
|
||||
__prepare__ :: bound method <class 'Meta'>.__prepare__(name: str, bases: tuple[type, ...], /, **kwds: Any) -> MutableMapping[str, object]
|
||||
__qualname__ :: str
|
||||
@@ -1522,7 +1522,7 @@ Quux.<CURSOR>
|
||||
__mro__ :: tuple[<class 'Quux'>, <class 'object'>]
|
||||
__name__ :: str
|
||||
__ne__ :: def __ne__(self, value: object, /) -> bool
|
||||
__new__ :: def __new__(cls) -> Self
|
||||
__new__ :: def __new__(cls) -> Self@object
|
||||
__or__ :: bound method <class 'Quux'>.__or__(value: Any, /) -> UnionType
|
||||
__prepare__ :: bound method <class 'type'>.__prepare__(name: str, bases: tuple[type, ...], /, **kwds: Any) -> MutableMapping[str, object]
|
||||
__qualname__ :: str
|
||||
@@ -1574,8 +1574,8 @@ Answer.<CURSOR>
|
||||
__bool__ :: bound method <class 'Answer'>.__bool__() -> Literal[True]
|
||||
__class__ :: <class 'EnumMeta'>
|
||||
__contains__ :: bound method <class 'Answer'>.__contains__(value: object) -> bool
|
||||
__copy__ :: def __copy__(self) -> Self
|
||||
__deepcopy__ :: def __deepcopy__(self, memo: Any) -> Self
|
||||
__copy__ :: def __copy__(self) -> Self@Enum
|
||||
__deepcopy__ :: def __deepcopy__(self, memo: Any) -> Self@Enum
|
||||
__delattr__ :: def __delattr__(self, name: str, /) -> None
|
||||
__dict__ :: MappingProxyType[str, Any]
|
||||
__dictoffset__ :: int
|
||||
@@ -1585,28 +1585,28 @@ Answer.<CURSOR>
|
||||
__flags__ :: int
|
||||
__format__ :: def __format__(self, format_spec: str) -> str
|
||||
__getattribute__ :: def __getattribute__(self, name: str, /) -> Any
|
||||
__getitem__ :: bound method <class 'Answer'>.__getitem__(name: str) -> _EnumMemberT
|
||||
__getitem__ :: bound method <class 'Answer'>.__getitem__(name: str) -> _EnumMemberT@__getitem__
|
||||
__getstate__ :: def __getstate__(self) -> object
|
||||
__hash__ :: def __hash__(self) -> int
|
||||
__init__ :: def __init__(self) -> None
|
||||
__init_subclass__ :: def __init_subclass__(cls) -> None
|
||||
__instancecheck__ :: bound method <class 'Answer'>.__instancecheck__(instance: Any, /) -> bool
|
||||
__itemsize__ :: int
|
||||
__iter__ :: bound method <class 'Answer'>.__iter__() -> Iterator[_EnumMemberT]
|
||||
__iter__ :: bound method <class 'Answer'>.__iter__() -> Iterator[_EnumMemberT@__iter__]
|
||||
__len__ :: bound method <class 'Answer'>.__len__() -> int
|
||||
__members__ :: MappingProxyType[str, Unknown]
|
||||
__module__ :: str
|
||||
__mro__ :: tuple[<class 'Answer'>, <class 'Enum'>, <class 'object'>]
|
||||
__name__ :: str
|
||||
__ne__ :: def __ne__(self, value: object, /) -> bool
|
||||
__new__ :: def __new__(cls, value: object) -> Self
|
||||
__new__ :: def __new__(cls, value: object) -> Self@Enum
|
||||
__or__ :: bound method <class 'Answer'>.__or__(value: Any, /) -> UnionType
|
||||
__order__ :: str
|
||||
__prepare__ :: bound method <class 'EnumMeta'>.__prepare__(cls: str, bases: tuple[type, ...], **kwds: Any) -> _EnumDict
|
||||
__qualname__ :: str
|
||||
__reduce__ :: def __reduce__(self) -> str | tuple[Any, ...]
|
||||
__repr__ :: def __repr__(self) -> str
|
||||
__reversed__ :: bound method <class 'Answer'>.__reversed__() -> Iterator[_EnumMemberT]
|
||||
__reversed__ :: bound method <class 'Answer'>.__reversed__() -> Iterator[_EnumMemberT@__reversed__]
|
||||
__ror__ :: bound method <class 'Answer'>.__ror__(value: Any, /) -> UnionType
|
||||
__setattr__ :: def __setattr__(self, name: str, value: Any, /) -> None
|
||||
__signature__ :: bound method <class 'Answer'>.__signature__() -> str
|
||||
|
||||
@@ -38,7 +38,7 @@ mod tests {
|
||||
|
||||
impl CursorTest {
|
||||
fn references(&self) -> String {
|
||||
let Some(reference_results) =
|
||||
let Some(mut reference_results) =
|
||||
goto_references(&self.db, self.cursor.file, self.cursor.offset, true)
|
||||
else {
|
||||
return "No references found".to_string();
|
||||
@@ -48,6 +48,8 @@ mod tests {
|
||||
return "No references found".to_string();
|
||||
}
|
||||
|
||||
reference_results.sort_by_key(ReferenceTarget::file);
|
||||
|
||||
self.render_diagnostics(reference_results.into_iter().enumerate().map(
|
||||
|(i, ref_item)| -> ReferenceResult {
|
||||
ReferenceResult {
|
||||
|
||||
@@ -371,10 +371,10 @@ mod tests {
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @r"
|
||||
T
|
||||
T@Alias
|
||||
---------------------------------------------
|
||||
```python
|
||||
T
|
||||
T@Alias
|
||||
```
|
||||
---------------------------------------------
|
||||
info[hover]: Hovered content is
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
use std::fmt::Formatter;
|
||||
use std::panic::{AssertUnwindSafe, RefUnwindSafe};
|
||||
use std::panic::RefUnwindSafe;
|
||||
use std::sync::Arc;
|
||||
use std::{cmp, fmt};
|
||||
|
||||
pub use self::changes::ChangeResult;
|
||||
use crate::metadata::settings::file_settings;
|
||||
use crate::{DEFAULT_LINT_REGISTRY, DummyReporter};
|
||||
use crate::{CollectReporter, DEFAULT_LINT_REGISTRY};
|
||||
use crate::{ProgressReporter, Project, ProjectMetadata};
|
||||
use ruff_db::Db as SourceDb;
|
||||
use ruff_db::diagnostic::Diagnostic;
|
||||
use ruff_db::files::{File, Files};
|
||||
use ruff_db::system::System;
|
||||
use ruff_db::vendored::VendoredFileSystem;
|
||||
use salsa::plumbing::ZalsaDatabase;
|
||||
use salsa::{Event, Setter};
|
||||
use salsa::{Database, Event, Setter};
|
||||
use ty_python_semantic::lint::{LintRegistry, RuleSelection};
|
||||
use ty_python_semantic::{Db as SemanticDb, Program};
|
||||
|
||||
@@ -34,7 +33,7 @@ pub struct ProjectDatabase {
|
||||
// or the "trick" to get a mutable `Arc` in `Self::system_mut` is no longer guaranteed to work.
|
||||
system: Arc<dyn System + Send + Sync + RefUnwindSafe>,
|
||||
|
||||
// IMPORTANT: This field must be the last because we use `zalsa_mut` (drops all other storage references)
|
||||
// IMPORTANT: This field must be the last because we use `trigger_cancellation` (drops all other storage references)
|
||||
// to drop all other references to the database, which gives us exclusive access to other `Arc`s stored on this db.
|
||||
// However, for this to work it's important that the `storage` is dropped AFTER any `Arc` that
|
||||
// we try to mutably borrow using `Arc::get_mut` (like `system`).
|
||||
@@ -87,9 +86,9 @@ impl ProjectDatabase {
|
||||
///
|
||||
/// [`set_check_mode`]: ProjectDatabase::set_check_mode
|
||||
pub fn check(&self) -> Vec<Diagnostic> {
|
||||
let mut reporter = DummyReporter;
|
||||
let reporter = AssertUnwindSafe(&mut reporter as &mut dyn ProgressReporter);
|
||||
self.project().check(self, reporter)
|
||||
let mut collector = CollectReporter::default();
|
||||
self.project().check(self, &mut collector);
|
||||
collector.into_sorted(self)
|
||||
}
|
||||
|
||||
/// Checks the files in the project and its dependencies, using the given reporter.
|
||||
@@ -97,9 +96,8 @@ impl ProjectDatabase {
|
||||
/// Use [`set_check_mode`] to update the check mode.
|
||||
///
|
||||
/// [`set_check_mode`]: ProjectDatabase::set_check_mode
|
||||
pub fn check_with_reporter(&self, reporter: &mut dyn ProgressReporter) -> Vec<Diagnostic> {
|
||||
let reporter = AssertUnwindSafe(reporter);
|
||||
self.project().check(self, reporter)
|
||||
pub fn check_with_reporter(&self, reporter: &mut dyn ProgressReporter) {
|
||||
self.project().check(self, reporter);
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(self))]
|
||||
@@ -117,12 +115,11 @@ impl ProjectDatabase {
|
||||
///
|
||||
/// WARNING: Triggers a new revision, canceling other database handles. This can lead to deadlock.
|
||||
pub fn system_mut(&mut self) -> &mut dyn System {
|
||||
// TODO: Use a more official method to cancel other queries.
|
||||
// https://salsa.zulipchat.com/#narrow/stream/333573-salsa-3.2E0/topic/Expose.20an.20API.20to.20cancel.20other.20queries
|
||||
let _ = self.zalsa_mut();
|
||||
self.trigger_cancellation();
|
||||
|
||||
Arc::get_mut(&mut self.system)
|
||||
.expect("ref count should be 1 because `zalsa_mut` drops all other DB references.")
|
||||
Arc::get_mut(&mut self.system).expect(
|
||||
"ref count should be 1 because `trigger_cancellation` drops all other DB references.",
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a [`SalsaMemoryDump`] that can be use to dump Salsa memory usage information
|
||||
|
||||
@@ -58,17 +58,16 @@ impl IndexedFiles {
|
||||
///
|
||||
/// The changes are automatically written back to the database once the view is dropped.
|
||||
pub(super) fn indexed_mut(db: &mut dyn Db, project: Project) -> Option<IndexedMut> {
|
||||
// Calling `zalsa_mut` cancels all pending salsa queries. This ensures that there are no pending
|
||||
// reads to the file set.
|
||||
// TODO: Use a non-internal API instead https://salsa.zulipchat.com/#narrow/stream/333573-salsa-3.2E0/topic/Expose.20an.20API.20to.20cancel.20other.20queries
|
||||
let _ = db.as_dyn_database_mut().zalsa_mut();
|
||||
// Calling `trigger_cancellation` cancels all pending salsa queries. This ensures that there are no pending
|
||||
// reads to the file set (this `db` is the only alive db).
|
||||
db.trigger_cancellation();
|
||||
|
||||
// Replace the state with lazy. The `IndexedMut` guard restores the state
|
||||
// to `State::Indexed` or sets a new `PackageFiles` when it gets dropped to ensure the state
|
||||
// is restored to how it has been before replacing the value.
|
||||
//
|
||||
// It isn't necessary to hold on to the lock after this point:
|
||||
// * The above call to `zalsa_mut` guarantees that there's exactly **one** DB reference.
|
||||
// * The above call to `trigger_cancellation` guarantees that there's exactly **one** DB reference.
|
||||
// * `Indexed` has a `'db` lifetime, and this method requires a `&mut db`.
|
||||
// This means that there can't be any pending reference to `Indexed` because Rust
|
||||
// doesn't allow borrowing `db` as mutable (to call this method) and immutable (`Indexed<'db>`) at the same time.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user