Compare commits
94 Commits
0.12.7
...
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 |
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
|
||||
|
||||
337
Cargo.lock
generated
337
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",
|
||||
]
|
||||
@@ -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",
|
||||
@@ -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",
|
||||
@@ -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"] }
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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 []))}"
|
||||
|
||||
@@ -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,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() {
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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")
|
||||
/// ```
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -127,17 +127,46 @@ pub trait ProgressReporter: Send + Sync {
|
||||
/// Initialize the reporter with the number of files.
|
||||
fn set_files(&mut self, files: usize);
|
||||
|
||||
/// Report the completion of a given file.
|
||||
fn report_file(&self, file: &File);
|
||||
/// Report the completion of checking a given file along with its diagnostics.
|
||||
fn report_checked_file(&self, db: &dyn Db, file: File, diagnostics: &[Diagnostic]);
|
||||
|
||||
/// Reports settings or IO related diagnostics. The diagnostics
|
||||
/// can belong to different files or no file at all.
|
||||
/// But it's never a file for which [`Self::report_checked_file`] gets called.
|
||||
fn report_diagnostics(&mut self, db: &dyn Db, diagnostics: Vec<Diagnostic>);
|
||||
}
|
||||
|
||||
/// A no-op implementation of [`ProgressReporter`].
|
||||
/// Reporter that collects all diagnostics into a `Vec`.
|
||||
#[derive(Default)]
|
||||
pub struct DummyReporter;
|
||||
pub struct CollectReporter(std::sync::Mutex<Vec<Diagnostic>>);
|
||||
|
||||
impl ProgressReporter for DummyReporter {
|
||||
impl CollectReporter {
|
||||
pub fn into_sorted(self, db: &dyn Db) -> Vec<Diagnostic> {
|
||||
let mut diagnostics = self.0.into_inner().unwrap();
|
||||
diagnostics.sort_by(|left, right| {
|
||||
left.rendering_sort_key(db)
|
||||
.cmp(&right.rendering_sort_key(db))
|
||||
});
|
||||
diagnostics
|
||||
}
|
||||
}
|
||||
|
||||
impl ProgressReporter for CollectReporter {
|
||||
fn set_files(&mut self, _files: usize) {}
|
||||
fn report_file(&self, _file: &File) {}
|
||||
fn report_checked_file(&self, _db: &dyn Db, _file: File, diagnostics: &[Diagnostic]) {
|
||||
if diagnostics.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.0
|
||||
.lock()
|
||||
.unwrap()
|
||||
.extend(diagnostics.iter().map(Clone::clone));
|
||||
}
|
||||
|
||||
fn report_diagnostics(&mut self, _db: &dyn Db, diagnostics: Vec<Diagnostic>) {
|
||||
self.0.get_mut().unwrap().extend(diagnostics);
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::tracked]
|
||||
@@ -174,7 +203,7 @@ impl Project {
|
||||
/// This is a salsa query to prevent re-computing queries if other, unrelated
|
||||
/// settings change. For example, we don't want that changing the terminal settings
|
||||
/// invalidates any type checking queries.
|
||||
#[salsa::tracked(returns(deref), heap_size=get_size2::GetSize::get_heap_size)]
|
||||
#[salsa::tracked(returns(deref), heap_size=get_size2::heap_size)]
|
||||
pub fn rules(self, db: &dyn Db) -> Arc<RuleSelection> {
|
||||
self.settings(db).to_rules()
|
||||
}
|
||||
@@ -225,11 +254,7 @@ impl Project {
|
||||
}
|
||||
|
||||
/// Checks the project and its dependencies according to the project's check mode.
|
||||
pub(crate) fn check(
|
||||
self,
|
||||
db: &ProjectDatabase,
|
||||
mut reporter: AssertUnwindSafe<&mut dyn ProgressReporter>,
|
||||
) -> Vec<Diagnostic> {
|
||||
pub(crate) fn check(self, db: &ProjectDatabase, reporter: &mut dyn ProgressReporter) {
|
||||
let project_span = tracing::debug_span!("Project::check");
|
||||
let _span = project_span.enter();
|
||||
|
||||
@@ -239,12 +264,11 @@ impl Project {
|
||||
name = self.name(db)
|
||||
);
|
||||
|
||||
let mut diagnostics: Vec<Diagnostic> = Vec::new();
|
||||
diagnostics.extend(
|
||||
self.settings_diagnostics(db)
|
||||
.iter()
|
||||
.map(OptionDiagnostic::to_diagnostic),
|
||||
);
|
||||
let mut diagnostics: Vec<Diagnostic> = self
|
||||
.settings_diagnostics(db)
|
||||
.iter()
|
||||
.map(OptionDiagnostic::to_diagnostic)
|
||||
.collect();
|
||||
|
||||
let files = ProjectFiles::new(db, self);
|
||||
reporter.set_files(files.len());
|
||||
@@ -256,19 +280,19 @@ impl Project {
|
||||
.map(IOErrorDiagnostic::to_diagnostic),
|
||||
);
|
||||
|
||||
reporter.report_diagnostics(db, diagnostics);
|
||||
|
||||
let open_files = self.open_files(db);
|
||||
let check_start = ruff_db::Instant::now();
|
||||
let file_diagnostics = std::sync::Mutex::new(vec![]);
|
||||
|
||||
{
|
||||
let db = db.clone();
|
||||
let file_diagnostics = &file_diagnostics;
|
||||
let project_span = &project_span;
|
||||
let reporter = &reporter;
|
||||
|
||||
rayon::scope(move |scope| {
|
||||
for file in &files {
|
||||
let db = db.clone();
|
||||
let reporter = &*reporter;
|
||||
scope.spawn(move |_| {
|
||||
let check_file_span =
|
||||
tracing::debug_span!(parent: project_span, "check_file", ?file);
|
||||
@@ -276,10 +300,7 @@ impl Project {
|
||||
|
||||
match check_file_impl(&db, file) {
|
||||
Ok(diagnostics) => {
|
||||
file_diagnostics
|
||||
.lock()
|
||||
.unwrap()
|
||||
.extend(diagnostics.iter().map(Clone::clone));
|
||||
reporter.report_checked_file(&db, file, diagnostics);
|
||||
|
||||
// This is outside `check_file_impl` to avoid that opening or closing
|
||||
// a file invalidates the `check_file_impl` query of every file!
|
||||
@@ -295,28 +316,22 @@ impl Project {
|
||||
}
|
||||
}
|
||||
Err(io_error) => {
|
||||
file_diagnostics.lock().unwrap().push(io_error.clone());
|
||||
reporter.report_checked_file(
|
||||
&db,
|
||||
file,
|
||||
std::slice::from_ref(io_error),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
reporter.report_file(&file);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
tracing::debug!(
|
||||
"Checking all files took {:.3}s",
|
||||
check_start.elapsed().as_secs_f64(),
|
||||
);
|
||||
|
||||
let mut file_diagnostics = file_diagnostics.into_inner().unwrap();
|
||||
file_diagnostics.sort_by(|left, right| {
|
||||
left.rendering_sort_key(db)
|
||||
.cmp(&right.rendering_sort_key(db))
|
||||
});
|
||||
diagnostics.extend(file_diagnostics);
|
||||
diagnostics
|
||||
}
|
||||
|
||||
pub(crate) fn check_file(self, db: &dyn Db, file: File) -> Vec<Diagnostic> {
|
||||
@@ -511,7 +526,7 @@ impl Project {
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::tracked(returns(ref), heap_size=get_size2::GetSize::get_heap_size)]
|
||||
#[salsa::tracked(returns(ref), heap_size=get_size2::heap_size)]
|
||||
pub(crate) fn check_file_impl(db: &dyn Db, file: File) -> Result<Box<[Diagnostic]>, Diagnostic> {
|
||||
let mut diagnostics: Vec<Diagnostic> = Vec::new();
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ impl Override {
|
||||
}
|
||||
|
||||
/// Resolves the settings for a given file.
|
||||
#[salsa::tracked(returns(ref), heap_size=get_size2::GetSize::get_heap_size)]
|
||||
#[salsa::tracked(returns(ref), heap_size=get_size2::heap_size)]
|
||||
pub(crate) fn file_settings(db: &dyn Db, file: File) -> FileSettings {
|
||||
let settings = db.project().settings(db);
|
||||
|
||||
@@ -155,7 +155,7 @@ pub(crate) fn file_settings(db: &dyn Db, file: File) -> FileSettings {
|
||||
/// This is to make Salsa happy because it requires that queries with only a single argument
|
||||
/// take a salsa-struct as argument, which isn't the case here. The `()` enables salsa's
|
||||
/// automatic interning for the arguments.
|
||||
#[salsa::tracked(heap_size=get_size2::GetSize::get_heap_size)]
|
||||
#[salsa::tracked(heap_size=get_size2::heap_size)]
|
||||
fn merge_overrides(db: &dyn Db, overrides: Vec<Arc<InnerOverrideOptions>>, _: ()) -> FileSettings {
|
||||
let mut overrides = overrides.into_iter().rev();
|
||||
let mut merged = (*overrides.next().unwrap()).clone();
|
||||
|
||||
@@ -173,21 +173,30 @@ impl<'a> ProjectFilesWalker<'a> {
|
||||
Ok(entry) => {
|
||||
// Skip excluded directories unless they were explicitly passed to the walker
|
||||
// (which is the case passed to `ty check <paths>`).
|
||||
if entry.file_type().is_directory() && entry.depth() > 0 {
|
||||
return match self.filter.is_directory_included(entry.path(), GlobFilterCheckMode::TopDown) {
|
||||
IncludeResult::Included => WalkState::Continue,
|
||||
IncludeResult::Excluded => {
|
||||
tracing::debug!("Skipping directory '{path}' because it is excluded by a default or `src.exclude` pattern", path=entry.path());
|
||||
WalkState::Skip
|
||||
},
|
||||
IncludeResult::NotIncluded => {
|
||||
tracing::debug!("Skipping directory `{path}` because it doesn't match any `src.include` pattern or path specified on the CLI", path=entry.path());
|
||||
WalkState::Skip
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if entry.file_type().is_file() {
|
||||
if entry.file_type().is_directory() {
|
||||
if entry.depth() > 0 {
|
||||
let directory_included = self
|
||||
.filter
|
||||
.is_directory_included(entry.path(), GlobFilterCheckMode::TopDown);
|
||||
return match directory_included {
|
||||
IncludeResult::Included => WalkState::Continue,
|
||||
IncludeResult::Excluded => {
|
||||
tracing::debug!(
|
||||
"Skipping directory '{path}' because it is excluded by a default or `src.exclude` pattern",
|
||||
path=entry.path()
|
||||
);
|
||||
WalkState::Skip
|
||||
},
|
||||
IncludeResult::NotIncluded => {
|
||||
tracing::debug!(
|
||||
"Skipping directory `{path}` because it doesn't match any `src.include` pattern or path specified on the CLI",
|
||||
path=entry.path()
|
||||
);
|
||||
WalkState::Skip
|
||||
},
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// Ignore any non python files to avoid creating too many entries in `Files`.
|
||||
if entry
|
||||
.path()
|
||||
@@ -201,14 +210,23 @@ impl<'a> ProjectFilesWalker<'a> {
|
||||
// For all files, except the ones that were explicitly passed to the walker (CLI),
|
||||
// check if they're included in the project.
|
||||
if entry.depth() > 0 {
|
||||
match self.filter.is_file_included(entry.path(), GlobFilterCheckMode::TopDown) {
|
||||
match self
|
||||
.filter
|
||||
.is_file_included(entry.path(), GlobFilterCheckMode::TopDown)
|
||||
{
|
||||
IncludeResult::Included => {},
|
||||
IncludeResult::Excluded => {
|
||||
tracing::debug!("Ignoring file `{path}` because it is excluded by a default or `src.exclude` pattern.", path=entry.path());
|
||||
tracing::debug!(
|
||||
"Ignoring file `{path}` because it is excluded by a default or `src.exclude` pattern.",
|
||||
path=entry.path()
|
||||
);
|
||||
return WalkState::Continue;
|
||||
},
|
||||
IncludeResult::NotIncluded => {
|
||||
tracing::debug!("Ignoring file `{path}` because it doesn't match any `src.include` pattern or path specified on the CLI.", path=entry.path());
|
||||
tracing::debug!(
|
||||
"Ignoring file `{path}` because it doesn't match any `src.include` pattern or path specified on the CLI.",
|
||||
path=entry.path()
|
||||
);
|
||||
return WalkState::Continue;
|
||||
},
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ from typing import Self
|
||||
|
||||
class Shape:
|
||||
def set_scale(self: Self, scale: float) -> Self:
|
||||
reveal_type(self) # revealed: Self
|
||||
reveal_type(self) # revealed: Self@Shape
|
||||
return self
|
||||
|
||||
def nested_type(self: Self) -> list[Self]:
|
||||
@@ -24,7 +24,7 @@ class Shape:
|
||||
|
||||
def nested_func(self: Self) -> Self:
|
||||
def inner() -> Self:
|
||||
reveal_type(self) # revealed: Self
|
||||
reveal_type(self) # revealed: Self@Shape
|
||||
return self
|
||||
return inner()
|
||||
|
||||
@@ -38,13 +38,13 @@ reveal_type(Shape().nested_func()) # revealed: Shape
|
||||
|
||||
class Circle(Shape):
|
||||
def set_scale(self: Self, scale: float) -> Self:
|
||||
reveal_type(self) # revealed: Self
|
||||
reveal_type(self) # revealed: Self@Circle
|
||||
return self
|
||||
|
||||
class Outer:
|
||||
class Inner:
|
||||
def foo(self: Self) -> Self:
|
||||
reveal_type(self) # revealed: Self
|
||||
reveal_type(self) # revealed: Self@Inner
|
||||
return self
|
||||
```
|
||||
|
||||
@@ -151,7 +151,7 @@ from typing import Self
|
||||
|
||||
class Shape:
|
||||
def union(self: Self, other: Self | None):
|
||||
reveal_type(other) # revealed: Self | None
|
||||
reveal_type(other) # revealed: Self@Shape | None
|
||||
return self
|
||||
```
|
||||
|
||||
|
||||
@@ -18,5 +18,5 @@ def append_int(*args: *Ts) -> tuple[*Ts, int]:
|
||||
return (*args, 1)
|
||||
|
||||
# TODO should be tuple[Literal[True], Literal["a"], int]
|
||||
reveal_type(append_int(True, "a")) # revealed: @Todo(PEP 646)
|
||||
reveal_type(append_int(True, "a")) # revealed: tuple[@Todo(PEP 646), ...]
|
||||
```
|
||||
|
||||
@@ -17,6 +17,7 @@ Alias: TypeAlias = int
|
||||
def f(*args: Unpack[Ts]) -> tuple[Unpack[Ts]]:
|
||||
reveal_type(args) # revealed: tuple[@Todo(`Unpack[]` special form), ...]
|
||||
reveal_type(Alias) # revealed: @Todo(Support for `typing.TypeAlias`)
|
||||
return args
|
||||
|
||||
def g() -> TypeGuard[int]: ...
|
||||
def i(callback: Callable[Concatenate[int, P], R_co], *args: P.args, **kwargs: P.kwargs) -> R_co:
|
||||
@@ -26,7 +27,7 @@ def i(callback: Callable[Concatenate[int, P], R_co], *args: P.args, **kwargs: P.
|
||||
|
||||
class Foo:
|
||||
def method(self, x: Self):
|
||||
reveal_type(x) # revealed: Self
|
||||
reveal_type(x) # revealed: Self@Foo
|
||||
```
|
||||
|
||||
## Type expressions
|
||||
|
||||
@@ -59,7 +59,7 @@ reveal_type(d) # revealed: tuple[tuple[str, str], tuple[int, int]]
|
||||
reveal_type(e) # revealed: tuple[str, ...]
|
||||
|
||||
reveal_type(f) # revealed: tuple[str, *tuple[int, ...], bytes]
|
||||
reveal_type(g) # revealed: @Todo(PEP 646)
|
||||
reveal_type(g) # revealed: tuple[@Todo(PEP 646), ...]
|
||||
|
||||
reveal_type(h) # revealed: tuple[list[int], list[int]]
|
||||
reveal_type(i) # revealed: tuple[str | int, str | int]
|
||||
|
||||
@@ -13,7 +13,7 @@ reveal_type(x) # revealed: int | float
|
||||
|
||||
x = (1, 2)
|
||||
x += (3, 4)
|
||||
reveal_type(x) # revealed: tuple[Literal[1], Literal[2], Literal[3], Literal[4]]
|
||||
reveal_type(x) # revealed: tuple[Literal[1, 2, 3, 4], ...]
|
||||
```
|
||||
|
||||
## Dunder methods
|
||||
|
||||
123
crates/ty_python_semantic/resources/mdtest/async.md
Normal file
123
crates/ty_python_semantic/resources/mdtest/async.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# `async` / `await`
|
||||
|
||||
## Basic
|
||||
|
||||
```py
|
||||
async def retrieve() -> int:
|
||||
return 42
|
||||
|
||||
async def main():
|
||||
result = await retrieve()
|
||||
|
||||
reveal_type(result) # revealed: int
|
||||
```
|
||||
|
||||
## Generic `async` functions
|
||||
|
||||
```py
|
||||
from typing import TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
async def persist(x: T) -> T:
|
||||
return x
|
||||
|
||||
async def f(x: int):
|
||||
result = await persist(x)
|
||||
|
||||
reveal_type(result) # revealed: int
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
### `Future`
|
||||
|
||||
```py
|
||||
import asyncio
|
||||
import concurrent.futures
|
||||
|
||||
def blocking_function() -> int:
|
||||
return 42
|
||||
|
||||
async def main():
|
||||
loop = asyncio.get_event_loop()
|
||||
with concurrent.futures.ThreadPoolExecutor() as pool:
|
||||
result = await loop.run_in_executor(pool, blocking_function)
|
||||
|
||||
# TODO: should be `int`
|
||||
reveal_type(result) # revealed: Unknown
|
||||
```
|
||||
|
||||
### `asyncio.Task`
|
||||
|
||||
```py
|
||||
import asyncio
|
||||
|
||||
async def f() -> int:
|
||||
return 1
|
||||
|
||||
async def main():
|
||||
task = asyncio.create_task(f())
|
||||
|
||||
result = await task
|
||||
|
||||
# TODO: this should be `int`
|
||||
reveal_type(result) # revealed: Unknown
|
||||
```
|
||||
|
||||
### `asyncio.gather`
|
||||
|
||||
```py
|
||||
import asyncio
|
||||
|
||||
async def task(name: str) -> int:
|
||||
return len(name)
|
||||
|
||||
async def main():
|
||||
(a, b) = await asyncio.gather(
|
||||
task("A"),
|
||||
task("B"),
|
||||
)
|
||||
|
||||
# TODO: these should be `int`
|
||||
reveal_type(a) # revealed: Unknown
|
||||
reveal_type(b) # revealed: Unknown
|
||||
```
|
||||
|
||||
## Under the hood
|
||||
|
||||
```toml
|
||||
[environment]
|
||||
python-version = "3.12" # Use 3.12 to be able to use PEP 695 generics
|
||||
```
|
||||
|
||||
Let's look at the example from the beginning again:
|
||||
|
||||
```py
|
||||
async def retrieve() -> int:
|
||||
return 42
|
||||
```
|
||||
|
||||
When we look at the signature of this function, we see that it actually returns a `CoroutineType`:
|
||||
|
||||
```py
|
||||
reveal_type(retrieve) # revealed: def retrieve() -> CoroutineType[Any, Any, int]
|
||||
```
|
||||
|
||||
The expression `await retrieve()` desugars into a call to the `__await__` dunder method on the
|
||||
`CoroutineType` object, followed by a `yield from`. Let's first see the return type of `__await__`:
|
||||
|
||||
```py
|
||||
reveal_type(retrieve().__await__()) # revealed: Generator[Any, None, int]
|
||||
```
|
||||
|
||||
We can see that this returns a `Generator` that yields `Any`, and eventually returns `int`. For the
|
||||
final type of the `await` expression, we retrieve that third argument of the `Generator` type:
|
||||
|
||||
```py
|
||||
from typing import Generator
|
||||
|
||||
def _():
|
||||
result = yield from retrieve().__await__()
|
||||
reveal_type(result) # revealed: int
|
||||
```
|
||||
@@ -1916,7 +1916,7 @@ d = True
|
||||
reveal_type(d.__class__) # revealed: <class 'bool'>
|
||||
|
||||
e = (42, 42)
|
||||
reveal_type(e.__class__) # revealed: <class 'tuple[Literal[42], Literal[42]]'>
|
||||
reveal_type(e.__class__) # revealed: type[tuple[Literal[42], Literal[42]]]
|
||||
|
||||
def f(a: int, b: typing_extensions.LiteralString, c: int | str, d: type[str]):
|
||||
reveal_type(a.__class__) # revealed: type[int]
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
# Static binary operations using `in`
|
||||
|
||||
## Basic functionality
|
||||
|
||||
This demonstrates type inference support for `<str-literal> in <tuple>`:
|
||||
|
||||
```py
|
||||
from ty_extensions import static_assert
|
||||
|
||||
static_assert("foo" in ("quux", "foo", "baz"))
|
||||
static_assert("foo" not in ("quux", "bar", "baz"))
|
||||
```
|
||||
|
||||
## With variables
|
||||
|
||||
```py
|
||||
from ty_extensions import static_assert
|
||||
|
||||
x = ("quux", "foo", "baz")
|
||||
static_assert("foo" in x)
|
||||
|
||||
x = ("quux", "bar", "baz")
|
||||
static_assert("foo" not in x)
|
||||
```
|
||||
|
||||
## Statically unknown results in a `bool`
|
||||
|
||||
```py
|
||||
def _(a: str, b: str):
|
||||
reveal_type("foo" in (a, b)) # revealed: bool
|
||||
```
|
||||
|
||||
## Values being unknown doesn't mean the result is unknown
|
||||
|
||||
For example, when the types are completely disjoint:
|
||||
|
||||
```py
|
||||
from ty_extensions import static_assert
|
||||
|
||||
def _(a: int, b: int):
|
||||
static_assert("foo" not in (a, b))
|
||||
```
|
||||
|
||||
## Failure cases
|
||||
|
||||
```py
|
||||
# We don't support byte strings.
|
||||
reveal_type(b"foo" not in (b"quux", b"foo", b"baz")) # revealed: bool
|
||||
```
|
||||
@@ -3,14 +3,14 @@
|
||||
## Concatenation for heterogeneous tuples
|
||||
|
||||
```py
|
||||
reveal_type((1, 2) + (3, 4)) # revealed: tuple[Literal[1], Literal[2], Literal[3], Literal[4]]
|
||||
reveal_type(() + (1, 2)) # revealed: tuple[Literal[1], Literal[2]]
|
||||
reveal_type((1, 2) + ()) # revealed: tuple[Literal[1], Literal[2]]
|
||||
reveal_type((1, 2) + (3, 4)) # revealed: tuple[Literal[1, 2, 3, 4], ...]
|
||||
reveal_type(() + (1, 2)) # revealed: tuple[Literal[1, 2], ...]
|
||||
reveal_type((1, 2) + ()) # revealed: tuple[Literal[1, 2], ...]
|
||||
reveal_type(() + ()) # revealed: tuple[()]
|
||||
|
||||
def _(x: tuple[int, str], y: tuple[None, tuple[int]]):
|
||||
reveal_type(x + y) # revealed: tuple[int, str, None, tuple[int]]
|
||||
reveal_type(y + x) # revealed: tuple[None, tuple[int], int, str]
|
||||
reveal_type(x + y) # revealed: tuple[int | str | None | tuple[int], ...]
|
||||
reveal_type(y + x) # revealed: tuple[None | tuple[int] | int | str, ...]
|
||||
```
|
||||
|
||||
## Concatenation for homogeneous tuples
|
||||
@@ -19,10 +19,10 @@ def _(x: tuple[int, str], y: tuple[None, tuple[int]]):
|
||||
def _(x: tuple[int, ...], y: tuple[str, ...]):
|
||||
reveal_type(x + x) # revealed: tuple[int, ...]
|
||||
reveal_type(x + y) # revealed: tuple[int | str, ...]
|
||||
reveal_type((1, 2) + x) # revealed: tuple[Literal[1], Literal[2], *tuple[int, ...]]
|
||||
reveal_type(x + (3, 4)) # revealed: tuple[*tuple[int, ...], Literal[3], Literal[4]]
|
||||
reveal_type((1, 2) + x + (3, 4)) # revealed: tuple[Literal[1], Literal[2], *tuple[int, ...], Literal[3], Literal[4]]
|
||||
reveal_type((1, 2) + y + (3, 4) + x) # revealed: tuple[Literal[1], Literal[2], *tuple[int | str, ...]]
|
||||
reveal_type((1, 2) + x) # revealed: tuple[int, ...]
|
||||
reveal_type(x + (3, 4)) # revealed: tuple[int, ...]
|
||||
reveal_type((1, 2) + x + (3, 4)) # revealed: tuple[int, ...]
|
||||
reveal_type((1, 2) + y + (3, 4) + x) # revealed: tuple[int | str, ...]
|
||||
```
|
||||
|
||||
We get the same results even when we use a legacy type alias, even though this involves first
|
||||
@@ -41,8 +41,8 @@ StrTuple = tuple[str, ...]
|
||||
def _(one_two: OneTwo, x: IntTuple, y: StrTuple, three_four: ThreeFour):
|
||||
reveal_type(x + x) # revealed: tuple[int, ...]
|
||||
reveal_type(x + y) # revealed: tuple[int | str, ...]
|
||||
reveal_type(one_two + x) # revealed: tuple[Literal[1], Literal[2], *tuple[int, ...]]
|
||||
reveal_type(x + three_four) # revealed: tuple[*tuple[int, ...], Literal[3], Literal[4]]
|
||||
reveal_type(one_two + x + three_four) # revealed: tuple[Literal[1], Literal[2], *tuple[int, ...], Literal[3], Literal[4]]
|
||||
reveal_type(one_two + y + three_four + x) # revealed: tuple[Literal[1], Literal[2], *tuple[int | str, ...]]
|
||||
reveal_type(one_two + x) # revealed: tuple[int, ...]
|
||||
reveal_type(x + three_four) # revealed: tuple[int, ...]
|
||||
reveal_type(one_two + x + three_four) # revealed: tuple[int, ...]
|
||||
reveal_type(one_two + y + three_four + x) # revealed: tuple[int | str, ...]
|
||||
```
|
||||
|
||||
@@ -152,7 +152,8 @@ s = SubclassOfA()
|
||||
reveal_type(isinstance(s, SubclassOfA)) # revealed: Literal[True]
|
||||
reveal_type(isinstance(s, A)) # revealed: Literal[True]
|
||||
|
||||
def _(x: A | B):
|
||||
def _(x: A | B, y: list[int]):
|
||||
reveal_type(isinstance(y, list)) # revealed: Literal[True]
|
||||
reveal_type(isinstance(x, A)) # revealed: bool
|
||||
|
||||
if isinstance(x, A):
|
||||
|
||||
@@ -15,8 +15,7 @@ reveal_type(get_int()) # revealed: int
|
||||
async def get_int_async() -> int:
|
||||
return 42
|
||||
|
||||
# TODO: we don't yet support `types.CoroutineType`, should be generic `Coroutine[Any, Any, int]`
|
||||
reveal_type(get_int_async()) # revealed: @Todo(generic types.CoroutineType)
|
||||
reveal_type(get_int_async()) # revealed: CoroutineType[Any, Any, int]
|
||||
```
|
||||
|
||||
## Generic
|
||||
|
||||
@@ -128,7 +128,7 @@ class AsyncIterable:
|
||||
return AsyncIterator()
|
||||
|
||||
async def _():
|
||||
# revealed: @Todo(async iterables/iterators)
|
||||
# revealed: int
|
||||
[reveal_type(x) async for x in AsyncIterable()]
|
||||
```
|
||||
|
||||
@@ -147,6 +147,7 @@ class Iterable:
|
||||
return Iterator()
|
||||
|
||||
async def _():
|
||||
# revealed: @Todo(async iterables/iterators)
|
||||
# error: [not-iterable] "Object of type `Iterable` is not async-iterable"
|
||||
# revealed: Unknown
|
||||
[reveal_type(x) async for x in Iterable()]
|
||||
```
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user