Compare commits
280 Commits
david/stab
...
brent/stab
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db83038c04 | ||
|
|
021f0c01aa | ||
|
|
75cfafd9a1 | ||
|
|
d4d29858b4 | ||
|
|
829acf498d | ||
|
|
e07f352f99 | ||
|
|
8d0b6882b7 | ||
|
|
65a2daea02 | ||
|
|
8baaa2f7f3 | ||
|
|
8b1ce32f04 | ||
|
|
eb5abda8ac | ||
|
|
9c4ecf77b6 | ||
|
|
0809d88ca0 | ||
|
|
5c59167686 | ||
|
|
e2ea301c74 | ||
|
|
62364ea47e | ||
|
|
331821244b | ||
|
|
1dc8f8f903 | ||
|
|
301b9f4135 | ||
|
|
86e5a311f0 | ||
|
|
0c20010bb9 | ||
|
|
72552f31e4 | ||
|
|
95497ffaab | ||
|
|
b3b900dc1e | ||
|
|
503427855d | ||
|
|
6e785867c3 | ||
|
|
1274521f9f | ||
|
|
8d24760643 | ||
|
|
db8db536f8 | ||
|
|
cb8246bc5f | ||
|
|
5faf72a4d9 | ||
|
|
28dbc5c51e | ||
|
|
ce216c79cc | ||
|
|
33468cc8cc | ||
|
|
8531f4b3ca | ||
|
|
55100209c7 | ||
|
|
c0bb83b882 | ||
|
|
74a4e9af3d | ||
|
|
8485dbb324 | ||
|
|
0858896bc4 | ||
|
|
ce8b744f17 | ||
|
|
5a8cdab771 | ||
|
|
3a8191529c | ||
|
|
e658778ced | ||
|
|
f1883d71a4 | ||
|
|
11db567b0b | ||
|
|
9f8c3de462 | ||
|
|
293d4ac388 | ||
|
|
9e8a7e9353 | ||
|
|
453e5f5934 | ||
|
|
7ea773daf2 | ||
|
|
0079cc6817 | ||
|
|
e8ea40012a | ||
|
|
71d8a5da2a | ||
|
|
2c3b3d3230 | ||
|
|
8d98c601d8 | ||
|
|
0986edf427 | ||
|
|
03f1f8e218 | ||
|
|
628bb2cd1d | ||
|
|
f23d2c9b9e | ||
|
|
67d94d9ec8 | ||
|
|
d1cb8e2142 | ||
|
|
57202c1c77 | ||
|
|
2289187b74 | ||
|
|
14c42a8ddf | ||
|
|
e677863787 | ||
|
|
f379eb6e62 | ||
|
|
47698883ae | ||
|
|
e2d96df501 | ||
|
|
384e80ec80 | ||
|
|
b9f3b0e0a6 | ||
|
|
1e6d76c878 | ||
|
|
844c8626c3 | ||
|
|
1c8d9d707e | ||
|
|
4856377478 | ||
|
|
643c845a47 | ||
|
|
9e952cf0e0 | ||
|
|
c4015edf48 | ||
|
|
97b824db3e | ||
|
|
220ab88779 | ||
|
|
7a63ac145a | ||
|
|
54f597658c | ||
|
|
aa1fad61e0 | ||
|
|
b390b3cb8e | ||
|
|
88866f0048 | ||
|
|
9bbf4987e8 | ||
|
|
ad024f9a09 | ||
|
|
fc549bda94 | ||
|
|
77c8ddf101 | ||
|
|
e730f27f80 | ||
|
|
d65bd69963 | ||
|
|
c713e76e4d | ||
|
|
8005ebb405 | ||
|
|
0c29e258c6 | ||
|
|
b5b6b657cc | ||
|
|
ad2f667ee4 | ||
|
|
363f061f09 | ||
|
|
9b0dfc505f | ||
|
|
695de4f27f | ||
|
|
3445d1322d | ||
|
|
2c3f091e0e | ||
|
|
9d3cad95bc | ||
|
|
7df79cfb70 | ||
|
|
33ed502edb | ||
|
|
a827b16ebd | ||
|
|
47a2ec002e | ||
|
|
aee3af0f7a | ||
|
|
04dc48e17c | ||
|
|
27743efa1b | ||
|
|
c60b4d7f30 | ||
|
|
16621fa19d | ||
|
|
e23d4ea027 | ||
|
|
452f992fbc | ||
|
|
a5ebb3f3a2 | ||
|
|
9925910a29 | ||
|
|
a3ee6bb3b5 | ||
|
|
b60ba75d09 | ||
|
|
66ba1d8775 | ||
|
|
bbcd7e0196 | ||
|
|
48c425c15b | ||
|
|
6d210dd0c7 | ||
|
|
9ce83c215d | ||
|
|
602dd5c039 | ||
|
|
3eada01153 | ||
|
|
3e811fc369 | ||
|
|
743764d384 | ||
|
|
e03e05d2b3 | ||
|
|
9ec4a178a4 | ||
|
|
8d5655a7ba | ||
|
|
6453ac9ea1 | ||
|
|
0a11baf29c | ||
|
|
1d20cf9570 | ||
|
|
62ef96f51e | ||
|
|
4e68dd96a6 | ||
|
|
b25b642371 | ||
|
|
175402aa75 | ||
|
|
d8216fa328 | ||
|
|
d51f6940fe | ||
|
|
66b082ff71 | ||
|
|
5d93d619f3 | ||
|
|
e1b662bf5d | ||
|
|
f885cb8a2f | ||
|
|
4ef2c223c9 | ||
|
|
d078ecff37 | ||
|
|
7eca6f96e3 | ||
|
|
fbaf826a9d | ||
|
|
d8a5b9de17 | ||
|
|
c3feb8ce27 | ||
|
|
97ff015c88 | ||
|
|
1f7134f727 | ||
|
|
6a0b93170e | ||
|
|
cc59ff8aad | ||
|
|
2b90e7fcd7 | ||
|
|
a43f5b2129 | ||
|
|
f3fb7429ca | ||
|
|
83498b95fb | ||
|
|
03d7be3747 | ||
|
|
d95b029862 | ||
|
|
14c3755445 | ||
|
|
83a036960b | ||
|
|
be76fadb05 | ||
|
|
e293411679 | ||
|
|
53d19f8368 | ||
|
|
a1399656c9 | ||
|
|
6392dccd24 | ||
|
|
93ac0934dd | ||
|
|
aae4482c55 | ||
|
|
d02c9ada5d | ||
|
|
6c0a59ea78 | ||
|
|
0b181bc2ad | ||
|
|
0397682f1f | ||
|
|
bcefa459f4 | ||
|
|
91b7a570c2 | ||
|
|
98da200d45 | ||
|
|
029085fa72 | ||
|
|
6df10c638e | ||
|
|
bdf488462a | ||
|
|
01eeb2f0d6 | ||
|
|
cb04343b3b | ||
|
|
02394b8049 | ||
|
|
41463396cf | ||
|
|
da4be789ef | ||
|
|
02fd48132c | ||
|
|
d37592175f | ||
|
|
cb9e66927e | ||
|
|
76ab77fe01 | ||
|
|
7b253100f8 | ||
|
|
d098118e37 | ||
|
|
7917269d9a | ||
|
|
e8d4f6d891 | ||
|
|
60b486abce | ||
|
|
32403dfb28 | ||
|
|
76ab3425d3 | ||
|
|
90ca0a4c13 | ||
|
|
15dbfad265 | ||
|
|
4f8a005f8f | ||
|
|
3b56c7ca3d | ||
|
|
f9ca6eb63e | ||
|
|
8729cb208f | ||
|
|
a2c87c2bc1 | ||
|
|
b302d89da3 | ||
|
|
ce43dbab58 | ||
|
|
fb589730ef | ||
|
|
4fad15805b | ||
|
|
0ede831a3f | ||
|
|
d6009eb942 | ||
|
|
236633cd42 | ||
|
|
99cb89f90f | ||
|
|
ac5df56aa3 | ||
|
|
6985de4c40 | ||
|
|
55a410a885 | ||
|
|
97058e8093 | ||
|
|
569c94b71b | ||
|
|
59d80aff9f | ||
|
|
b913f568c4 | ||
|
|
4c889d5251 | ||
|
|
220137ca7b | ||
|
|
34337fb8ba | ||
|
|
38c332fe23 | ||
|
|
9f743d1b9f | ||
|
|
405544cc8f | ||
|
|
ab96adbcd1 | ||
|
|
a761b8cfa2 | ||
|
|
8c020cc2e9 | ||
|
|
c67aa0cce2 | ||
|
|
b00e390f3a | ||
|
|
1f9df0c8f0 | ||
|
|
9dd9227bca | ||
|
|
181a380ee0 | ||
|
|
12f5e99389 | ||
|
|
d9cd6399e6 | ||
|
|
c40a801002 | ||
|
|
04168cf1ce | ||
|
|
6d0703ae78 | ||
|
|
f7691a79a0 | ||
|
|
6e7340c68b | ||
|
|
5095248b7e | ||
|
|
b1e6c6edce | ||
|
|
8ee92c6c77 | ||
|
|
d6709abd94 | ||
|
|
660375d429 | ||
|
|
dd04ca7f58 | ||
|
|
b86960f18c | ||
|
|
2abcd86c57 | ||
|
|
c6e55f673c | ||
|
|
3d55a16c91 | ||
|
|
e21972a79b | ||
|
|
0adbb3d600 | ||
|
|
28fb802467 | ||
|
|
a1d007c37c | ||
|
|
1ba56b4bc6 | ||
|
|
e677cabd69 | ||
|
|
9910ec700c | ||
|
|
9ae698fe30 | ||
|
|
e67b35743a | ||
|
|
8644c9da43 | ||
|
|
196e4befba | ||
|
|
6e39250015 | ||
|
|
7dc4fefb47 | ||
|
|
e5435eb106 | ||
|
|
f53c580c53 | ||
|
|
2ceba6ae67 | ||
|
|
d3a7cb3fe4 | ||
|
|
69393b2e6e | ||
|
|
c066bf0127 | ||
|
|
a5ee1a3bb1 | ||
|
|
e2c5b83fe1 | ||
|
|
b35bf8ae07 | ||
|
|
279dac1c0e | ||
|
|
57617031de | ||
|
|
28b5a868d3 | ||
|
|
b6b7caa023 | ||
|
|
46be305ad2 | ||
|
|
c3a4992ae9 | ||
|
|
9aa6330bb1 | ||
|
|
b600ff106a | ||
|
|
466021d5e1 | ||
|
|
33e14c5963 | ||
|
|
6800a9f6f3 | ||
|
|
68559fc17d |
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -21,6 +21,7 @@ crates/ruff_linter/resources/test/fixtures/pyupgrade/UP018_LF.py text eol=lf
|
||||
crates/ruff_python_parser/resources/inline linguist-generated=true
|
||||
|
||||
ruff.schema.json -diff linguist-generated=true text=auto eol=lf
|
||||
ty.schema.json -diff linguist-generated=true text=auto eol=lf
|
||||
crates/ruff_python_ast/src/generated.rs -diff linguist-generated=true text=auto eol=lf
|
||||
crates/ruff_python_formatter/src/generated.rs -diff linguist-generated=true text=auto eol=lf
|
||||
*.md.snap linguist-language=Markdown
|
||||
|
||||
1
.github/mypy-primer-ty.toml
vendored
1
.github/mypy-primer-ty.toml
vendored
@@ -5,3 +5,4 @@
|
||||
[rules]
|
||||
possibly-unresolved-reference = "warn"
|
||||
unused-ignore-comment = "warn"
|
||||
division-by-zero = "warn"
|
||||
|
||||
4
.github/workflows/build-binaries.yml
vendored
4
.github/workflows/build-binaries.yml
vendored
@@ -310,7 +310,7 @@ jobs:
|
||||
manylinux: auto
|
||||
docker-options: ${{ matrix.platform.maturin_docker_options }}
|
||||
args: --release --locked --out dist
|
||||
- uses: uraimo/run-on-arch-action@ac33288c3728ca72563c97b8b88dda5a65a84448 # v2
|
||||
- uses: uraimo/run-on-arch-action@d94c13912ea685de38fccc1109385b83fd79427d # v3.0.1
|
||||
if: ${{ matrix.platform.arch != 'ppc64' && matrix.platform.arch != 'ppc64le'}}
|
||||
name: Test wheel
|
||||
with:
|
||||
@@ -441,7 +441,7 @@ jobs:
|
||||
manylinux: musllinux_1_2
|
||||
args: --release --locked --out dist
|
||||
docker-options: ${{ matrix.platform.maturin_docker_options }}
|
||||
- uses: uraimo/run-on-arch-action@ac33288c3728ca72563c97b8b88dda5a65a84448 # v2
|
||||
- uses: uraimo/run-on-arch-action@d94c13912ea685de38fccc1109385b83fd79427d # v3.0.1
|
||||
name: Test wheel
|
||||
with:
|
||||
arch: ${{ matrix.platform.arch }}
|
||||
|
||||
20
.github/workflows/build-docker.yml
vendored
20
.github/workflows/build-docker.yml
vendored
@@ -38,7 +38,7 @@ jobs:
|
||||
submodules: recursive
|
||||
persist-credentials: false
|
||||
|
||||
- uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
||||
- uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
||||
|
||||
- uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
with:
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.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
|
||||
@@ -79,7 +79,7 @@ jobs:
|
||||
# Adapted from https://docs.docker.com/build/ci/github-actions/multi-platform/
|
||||
- name: Build and push by digest
|
||||
id: build
|
||||
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
with:
|
||||
context: .
|
||||
platforms: ${{ matrix.platform }}
|
||||
@@ -119,11 +119,11 @@ jobs:
|
||||
pattern: digests-*
|
||||
merge-multiple: true
|
||||
|
||||
- uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
||||
- uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.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
|
||||
@@ -167,7 +167,7 @@ jobs:
|
||||
- debian:bookworm-slim,bookworm-slim,debian-slim
|
||||
- buildpack-deps:bookworm,bookworm,debian
|
||||
steps:
|
||||
- uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
||||
- uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
||||
|
||||
- uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
with:
|
||||
@@ -219,7 +219,7 @@ jobs:
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||
# ghcr.io prefers index level annotations
|
||||
env:
|
||||
DOCKER_METADATA_ANNOTATIONS_LEVELS: index
|
||||
@@ -231,7 +231,7 @@ jobs:
|
||||
${{ env.TAG_PATTERNS }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
@@ -262,11 +262,11 @@ jobs:
|
||||
pattern: digests-*
|
||||
merge-multiple: true
|
||||
|
||||
- uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
|
||||
- uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||
env:
|
||||
DOCKER_METADATA_ANNOTATIONS_LEVELS: index
|
||||
with:
|
||||
|
||||
44
.github/workflows/ci.yaml
vendored
44
.github/workflows/ci.yaml
vendored
@@ -237,13 +237,13 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@e16410e7f8d9e167b74ad5697a9089a35126eb50 # v1
|
||||
uses: rui314/setup-mold@67424c1b3680e35255d95971cbd5de0047bf31c3 # v1
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@83254c543806f3224380bf1001d6fac8feaf2d0b # v2
|
||||
uses: taiki-e/install-action@735e5933943122c5ac182670a935f54a949265c1 # v2.52.4
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Install cargo insta"
|
||||
uses: taiki-e/install-action@83254c543806f3224380bf1001d6fac8feaf2d0b # v2
|
||||
uses: taiki-e/install-action@735e5933943122c5ac182670a935f54a949265c1 # v2.52.4
|
||||
with:
|
||||
tool: cargo-insta
|
||||
- name: ty mdtests (GitHub annotations)
|
||||
@@ -295,13 +295,13 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@e16410e7f8d9e167b74ad5697a9089a35126eb50 # v1
|
||||
uses: rui314/setup-mold@67424c1b3680e35255d95971cbd5de0047bf31c3 # v1
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@83254c543806f3224380bf1001d6fac8feaf2d0b # v2
|
||||
uses: taiki-e/install-action@735e5933943122c5ac182670a935f54a949265c1 # v2.52.4
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Install cargo insta"
|
||||
uses: taiki-e/install-action@83254c543806f3224380bf1001d6fac8feaf2d0b # v2
|
||||
uses: taiki-e/install-action@735e5933943122c5ac182670a935f54a949265c1 # v2.52.4
|
||||
with:
|
||||
tool: cargo-insta
|
||||
- name: "Run tests"
|
||||
@@ -324,7 +324,7 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@83254c543806f3224380bf1001d6fac8feaf2d0b # v2
|
||||
uses: taiki-e/install-action@735e5933943122c5ac182670a935f54a949265c1 # v2.52.4
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Run tests"
|
||||
@@ -380,7 +380,7 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@e16410e7f8d9e167b74ad5697a9089a35126eb50 # v1
|
||||
uses: rui314/setup-mold@67424c1b3680e35255d95971cbd5de0047bf31c3 # v1
|
||||
- name: "Build"
|
||||
run: cargo build --release --locked
|
||||
|
||||
@@ -405,13 +405,13 @@ jobs:
|
||||
MSRV: ${{ steps.msrv.outputs.value }}
|
||||
run: rustup default "${MSRV}"
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@e16410e7f8d9e167b74ad5697a9089a35126eb50 # v1
|
||||
uses: rui314/setup-mold@67424c1b3680e35255d95971cbd5de0047bf31c3 # v1
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@83254c543806f3224380bf1001d6fac8feaf2d0b # v2
|
||||
uses: taiki-e/install-action@735e5933943122c5ac182670a935f54a949265c1 # v2.52.4
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Install cargo insta"
|
||||
uses: taiki-e/install-action@83254c543806f3224380bf1001d6fac8feaf2d0b # v2
|
||||
uses: taiki-e/install-action@735e5933943122c5ac182670a935f54a949265c1 # v2.52.4
|
||||
with:
|
||||
tool: cargo-insta
|
||||
- name: "Run tests"
|
||||
@@ -437,7 +437,7 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install cargo-binstall"
|
||||
uses: cargo-bins/cargo-binstall@13f9d60d5358393bf14644dba56d9f123bc5d595 # v1.12.4
|
||||
uses: cargo-bins/cargo-binstall@e8c9cc3599f6c4063d143083205f98ca25d91677 # v1.12.6
|
||||
with:
|
||||
tool: cargo-fuzz@0.11.2
|
||||
- name: "Install cargo-fuzz"
|
||||
@@ -459,7 +459,7 @@ jobs:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
|
||||
- uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
|
||||
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
name: Download Ruff binary to test
|
||||
id: download-cached-binary
|
||||
@@ -504,12 +504,10 @@ jobs:
|
||||
# Verify that adding a plugin or rule produces clean code.
|
||||
- run: ./scripts/add_rule.py --name DoTheThing --prefix F --code 999 --linter pyflakes
|
||||
- run: cargo check
|
||||
- run: cargo fmt --all --check
|
||||
- run: |
|
||||
./scripts/add_plugin.py test --url https://pypi.org/project/-test/0.1.0/ --prefix TST
|
||||
./scripts/add_rule.py --name FirstRule --prefix TST --code 001 --linter test
|
||||
- run: cargo check
|
||||
- run: cargo fmt --all --check
|
||||
|
||||
ecosystem:
|
||||
name: "ecosystem"
|
||||
@@ -662,7 +660,7 @@ jobs:
|
||||
branch: ${{ github.event.pull_request.base.ref }}
|
||||
workflow: "ci.yaml"
|
||||
check_artifacts: true
|
||||
- uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
|
||||
- uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
|
||||
- name: Fuzz
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
@@ -692,7 +690,7 @@ jobs:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: cargo-bins/cargo-binstall@13f9d60d5358393bf14644dba56d9f123bc5d595 # v1.12.4
|
||||
- uses: cargo-bins/cargo-binstall@e8c9cc3599f6c4063d143083205f98ca25d91677 # v1.12.6
|
||||
- run: cargo binstall --no-confirm cargo-shear
|
||||
- run: cargo shear
|
||||
|
||||
@@ -732,7 +730,11 @@ jobs:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
|
||||
- uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
|
||||
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version: 22
|
||||
- name: "Cache pre-commit"
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
with:
|
||||
@@ -771,7 +773,7 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
|
||||
uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
|
||||
- name: "Install Insiders dependencies"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
run: uv pip install -r docs/requirements-insiders.txt --system
|
||||
@@ -820,7 +822,7 @@ jobs:
|
||||
- determine_changes
|
||||
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
|
||||
steps:
|
||||
- uses: extractions/setup-just@dd310ad5a97d8e7b41793f8ef055398d51ad4de6 # v2
|
||||
- uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3.0.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -908,7 +910,7 @@ jobs:
|
||||
run: rustup show
|
||||
|
||||
- name: "Install codspeed"
|
||||
uses: taiki-e/install-action@83254c543806f3224380bf1001d6fac8feaf2d0b # v2
|
||||
uses: taiki-e/install-action@735e5933943122c5ac182670a935f54a949265c1 # v2.52.4
|
||||
with:
|
||||
tool: cargo-codspeed
|
||||
|
||||
|
||||
4
.github/workflows/daily_fuzz.yaml
vendored
4
.github/workflows/daily_fuzz.yaml
vendored
@@ -34,11 +34,11 @@ jobs:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
|
||||
- uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@e16410e7f8d9e167b74ad5697a9089a35126eb50 # v1
|
||||
uses: rui314/setup-mold@67424c1b3680e35255d95971cbd5de0047bf31c3 # v1
|
||||
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||
- name: Build ruff
|
||||
# A debug build means the script runs slower once it gets started,
|
||||
|
||||
72
.github/workflows/daily_property_tests.yaml
vendored
72
.github/workflows/daily_property_tests.yaml
vendored
@@ -1,72 +0,0 @@
|
||||
name: Daily property test run
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 12 * * *"
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/daily_property_tests.yaml"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
CARGO_INCREMENTAL: 0
|
||||
CARGO_NET_RETRY: 10
|
||||
CARGO_TERM_COLOR: always
|
||||
RUSTUP_MAX_RETRIES: 10
|
||||
FORCE_COLOR: 1
|
||||
|
||||
jobs:
|
||||
property_tests:
|
||||
name: Property tests
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
# Don't run the cron job on forks:
|
||||
if: ${{ github.repository == 'astral-sh/ruff' || github.event_name != 'schedule' }}
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@e16410e7f8d9e167b74ad5697a9089a35126eb50 # v1
|
||||
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||
- name: Build ty
|
||||
# A release build takes longer (2 min vs 1 min), but the property tests run much faster in release
|
||||
# mode (1.5 min vs 14 min), so the overall time is shorter with a release build.
|
||||
run: cargo build --locked --release --package ty_python_semantic --tests
|
||||
- name: Run property tests
|
||||
shell: bash
|
||||
run: |
|
||||
export QUICKCHECK_TESTS=100000
|
||||
for _ in {1..5}; do
|
||||
cargo test --locked --release --package ty_python_semantic -- --ignored list::property_tests
|
||||
cargo test --locked --release --package ty_python_semantic -- --ignored types::property_tests::stable
|
||||
done
|
||||
|
||||
create-issue-on-failure:
|
||||
name: Create an issue if the daily property test run surfaced any bugs
|
||||
runs-on: ubuntu-latest
|
||||
needs: property_tests
|
||||
if: ${{ github.repository == 'astral-sh/ruff' && always() && github.event_name == 'schedule' && needs.property_tests.result == 'failure' }}
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
await github.rest.issues.create({
|
||||
owner: "astral-sh",
|
||||
repo: "ruff",
|
||||
title: `Daily property test run failed on ${new Date().toDateString()}`,
|
||||
body: "Run listed here: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}",
|
||||
labels: ["bug", "ty", "testing"],
|
||||
})
|
||||
5
.github/workflows/mypy_primer.yaml
vendored
5
.github/workflows/mypy_primer.yaml
vendored
@@ -11,6 +11,7 @@ on:
|
||||
- "crates/ruff_python_parser"
|
||||
- ".github/workflows/mypy_primer.yaml"
|
||||
- ".github/workflows/mypy_primer_comment.yaml"
|
||||
- "Cargo.lock"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.event.pull_request.number || github.sha }}
|
||||
@@ -36,7 +37,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install the latest version of uv
|
||||
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
|
||||
uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
|
||||
|
||||
- uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8
|
||||
with:
|
||||
@@ -69,7 +70,7 @@ jobs:
|
||||
echo "Project selector: $PRIMER_SELECTOR"
|
||||
# Allow the exit code to be 0 or 1, only fail for actual mypy_primer crashes/bugs
|
||||
uvx \
|
||||
--from="git+https://github.com/hauntsaninja/mypy_primer@968b2b61c05f84462d6fcc78d2f5205bbb8b98c2" \
|
||||
--from="git+https://github.com/hauntsaninja/mypy_primer@01a7ca325f674433c58e02416a867178d1571128" \
|
||||
mypy_primer \
|
||||
--repo ruff \
|
||||
--type-checker ty \
|
||||
|
||||
2
.github/workflows/mypy_primer_comment.yaml
vendored
2
.github/workflows/mypy_primer_comment.yaml
vendored
@@ -79,7 +79,7 @@ jobs:
|
||||
echo 'EOF' >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Find existing comment
|
||||
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3
|
||||
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3.1.0
|
||||
if: steps.generate-comment.outcome == 'success'
|
||||
id: find-comment
|
||||
with:
|
||||
|
||||
2
.github/workflows/pr-comment.yaml
vendored
2
.github/workflows/pr-comment.yaml
vendored
@@ -70,7 +70,7 @@ jobs:
|
||||
echo 'EOF' >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Find existing comment
|
||||
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3
|
||||
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3.1.0
|
||||
if: steps.generate-comment.outcome == 'success'
|
||||
id: find-comment
|
||||
with:
|
||||
|
||||
2
.github/workflows/publish-pypi.yml
vendored
2
.github/workflows/publish-pypi.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
id-token: write
|
||||
steps:
|
||||
- name: "Install uv"
|
||||
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
|
||||
uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
|
||||
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
with:
|
||||
pattern: wheels-*
|
||||
|
||||
@@ -29,3 +29,7 @@ MD024:
|
||||
#
|
||||
# Ref: https://github.com/astral-sh/ruff/pull/15011#issuecomment-2544790854
|
||||
MD046: false
|
||||
|
||||
# Link text should be descriptive
|
||||
# Disallows link text like *here* which is annoying.
|
||||
MD059: false
|
||||
|
||||
@@ -43,7 +43,7 @@ repos:
|
||||
)$
|
||||
|
||||
- repo: https://github.com/igorshubovych/markdownlint-cli
|
||||
rev: v0.44.0
|
||||
rev: v0.45.0
|
||||
hooks:
|
||||
- id: markdownlint-fix
|
||||
exclude: |
|
||||
@@ -80,7 +80,7 @@ repos:
|
||||
pass_filenames: false # This makes it a lot faster
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.11.9
|
||||
rev: v0.11.12
|
||||
hooks:
|
||||
- id: ruff-format
|
||||
- id: ruff
|
||||
@@ -98,7 +98,7 @@ repos:
|
||||
# zizmor detects security vulnerabilities in GitHub Actions workflows.
|
||||
# Additional configuration for the tool is found in `.github/zizmor.yml`
|
||||
- repo: https://github.com/woodruffw/zizmor-pre-commit
|
||||
rev: v1.7.0
|
||||
rev: v1.9.0
|
||||
hooks:
|
||||
- id: zizmor
|
||||
|
||||
|
||||
119
CHANGELOG.md
119
CHANGELOG.md
@@ -1,5 +1,124 @@
|
||||
# Changelog
|
||||
|
||||
## 0.11.13
|
||||
|
||||
### Preview features
|
||||
|
||||
- \[`airflow`\] Add unsafe fix for module moved cases (`AIR301`,`AIR311`,`AIR312`,`AIR302`) ([#18367](https://github.com/astral-sh/ruff/pull/18367),[#18366](https://github.com/astral-sh/ruff/pull/18366),[#18363](https://github.com/astral-sh/ruff/pull/18363),[#18093](https://github.com/astral-sh/ruff/pull/18093))
|
||||
- \[`refurb`\] Add coverage of `set` and `frozenset` calls (`FURB171`) ([#18035](https://github.com/astral-sh/ruff/pull/18035))
|
||||
- \[`refurb`\] Mark `FURB180` fix unsafe when class has bases ([#18149](https://github.com/astral-sh/ruff/pull/18149))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- \[`perflint`\] Fix missing parentheses for lambda and ternary conditions (`PERF401`, `PERF403`) ([#18412](https://github.com/astral-sh/ruff/pull/18412))
|
||||
- \[`pyupgrade`\] Apply `UP035` only on py313+ for `get_type_hints()` ([#18476](https://github.com/astral-sh/ruff/pull/18476))
|
||||
- \[`pyupgrade`\] Make fix unsafe if it deletes comments (`UP004`,`UP050`) ([#18393](https://github.com/astral-sh/ruff/pull/18393), [#18390](https://github.com/astral-sh/ruff/pull/18390))
|
||||
|
||||
### Rule changes
|
||||
|
||||
- \[`fastapi`\] Avoid false positive for class dependencies (`FAST003`) ([#18271](https://github.com/astral-sh/ruff/pull/18271))
|
||||
|
||||
### Documentation
|
||||
|
||||
- Update editor setup docs for Neovim and Vim ([#18324](https://github.com/astral-sh/ruff/pull/18324))
|
||||
|
||||
### Other changes
|
||||
|
||||
- Support Python 3.14 template strings (t-strings) in formatter and parser ([#17851](https://github.com/astral-sh/ruff/pull/17851))
|
||||
|
||||
## 0.11.12
|
||||
|
||||
### Preview features
|
||||
|
||||
- \[`airflow`\] Revise fix titles (`AIR3`) ([#18215](https://github.com/astral-sh/ruff/pull/18215))
|
||||
- \[`pylint`\] Implement `missing-maxsplit-arg` (`PLC0207`) ([#17454](https://github.com/astral-sh/ruff/pull/17454))
|
||||
- \[`pyupgrade`\] New rule `UP050` (`useless-class-metaclass-type`) ([#18334](https://github.com/astral-sh/ruff/pull/18334))
|
||||
- \[`flake8-use-pathlib`\] Replace `os.symlink` with `Path.symlink_to` (`PTH211`) ([#18337](https://github.com/astral-sh/ruff/pull/18337))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- \[`flake8-bugbear`\] Ignore `__debug__` attribute in `B010` ([#18357](https://github.com/astral-sh/ruff/pull/18357))
|
||||
- \[`flake8-async`\] Fix `anyio.sleep` argument name (`ASYNC115`, `ASYNC116`) ([#18262](https://github.com/astral-sh/ruff/pull/18262))
|
||||
- \[`refurb`\] Fix `FURB129` autofix generating invalid syntax ([#18235](https://github.com/astral-sh/ruff/pull/18235))
|
||||
|
||||
### Rule changes
|
||||
|
||||
- \[`flake8-implicit-str-concat`\] Add autofix for `ISC003` ([#18256](https://github.com/astral-sh/ruff/pull/18256))
|
||||
- \[`pycodestyle`\] Improve the diagnostic message for `E712` ([#18328](https://github.com/astral-sh/ruff/pull/18328))
|
||||
- \[`flake8-2020`\] Fix diagnostic message for `!=` comparisons (`YTT201`) ([#18293](https://github.com/astral-sh/ruff/pull/18293))
|
||||
- \[`pyupgrade`\] Make fix unsafe if it deletes comments (`UP010`) ([#18291](https://github.com/astral-sh/ruff/pull/18291))
|
||||
|
||||
### Documentation
|
||||
|
||||
- Simplify rules table to improve readability ([#18297](https://github.com/astral-sh/ruff/pull/18297))
|
||||
- Update editor integrations link in README ([#17977](https://github.com/astral-sh/ruff/pull/17977))
|
||||
- \[`flake8-bugbear`\] Add fix safety section (`B006`) ([#17652](https://github.com/astral-sh/ruff/pull/17652))
|
||||
|
||||
## 0.11.11
|
||||
|
||||
### Preview features
|
||||
|
||||
- \[`airflow`\] Add autofixes for `AIR302` and `AIR312` ([#17942](https://github.com/astral-sh/ruff/pull/17942))
|
||||
- \[`airflow`\] Move rules from `AIR312` to `AIR302` ([#17940](https://github.com/astral-sh/ruff/pull/17940))
|
||||
- \[`airflow`\] Update `AIR301` and `AIR311` with the latest Airflow implementations ([#17985](https://github.com/astral-sh/ruff/pull/17985))
|
||||
- \[`flake8-simplify`\] Enable fix in preview mode (`SIM117`) ([#18208](https://github.com/astral-sh/ruff/pull/18208))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Fix inconsistent formatting of match-case on `[]` and `_` ([#18147](https://github.com/astral-sh/ruff/pull/18147))
|
||||
- \[`pylint`\] Fix `PLW1514` not recognizing the `encoding` positional argument of `codecs.open` ([#18109](https://github.com/astral-sh/ruff/pull/18109))
|
||||
|
||||
### CLI
|
||||
|
||||
- Add full option name in formatter warning ([#18217](https://github.com/astral-sh/ruff/pull/18217))
|
||||
|
||||
### Documentation
|
||||
|
||||
- Fix rendering of admonition in docs ([#18163](https://github.com/astral-sh/ruff/pull/18163))
|
||||
- \[`flake8-print`\] Improve print/pprint docs for `T201` and `T203` ([#18130](https://github.com/astral-sh/ruff/pull/18130))
|
||||
- \[`flake8-simplify`\] Add fix safety section (`SIM110`,`SIM210`) ([#18114](https://github.com/astral-sh/ruff/pull/18114),[#18100](https://github.com/astral-sh/ruff/pull/18100))
|
||||
- \[`pylint`\] Fix docs example that produced different output (`PLW0603`) ([#18216](https://github.com/astral-sh/ruff/pull/18216))
|
||||
|
||||
## 0.11.10
|
||||
|
||||
### Preview features
|
||||
|
||||
- \[`ruff`\] Implement a recursive check for `RUF060` ([#17976](https://github.com/astral-sh/ruff/pull/17976))
|
||||
- \[`airflow`\] Enable autofixes for `AIR301` and `AIR311` ([#17941](https://github.com/astral-sh/ruff/pull/17941))
|
||||
- \[`airflow`\] Apply try catch guard to all `AIR3` rules ([#17887](https://github.com/astral-sh/ruff/pull/17887))
|
||||
- \[`airflow`\] Extend `AIR311` rules ([#17913](https://github.com/astral-sh/ruff/pull/17913))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- \[`flake8-bugbear`\] Ignore `B028` if `skip_file_prefixes` is present ([#18047](https://github.com/astral-sh/ruff/pull/18047))
|
||||
- \[`flake8-pie`\] Mark autofix for `PIE804` as unsafe if the dictionary contains comments ([#18046](https://github.com/astral-sh/ruff/pull/18046))
|
||||
- \[`flake8-simplify`\] Correct behavior for `str.split`/`rsplit` with `maxsplit=0` (`SIM905`) ([#18075](https://github.com/astral-sh/ruff/pull/18075))
|
||||
- \[`flake8-simplify`\] Fix `SIM905` autofix for `rsplit` creating a reversed list literal ([#18045](https://github.com/astral-sh/ruff/pull/18045))
|
||||
- \[`flake8-use-pathlib`\] Suppress diagnostics for all `os.*` functions that have the `dir_fd` parameter (`PTH`) ([#17968](https://github.com/astral-sh/ruff/pull/17968))
|
||||
- \[`refurb`\] Mark autofix as safe only for number literals (`FURB116`) ([#17692](https://github.com/astral-sh/ruff/pull/17692))
|
||||
|
||||
### Rule changes
|
||||
|
||||
- \[`flake8-bandit`\] Skip `S608` for expressionless f-strings ([#17999](https://github.com/astral-sh/ruff/pull/17999))
|
||||
- \[`flake8-pytest-style`\] Don't recommend `usefixtures` for `parametrize` values (`PT019`) ([#17650](https://github.com/astral-sh/ruff/pull/17650))
|
||||
- \[`pyupgrade`\] Add `resource.error` as deprecated alias of `OSError` (`UP024`) ([#17933](https://github.com/astral-sh/ruff/pull/17933))
|
||||
|
||||
### CLI
|
||||
|
||||
- Disable jemalloc on Android ([#18033](https://github.com/astral-sh/ruff/pull/18033))
|
||||
|
||||
### Documentation
|
||||
|
||||
- Update Neovim setup docs ([#18108](https://github.com/astral-sh/ruff/pull/18108))
|
||||
- \[`flake8-simplify`\] Add fix safety section (`SIM103`) ([#18086](https://github.com/astral-sh/ruff/pull/18086))
|
||||
- \[`flake8-simplify`\] Add fix safety section (`SIM112`) ([#18099](https://github.com/astral-sh/ruff/pull/18099))
|
||||
- \[`pylint`\] Add fix safety section (`PLC0414`) ([#17802](https://github.com/astral-sh/ruff/pull/17802))
|
||||
- \[`pylint`\] Add fix safety section (`PLE4703`) ([#17824](https://github.com/astral-sh/ruff/pull/17824))
|
||||
- \[`pylint`\] Add fix safety section (`PLW1514`) ([#17932](https://github.com/astral-sh/ruff/pull/17932))
|
||||
- \[`pylint`\] Add fix safety section (`PLW3301`) ([#17878](https://github.com/astral-sh/ruff/pull/17878))
|
||||
- \[`ruff`\] Add fix safety section (`RUF007`) ([#17755](https://github.com/astral-sh/ruff/pull/17755))
|
||||
- \[`ruff`\] Add fix safety section (`RUF033`) ([#17760](https://github.com/astral-sh/ruff/pull/17760))
|
||||
|
||||
## 0.11.9
|
||||
|
||||
### Preview features
|
||||
|
||||
@@ -411,7 +411,7 @@ cargo install hyperfine
|
||||
To benchmark the release build:
|
||||
|
||||
```shell
|
||||
cargo build --release && hyperfine --warmup 10 \
|
||||
cargo build --release --bin ruff && hyperfine --warmup 10 \
|
||||
"./target/release/ruff check ./crates/ruff_linter/resources/test/cpython/ --no-cache -e" \
|
||||
"./target/release/ruff check ./crates/ruff_linter/resources/test/cpython/ -e"
|
||||
|
||||
@@ -610,8 +610,7 @@ Then convert the recorded profile
|
||||
perf script -F +pid > /tmp/test.perf
|
||||
```
|
||||
|
||||
You can now view the converted file with [firefox profiler](https://profiler.firefox.com/), with a
|
||||
more in-depth guide [here](https://profiler.firefox.com/docs/#/./guide-perf-profiling)
|
||||
You can now view the converted file with [firefox profiler](https://profiler.firefox.com/). To learn more about Firefox profiler, read the [Firefox profiler profiling-guide](https://profiler.firefox.com/docs/#/./guide-perf-profiling).
|
||||
|
||||
An alternative is to convert the perf data to `flamegraph.svg` using
|
||||
[flamegraph](https://github.com/flamegraph-rs/flamegraph) (`cargo install flamegraph`):
|
||||
|
||||
816
Cargo.lock
generated
816
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
24
Cargo.toml
24
Cargo.toml
@@ -3,8 +3,9 @@ members = ["crates/*"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
edition = "2021"
|
||||
rust-version = "1.84"
|
||||
# Please update rustfmt.toml when bumping the Rust edition
|
||||
edition = "2024"
|
||||
rust-version = "1.85"
|
||||
homepage = "https://docs.astral.sh/ruff"
|
||||
documentation = "https://docs.astral.sh/ruff"
|
||||
repository = "https://github.com/astral-sh/ruff"
|
||||
@@ -52,7 +53,7 @@ anstyle = { version = "1.0.10" }
|
||||
anyhow = { version = "1.0.80" }
|
||||
assert_fs = { version = "1.1.0" }
|
||||
argfile = { version = "0.2.0" }
|
||||
bincode = { version = "1.3.3" }
|
||||
bincode = { version = "2.0.0" }
|
||||
bitflags = { version = "2.5.0" }
|
||||
bstr = { version = "1.9.1" }
|
||||
cachedir = { version = "0.3.1" }
|
||||
@@ -66,7 +67,7 @@ console_error_panic_hook = { version = "0.1.7" }
|
||||
console_log = { version = "1.0.0" }
|
||||
countme = { version = "3.0.1" }
|
||||
compact_str = "0.9.0"
|
||||
criterion = { version = "0.5.1", default-features = false }
|
||||
criterion = { version = "0.6.0", default-features = false }
|
||||
crossbeam = { version = "0.8.4" }
|
||||
dashmap = { version = "6.0.1" }
|
||||
dir-test = { version = "0.4.0" }
|
||||
@@ -85,6 +86,7 @@ hashbrown = { version = "0.15.0", default-features = false, features = [
|
||||
"equivalent",
|
||||
"inline-more",
|
||||
] }
|
||||
heck = "0.5.0"
|
||||
ignore = { version = "0.4.22" }
|
||||
imara-diff = { version = "0.1.5" }
|
||||
imperative = { version = "1.0.4" }
|
||||
@@ -98,7 +100,7 @@ is-wsl = { version = "0.4.0" }
|
||||
itertools = { version = "0.14.0" }
|
||||
jiff = { version = "0.2.0" }
|
||||
js-sys = { version = "0.3.69" }
|
||||
jod-thread = { version = "0.1.2" }
|
||||
jod-thread = { version = "1.0.0" }
|
||||
libc = { version = "0.2.153" }
|
||||
libcst = { version = "1.1.0", default-features = false }
|
||||
log = { version = "0.4.17" }
|
||||
@@ -127,7 +129,7 @@ regex = { version = "1.10.2" }
|
||||
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.git", rev = "7edce6e248f35c8114b4b021cdb474a3fb2813b3" }
|
||||
salsa = { git = "https://github.com/carljm/salsa.git", rev = "0f6d406f6c309964279baef71588746b8c67b4a3" }
|
||||
schemars = { version = "0.8.16" }
|
||||
seahash = { version = "4.1.0" }
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
@@ -162,8 +164,9 @@ tracing-log = { version = "0.2.0" }
|
||||
tracing-subscriber = { version = "0.3.18", default-features = false, features = [
|
||||
"env-filter",
|
||||
"fmt",
|
||||
"ansi",
|
||||
"smallvec"
|
||||
] }
|
||||
tracing-tree = { version = "0.4.0" }
|
||||
tryfn = { version = "0.2.1" }
|
||||
typed-arena = { version = "2.0.2" }
|
||||
unic-ucd-category = { version = "0.9" }
|
||||
@@ -176,7 +179,6 @@ uuid = { version = "1.6.1", features = [
|
||||
"v4",
|
||||
"fast-rng",
|
||||
"macro-diagnostics",
|
||||
"js",
|
||||
] }
|
||||
walkdir = { version = "2.3.2" }
|
||||
wasm-bindgen = { version = "0.2.92" }
|
||||
@@ -185,7 +187,7 @@ wild = { version = "2" }
|
||||
zip = { version = "0.6.6", default-features = false }
|
||||
|
||||
[workspace.metadata.cargo-shear]
|
||||
ignored = ["getrandom", "ruff_options_metadata"]
|
||||
ignored = ["getrandom", "ruff_options_metadata", "uuid"]
|
||||
|
||||
|
||||
[workspace.lints.rust]
|
||||
@@ -213,6 +215,7 @@ similar_names = "allow"
|
||||
single_match_else = "allow"
|
||||
too_many_lines = "allow"
|
||||
needless_continue = "allow" # An explicit continue can be more readable, especially if the alternative is an empty block.
|
||||
unnecessary_debug_formatting = "allow" # too many instances, the display also doesn't quote the path which is often desired in logs where we use them the most often.
|
||||
# Without the hashes we run into a `rustfmt` bug in some snapshot tests, see #13250
|
||||
needless_raw_string_hashes = "allow"
|
||||
# Disallowed restriction lints
|
||||
@@ -257,6 +260,9 @@ opt-level = 3
|
||||
[profile.dev.package.similar]
|
||||
opt-level = 3
|
||||
|
||||
[profile.dev.package.salsa]
|
||||
opt-level = 3
|
||||
|
||||
# Reduce complexity of a parser function that would trigger a locals limit in a wasm tool.
|
||||
# https://github.com/bytecodealliance/wasm-tools/blob/b5c3d98e40590512a3b12470ef358d5c7b983b15/crates/wasmparser/src/limits.rs#L29
|
||||
[profile.dev.package.ruff_python_parser]
|
||||
|
||||
@@ -34,8 +34,7 @@ An extremely fast Python linter and code formatter, written in Rust.
|
||||
- 🔧 Fix support, for automatic error correction (e.g., automatically remove unused imports)
|
||||
- 📏 Over [800 built-in rules](https://docs.astral.sh/ruff/rules/), with native re-implementations
|
||||
of popular Flake8 plugins, like flake8-bugbear
|
||||
- ⌨️ First-party [editor integrations](https://docs.astral.sh/ruff/integrations/) for
|
||||
[VS Code](https://github.com/astral-sh/ruff-vscode) and [more](https://docs.astral.sh/ruff/editors/setup)
|
||||
- ⌨️ First-party [editor integrations](https://docs.astral.sh/ruff/editors) for [VS Code](https://github.com/astral-sh/ruff-vscode) and [more](https://docs.astral.sh/ruff/editors/setup)
|
||||
- 🌎 Monorepo-friendly, with [hierarchical and cascading configuration](https://docs.astral.sh/ruff/configuration/#config-file-discovery)
|
||||
|
||||
Ruff aims to be orders of magnitude faster than alternative tools while integrating more
|
||||
@@ -149,8 +148,8 @@ curl -LsSf https://astral.sh/ruff/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/install.ps1 | iex"
|
||||
|
||||
# For a specific version.
|
||||
curl -LsSf https://astral.sh/ruff/0.11.9/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/0.11.9/install.ps1 | iex"
|
||||
curl -LsSf https://astral.sh/ruff/0.11.13/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/0.11.13/install.ps1 | iex"
|
||||
```
|
||||
|
||||
You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff),
|
||||
@@ -183,7 +182,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff
|
||||
```yaml
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.11.9
|
||||
rev: v0.11.13
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
|
||||
@@ -4,6 +4,10 @@ extend-exclude = [
|
||||
"crates/ty_vendored/vendor/**/*",
|
||||
"**/resources/**/*",
|
||||
"**/snapshots/**/*",
|
||||
# Completion tests tend to have a lot of incomplete
|
||||
# words naturally. It's annoying to have to make all
|
||||
# of them actually words. So just ignore typos here.
|
||||
"crates/ty_ide/src/completion.rs",
|
||||
]
|
||||
|
||||
[default.extend-words]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
doc-valid-idents = [
|
||||
"..",
|
||||
"CodeQL",
|
||||
"CPython",
|
||||
"FastAPI",
|
||||
"IPython",
|
||||
"LangChain",
|
||||
@@ -14,7 +15,7 @@ doc-valid-idents = [
|
||||
"SNMPv1",
|
||||
"SNMPv2",
|
||||
"SNMPv3",
|
||||
"PyFlakes"
|
||||
"PyFlakes",
|
||||
]
|
||||
|
||||
ignore-interior-mutability = [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.11.9"
|
||||
version = "0.11.13"
|
||||
publish = true
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
@@ -31,7 +31,7 @@ ruff_workspace = { workspace = true }
|
||||
|
||||
anyhow = { workspace = true }
|
||||
argfile = { workspace = true }
|
||||
bincode = { workspace = true }
|
||||
bincode = { workspace = true, features = ["serde"] }
|
||||
bitflags = { workspace = true }
|
||||
cachedir = { workspace = true }
|
||||
clap = { workspace = true, features = ["derive", "env", "wrap_help"] }
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::sync::Arc;
|
||||
use crate::commands::completions::config::{OptionString, OptionStringParser};
|
||||
use anyhow::bail;
|
||||
use clap::builder::{TypedValueParser, ValueParserFactory};
|
||||
use clap::{command, Parser, Subcommand};
|
||||
use clap::{Parser, Subcommand, command};
|
||||
use colored::Colorize;
|
||||
use itertools::Itertools;
|
||||
use path_absolutize::path_dedot;
|
||||
@@ -1126,10 +1126,10 @@ impl std::fmt::Display for FormatRangeParseError {
|
||||
write!(
|
||||
f,
|
||||
"the start position '{start_invalid}' is greater than the end position '{end_invalid}'.\n {tip} Try switching start and end: '{end}-{start}'",
|
||||
start_invalid=start.to_string().bold().yellow(),
|
||||
end_invalid=end.to_string().bold().yellow(),
|
||||
start=start.to_string().green().bold(),
|
||||
end=end.to_string().green().bold()
|
||||
start_invalid = start.to_string().bold().yellow(),
|
||||
end_invalid = end.to_string().bold().yellow(),
|
||||
start = start.to_string().green().bold(),
|
||||
end = end.to_string().green().bold()
|
||||
)
|
||||
}
|
||||
FormatRangeParseError::InvalidStart(inner) => inner.write(f, true),
|
||||
@@ -1230,30 +1230,36 @@ impl LineColumnParseError {
|
||||
|
||||
match self {
|
||||
LineColumnParseError::ColumnParseError(inner) => {
|
||||
write!(f, "the {range}s column is not a valid number ({inner})'\n {tip} The format is 'line:column'.")
|
||||
write!(
|
||||
f,
|
||||
"the {range}s column is not a valid number ({inner})'\n {tip} The format is 'line:column'."
|
||||
)
|
||||
}
|
||||
LineColumnParseError::LineParseError(inner) => {
|
||||
write!(f, "the {range} line is not a valid number ({inner})\n {tip} The format is 'line:column'.")
|
||||
write!(
|
||||
f,
|
||||
"the {range} line is not a valid number ({inner})\n {tip} The format is 'line:column'."
|
||||
)
|
||||
}
|
||||
LineColumnParseError::ZeroColumnIndex { line } => {
|
||||
write!(
|
||||
f,
|
||||
"the {range} column is 0, but it should be 1 or greater.\n {tip} The column numbers start at 1.\n {tip} Try {suggestion} instead.",
|
||||
suggestion=format!("{line}:1").green().bold()
|
||||
suggestion = format!("{line}:1").green().bold()
|
||||
)
|
||||
}
|
||||
LineColumnParseError::ZeroLineIndex { column } => {
|
||||
write!(
|
||||
f,
|
||||
"the {range} line is 0, but it should be 1 or greater.\n {tip} The line numbers start at 1.\n {tip} Try {suggestion} instead.",
|
||||
suggestion=format!("1:{column}").green().bold()
|
||||
suggestion = format!("1:{column}").green().bold()
|
||||
)
|
||||
}
|
||||
LineColumnParseError::ZeroLineAndColumnIndex => {
|
||||
write!(
|
||||
f,
|
||||
"the {range} line and column are both 0, but they should be 1 or greater.\n {tip} The line and column numbers start at 1.\n {tip} Try {suggestion} instead.",
|
||||
suggestion="1:1".to_string().green().bold()
|
||||
suggestion = "1:1".to_string().green().bold()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ use std::fs::{self, File};
|
||||
use std::hash::Hasher;
|
||||
use std::io::{self, BufReader, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::Mutex;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
@@ -13,21 +13,21 @@ use itertools::Itertools;
|
||||
use log::{debug, error};
|
||||
use rayon::iter::ParallelIterator;
|
||||
use rayon::iter::{IntoParallelIterator, ParallelBridge};
|
||||
use ruff_linter::codes::Rule;
|
||||
use rustc_hash::FxHashMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
use ruff_cache::{CacheKey, CacheKeyHasher};
|
||||
use ruff_diagnostics::{DiagnosticKind, Fix};
|
||||
use ruff_linter::message::{DiagnosticMessage, Message};
|
||||
use ruff_diagnostics::Fix;
|
||||
use ruff_linter::message::Message;
|
||||
use ruff_linter::package::PackageRoot;
|
||||
use ruff_linter::{warn_user, VERSION};
|
||||
use ruff_linter::{VERSION, warn_user};
|
||||
use ruff_macros::CacheKey;
|
||||
use ruff_notebook::NotebookIndex;
|
||||
use ruff_source_file::SourceFileBuilder;
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
use ruff_workspace::resolver::Resolver;
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
use ruff_workspace::Settings;
|
||||
use ruff_workspace::resolver::Resolver;
|
||||
|
||||
use crate::diagnostics::Diagnostics;
|
||||
|
||||
@@ -117,13 +117,14 @@ impl Cache {
|
||||
}
|
||||
};
|
||||
|
||||
let mut package: PackageCache = match bincode::deserialize_from(BufReader::new(file)) {
|
||||
Ok(package) => package,
|
||||
Err(err) => {
|
||||
warn_user!("Failed parse cache file `{}`: {err}", path.display());
|
||||
return Cache::empty(path, package_root);
|
||||
}
|
||||
};
|
||||
let mut package: PackageCache =
|
||||
match bincode::decode_from_reader(BufReader::new(file), bincode::config::standard()) {
|
||||
Ok(package) => package,
|
||||
Err(err) => {
|
||||
warn_user!("Failed parse cache file `{}`: {err}", path.display());
|
||||
return Cache::empty(path, package_root);
|
||||
}
|
||||
};
|
||||
|
||||
// Sanity check.
|
||||
if package.package_root != package_root {
|
||||
@@ -175,8 +176,8 @@ impl Cache {
|
||||
|
||||
// Serialize to in-memory buffer because hyperfine benchmark showed that it's faster than
|
||||
// using a `BufWriter` and our cache files are small enough that streaming isn't necessary.
|
||||
let serialized =
|
||||
bincode::serialize(&self.package).context("Failed to serialize cache data")?;
|
||||
let serialized = bincode::encode_to_vec(&self.package, bincode::config::standard())
|
||||
.context("Failed to serialize cache data")?;
|
||||
temp_file
|
||||
.write_all(&serialized)
|
||||
.context("Failed to write serialized cache to temporary file.")?;
|
||||
@@ -311,7 +312,7 @@ impl Cache {
|
||||
}
|
||||
|
||||
/// On disk representation of a cache of a package.
|
||||
#[derive(Deserialize, Debug, Serialize)]
|
||||
#[derive(bincode::Encode, Debug, bincode::Decode)]
|
||||
struct PackageCache {
|
||||
/// Path to the root of the package.
|
||||
///
|
||||
@@ -323,7 +324,7 @@ struct PackageCache {
|
||||
}
|
||||
|
||||
/// On disk representation of the cache per source file.
|
||||
#[derive(Deserialize, Debug, Serialize)]
|
||||
#[derive(bincode::Decode, Debug, bincode::Encode)]
|
||||
pub(crate) struct FileCache {
|
||||
/// Key that determines if the cached item is still valid.
|
||||
key: u64,
|
||||
@@ -347,14 +348,16 @@ impl FileCache {
|
||||
lint.messages
|
||||
.iter()
|
||||
.map(|msg| {
|
||||
Message::Diagnostic(DiagnosticMessage {
|
||||
kind: msg.kind.clone(),
|
||||
range: msg.range,
|
||||
fix: msg.fix.clone(),
|
||||
file: file.clone(),
|
||||
noqa_offset: msg.noqa_offset,
|
||||
parent: msg.parent,
|
||||
})
|
||||
Message::diagnostic(
|
||||
msg.body.clone(),
|
||||
msg.suggestion.clone(),
|
||||
msg.range,
|
||||
msg.fix.clone(),
|
||||
msg.parent,
|
||||
file.clone(),
|
||||
msg.noqa_offset,
|
||||
msg.rule,
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
@@ -368,7 +371,7 @@ impl FileCache {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize)]
|
||||
#[derive(Debug, Default, bincode::Decode, bincode::Encode)]
|
||||
struct FileCacheData {
|
||||
lint: Option<LintCacheData>,
|
||||
formatted: bool,
|
||||
@@ -406,7 +409,7 @@ pub(crate) fn init(path: &Path) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Serialize, PartialEq)]
|
||||
#[derive(bincode::Decode, Debug, bincode::Encode, PartialEq)]
|
||||
pub(crate) struct LintCacheData {
|
||||
/// Imports made.
|
||||
// pub(super) imports: ImportMap,
|
||||
@@ -419,6 +422,7 @@ pub(crate) struct LintCacheData {
|
||||
/// This will be empty if `messages` is empty.
|
||||
pub(super) source: String,
|
||||
/// Notebook index if this file is a Jupyter Notebook.
|
||||
#[bincode(with_serde)]
|
||||
pub(super) notebook_index: Option<NotebookIndex>,
|
||||
}
|
||||
|
||||
@@ -435,20 +439,25 @@ impl LintCacheData {
|
||||
|
||||
let messages = messages
|
||||
.iter()
|
||||
.filter_map(|message| message.as_diagnostic_message())
|
||||
.map(|msg| {
|
||||
// Parse the kebab-case rule name into a `Rule`. This will fail for syntax errors, so
|
||||
// this also serves to filter them out, but we shouldn't be caching files with syntax
|
||||
// errors anyway.
|
||||
.filter_map(|msg| Some((msg.name().parse().ok()?, msg)))
|
||||
.map(|(rule, msg)| {
|
||||
// Make sure that all message use the same source file.
|
||||
assert_eq!(
|
||||
msg.file,
|
||||
msg.source_file(),
|
||||
messages.first().unwrap().source_file(),
|
||||
"message uses a different source file"
|
||||
);
|
||||
CacheMessage {
|
||||
kind: msg.kind.clone(),
|
||||
range: msg.range,
|
||||
rule,
|
||||
body: msg.body().to_string(),
|
||||
suggestion: msg.suggestion().map(ToString::to_string),
|
||||
range: msg.range(),
|
||||
parent: msg.parent,
|
||||
fix: msg.fix.clone(),
|
||||
noqa_offset: msg.noqa_offset,
|
||||
fix: msg.fix().cloned(),
|
||||
noqa_offset: msg.noqa_offset(),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
@@ -462,14 +471,24 @@ impl LintCacheData {
|
||||
}
|
||||
|
||||
/// On disk representation of a diagnostic message.
|
||||
#[derive(Deserialize, Debug, Serialize, PartialEq)]
|
||||
#[derive(bincode::Decode, Debug, bincode::Encode, PartialEq)]
|
||||
pub(super) struct CacheMessage {
|
||||
kind: DiagnosticKind,
|
||||
/// The rule for the cached diagnostic.
|
||||
#[bincode(with_serde)]
|
||||
rule: Rule,
|
||||
/// The message body to display to the user, to explain the diagnostic.
|
||||
body: String,
|
||||
/// The message to display to the user, to explain the suggested fix.
|
||||
suggestion: Option<String>,
|
||||
/// Range into the message's [`FileCache::source`].
|
||||
#[bincode(with_serde)]
|
||||
range: TextRange,
|
||||
#[bincode(with_serde)]
|
||||
parent: Option<TextSize>,
|
||||
#[bincode(with_serde)]
|
||||
fix: Option<Fix>,
|
||||
noqa_offset: TextSize,
|
||||
#[bincode(with_serde)]
|
||||
noqa_offset: Option<TextSize>,
|
||||
}
|
||||
|
||||
pub(crate) trait PackageCaches {
|
||||
@@ -587,7 +606,7 @@ mod tests {
|
||||
use std::time::SystemTime;
|
||||
|
||||
use anyhow::Result;
|
||||
use filetime::{set_file_mtime, FileTime};
|
||||
use filetime::{FileTime, set_file_mtime};
|
||||
use itertools::Itertools;
|
||||
use ruff_linter::settings::LinterSettings;
|
||||
use test_case::test_case;
|
||||
@@ -602,8 +621,8 @@ mod tests {
|
||||
|
||||
use crate::cache::{self, FileCache, FileCacheData, FileCacheKey};
|
||||
use crate::cache::{Cache, RelativePathBuf};
|
||||
use crate::commands::format::{format_path, FormatCommandError, FormatMode, FormatResult};
|
||||
use crate::diagnostics::{lint_path, Diagnostics};
|
||||
use crate::commands::format::{FormatCommandError, FormatMode, FormatResult, format_path};
|
||||
use crate::diagnostics::{Diagnostics, lint_path};
|
||||
|
||||
#[test_case("../ruff_linter/resources/test/fixtures", "ruff_tests/cache_same_results_ruff_linter"; "ruff_linter_fixtures")]
|
||||
#[test_case("../ruff_notebook/resources/test/fixtures", "ruff_tests/cache_same_results_ruff_notebook"; "ruff_notebook_fixtures")]
|
||||
|
||||
@@ -11,7 +11,7 @@ use ruff_linter::source_kind::SourceKind;
|
||||
use ruff_linter::warn_user_once;
|
||||
use ruff_python_ast::{PySourceType, SourceType};
|
||||
use ruff_workspace::resolver::{
|
||||
match_exclusion, python_files_in_path, PyprojectConfig, ResolvedFile,
|
||||
PyprojectConfig, ResolvedFile, match_exclusion, python_files_in_path,
|
||||
};
|
||||
|
||||
use crate::args::ConfigArguments;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::args::{AnalyzeGraphArgs, ConfigArguments};
|
||||
use crate::resolve::resolve;
|
||||
use crate::{resolve_default_files, ExitStatus};
|
||||
use crate::{ExitStatus, resolve_default_files};
|
||||
use anyhow::Result;
|
||||
use log::{debug, warn};
|
||||
use path_absolutize::CWD;
|
||||
@@ -9,7 +9,7 @@ use ruff_graph::{Direction, ImportMap, ModuleDb, ModuleImports};
|
||||
use ruff_linter::package::PackageRoot;
|
||||
use ruff_linter::{warn_user, warn_user_once};
|
||||
use ruff_python_ast::{PySourceType, SourceType};
|
||||
use ruff_workspace::resolver::{match_exclusion, python_files_in_path, ResolvedFile};
|
||||
use ruff_workspace::resolver::{ResolvedFile, match_exclusion, python_files_in_path};
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::io::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
@@ -12,17 +12,17 @@ use rayon::prelude::*;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use ruff_db::panic::catch_unwind;
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_linter::OldDiagnostic;
|
||||
use ruff_linter::message::Message;
|
||||
use ruff_linter::package::PackageRoot;
|
||||
use ruff_linter::registry::Rule;
|
||||
use ruff_linter::settings::types::UnsafeFixes;
|
||||
use ruff_linter::settings::{flags, LinterSettings};
|
||||
use ruff_linter::{fs, warn_user_once, IOError};
|
||||
use ruff_linter::settings::{LinterSettings, flags};
|
||||
use ruff_linter::{IOError, fs, warn_user_once};
|
||||
use ruff_source_file::SourceFileBuilder;
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
use ruff_text_size::TextRange;
|
||||
use ruff_workspace::resolver::{
|
||||
match_exclusion, python_files_in_path, PyprojectConfig, ResolvedFile,
|
||||
PyprojectConfig, ResolvedFile, match_exclusion, python_files_in_path,
|
||||
};
|
||||
|
||||
use crate::args::ConfigArguments;
|
||||
@@ -131,9 +131,8 @@ pub(crate) fn check(
|
||||
|
||||
Diagnostics::new(
|
||||
vec![Message::from_diagnostic(
|
||||
Diagnostic::new(IOError { message }, TextRange::default()),
|
||||
dummy,
|
||||
TextSize::default(),
|
||||
OldDiagnostic::new(IOError { message }, TextRange::default(), &dummy),
|
||||
None,
|
||||
)],
|
||||
FxHashMap::default(),
|
||||
)
|
||||
@@ -228,9 +227,9 @@ mod test {
|
||||
use ruff_linter::message::{Emitter, EmitterContext, TextEmitter};
|
||||
use ruff_linter::registry::Rule;
|
||||
use ruff_linter::settings::types::UnsafeFixes;
|
||||
use ruff_linter::settings::{flags, LinterSettings};
|
||||
use ruff_workspace::resolver::{PyprojectConfig, PyprojectDiscoveryStrategy};
|
||||
use ruff_linter::settings::{LinterSettings, flags};
|
||||
use ruff_workspace::Settings;
|
||||
use ruff_workspace::resolver::{PyprojectConfig, PyprojectDiscoveryStrategy};
|
||||
|
||||
use crate::args::ConfigArguments;
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@ use anyhow::Result;
|
||||
use ruff_linter::package::PackageRoot;
|
||||
use ruff_linter::packaging;
|
||||
use ruff_linter::settings::flags;
|
||||
use ruff_workspace::resolver::{match_exclusion, python_file_at_path, PyprojectConfig, Resolver};
|
||||
use ruff_workspace::resolver::{PyprojectConfig, Resolver, match_exclusion, python_file_at_path};
|
||||
|
||||
use crate::args::ConfigArguments;
|
||||
use crate::diagnostics::{lint_stdin, Diagnostics};
|
||||
use crate::diagnostics::{Diagnostics, lint_stdin};
|
||||
use crate::stdin::{parrot_stdin, read_from_stdin};
|
||||
|
||||
/// Run the linter over a single file, read from `stdin`.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use anyhow::{Result, anyhow};
|
||||
|
||||
use crate::args::HelpFormat;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::{stderr, stdout, Write};
|
||||
use std::io::{Write, stderr, stdout};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Instant;
|
||||
|
||||
@@ -16,7 +16,7 @@ use rustc_hash::FxHashSet;
|
||||
use thiserror::Error;
|
||||
use tracing::debug;
|
||||
|
||||
use ruff_db::panic::{catch_unwind, PanicError};
|
||||
use ruff_db::panic::{PanicError, catch_unwind};
|
||||
use ruff_diagnostics::SourceMap;
|
||||
use ruff_linter::fs;
|
||||
use ruff_linter::logging::{DisplayParseError, LogLevel};
|
||||
@@ -26,16 +26,16 @@ use ruff_linter::rules::flake8_quotes::settings::Quote;
|
||||
use ruff_linter::source_kind::{SourceError, SourceKind};
|
||||
use ruff_linter::warn_user_once;
|
||||
use ruff_python_ast::{PySourceType, SourceType};
|
||||
use ruff_python_formatter::{format_module_source, format_range, FormatModuleError, QuoteStyle};
|
||||
use ruff_python_formatter::{FormatModuleError, QuoteStyle, format_module_source, format_range};
|
||||
use ruff_source_file::LineIndex;
|
||||
use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||
use ruff_workspace::resolver::{match_exclusion, python_files_in_path, ResolvedFile, Resolver};
|
||||
use ruff_workspace::FormatterSettings;
|
||||
use ruff_workspace::resolver::{ResolvedFile, Resolver, match_exclusion, python_files_in_path};
|
||||
|
||||
use crate::args::{ConfigArguments, FormatArguments, FormatRange};
|
||||
use crate::cache::{Cache, FileCacheKey, PackageCacheMap, PackageCaches};
|
||||
use crate::resolve::resolve;
|
||||
use crate::{resolve_default_files, ExitStatus};
|
||||
use crate::{ExitStatus, resolve_default_files};
|
||||
|
||||
#[derive(Debug, Copy, Clone, is_macro::Is)]
|
||||
pub(crate) enum FormatMode {
|
||||
@@ -821,9 +821,14 @@ pub(super) fn warn_incompatible_formatter_settings(resolver: &Resolver) {
|
||||
.collect();
|
||||
rule_names.sort();
|
||||
if let [rule] = rule_names.as_slice() {
|
||||
warn_user_once!("The following rule may cause conflicts when used with the formatter: {rule}. To avoid unexpected behavior, we recommend disabling this rule, either by removing it from the `select` or `extend-select` configuration, or adding it to the `ignore` configuration.");
|
||||
warn_user_once!(
|
||||
"The following rule may cause conflicts when used with the formatter: {rule}. To avoid unexpected behavior, we recommend disabling this rule, either by removing it from the `lint.select` or `lint.extend-select` configuration, or adding it to the `lint.ignore` configuration."
|
||||
);
|
||||
} else {
|
||||
warn_user_once!("The following rules may cause conflicts when used with the formatter: {}. To avoid unexpected behavior, we recommend disabling these rules, either by removing them from the `select` or `extend-select` configuration, or adding them to the `ignore` configuration.", rule_names.join(", "));
|
||||
warn_user_once!(
|
||||
"The following rules may cause conflicts when used with the formatter: {}. To avoid unexpected behavior, we recommend disabling these rules, either by removing them from the `lint.select` or `lint.extend-select` configuration, or adding them to the `lint.ignore` configuration.",
|
||||
rule_names.join(", ")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -833,7 +838,9 @@ pub(super) fn warn_incompatible_formatter_settings(resolver: &Resolver) {
|
||||
if setting.linter.rules.enabled(Rule::TabIndentation)
|
||||
&& setting.formatter.indent_style.is_tab()
|
||||
{
|
||||
warn_user_once!("The `format.indent-style=\"tab\"` option is incompatible with `W191`, which lints against all uses of tabs. We recommend disabling these rules when using the formatter, which enforces a consistent indentation style. Alternatively, set the `format.indent-style` option to `\"space\"`.");
|
||||
warn_user_once!(
|
||||
"The `format.indent-style=\"tab\"` option is incompatible with `W191`, which lints against all uses of tabs. We recommend disabling these rules when using the formatter, which enforces a consistent indentation style. Alternatively, set the `format.indent-style` option to `\"space\"`."
|
||||
);
|
||||
}
|
||||
|
||||
if !setting
|
||||
@@ -846,14 +853,18 @@ pub(super) fn warn_incompatible_formatter_settings(resolver: &Resolver) {
|
||||
.enabled(Rule::MultiLineImplicitStringConcatenation)
|
||||
&& !setting.linter.flake8_implicit_str_concat.allow_multiline
|
||||
{
|
||||
warn_user_once!("The `lint.flake8-implicit-str-concat.allow-multiline = false` option is incompatible with the formatter unless `ISC001` is enabled. We recommend enabling `ISC001` or setting `allow-multiline=true`.");
|
||||
warn_user_once!(
|
||||
"The `lint.flake8-implicit-str-concat.allow-multiline = false` option is incompatible with the formatter unless `ISC001` is enabled. We recommend enabling `ISC001` or setting `allow-multiline=true`."
|
||||
);
|
||||
}
|
||||
|
||||
// Validate all rules that rely on tab styles.
|
||||
if setting.linter.rules.enabled(Rule::DocstringTabIndentation)
|
||||
&& setting.formatter.indent_style.is_tab()
|
||||
{
|
||||
warn_user_once!("The `format.indent-style=\"tab\"` option is incompatible with `D206`, with requires space-based indentation. We recommend disabling these rules when using the formatter, which enforces a consistent indentation style. Alternatively, set the `format.indent-style` option to `\"space\"`.");
|
||||
warn_user_once!(
|
||||
"The `format.indent-style=\"tab\"` option is incompatible with `D206`, with requires space-based indentation. We recommend disabling these rules when using the formatter, which enforces a consistent indentation style. Alternatively, set the `format.indent-style` option to `\"space\"`."
|
||||
);
|
||||
}
|
||||
|
||||
// Validate all rules that rely on custom indent widths.
|
||||
@@ -862,7 +873,9 @@ pub(super) fn warn_incompatible_formatter_settings(resolver: &Resolver) {
|
||||
Rule::IndentationWithInvalidMultipleComment,
|
||||
]) && setting.formatter.indent_width.value() != 4
|
||||
{
|
||||
warn_user_once!("The `format.indent-width` option with a value other than 4 is incompatible with `E111` and `E114`. We recommend disabling these rules when using the formatter, which enforces a consistent indentation width. Alternatively, set the `format.indent-width` option to `4`.");
|
||||
warn_user_once!(
|
||||
"The `format.indent-width` option with a value other than 4 is incompatible with `E111` and `E114`. We recommend disabling these rules when using the formatter, which enforces a consistent indentation width. Alternatively, set the `format.indent-width` option to `4`."
|
||||
);
|
||||
}
|
||||
|
||||
// Validate all rules that rely on quote styles.
|
||||
@@ -876,10 +889,14 @@ pub(super) fn warn_incompatible_formatter_settings(resolver: &Resolver) {
|
||||
setting.formatter.quote_style,
|
||||
) {
|
||||
(Quote::Double, QuoteStyle::Single) => {
|
||||
warn_user_once!("The `flake8-quotes.inline-quotes=\"double\"` option is incompatible with the formatter's `format.quote-style=\"single\"`. We recommend disabling `Q000` and `Q003` when using the formatter, which enforces a consistent quote style. Alternatively, set both options to either `\"single\"` or `\"double\"`.");
|
||||
warn_user_once!(
|
||||
"The `flake8-quotes.inline-quotes=\"double\"` option is incompatible with the formatter's `format.quote-style=\"single\"`. We recommend disabling `Q000` and `Q003` when using the formatter, which enforces a consistent quote style. Alternatively, set both options to either `\"single\"` or `\"double\"`."
|
||||
);
|
||||
}
|
||||
(Quote::Single, QuoteStyle::Double) => {
|
||||
warn_user_once!("The `flake8-quotes.inline-quotes=\"single\"` option is incompatible with the formatter's `format.quote-style=\"double\"`. We recommend disabling `Q000` and `Q003` when using the formatter, which enforces a consistent quote style. Alternatively, set both options to either `\"single\"` or `\"double\"`.");
|
||||
warn_user_once!(
|
||||
"The `flake8-quotes.inline-quotes=\"single\"` option is incompatible with the formatter's `format.quote-style=\"double\"`. We recommend disabling `Q000` and `Q003` when using the formatter, which enforces a consistent quote style. Alternatively, set both options to either `\"single\"` or `\"double\"`."
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -892,7 +909,9 @@ pub(super) fn warn_incompatible_formatter_settings(resolver: &Resolver) {
|
||||
QuoteStyle::Single | QuoteStyle::Double
|
||||
)
|
||||
{
|
||||
warn_user_once!("The `flake8-quotes.multiline-quotes=\"single\"` option is incompatible with the formatter. We recommend disabling `Q001` when using the formatter, which enforces double quotes for multiline strings. Alternatively, set the `flake8-quotes.multiline-quotes` option to `\"double\"`.`");
|
||||
warn_user_once!(
|
||||
"The `flake8-quotes.multiline-quotes=\"single\"` option is incompatible with the formatter. We recommend disabling `Q001` when using the formatter, which enforces double quotes for multiline strings. Alternatively, set the `flake8-quotes.multiline-quotes` option to `\"double\"`.`"
|
||||
);
|
||||
}
|
||||
|
||||
if setting.linter.rules.enabled(Rule::BadQuotesDocstring)
|
||||
@@ -902,7 +921,9 @@ pub(super) fn warn_incompatible_formatter_settings(resolver: &Resolver) {
|
||||
QuoteStyle::Single | QuoteStyle::Double
|
||||
)
|
||||
{
|
||||
warn_user_once!("The `flake8-quotes.docstring-quotes=\"single\"` option is incompatible with the formatter. We recommend disabling `Q002` when using the formatter, which enforces double quotes for docstrings. Alternatively, set the `flake8-quotes.docstring-quotes` option to `\"double\"`.`");
|
||||
warn_user_once!(
|
||||
"The `flake8-quotes.docstring-quotes=\"single\"` option is incompatible with the formatter. We recommend disabling `Q002` when using the formatter, which enforces double quotes for docstrings. Alternatively, set the `flake8-quotes.docstring-quotes` option to `\"double\"`.`"
|
||||
);
|
||||
}
|
||||
|
||||
// Validate all isort settings.
|
||||
@@ -910,12 +931,16 @@ pub(super) fn warn_incompatible_formatter_settings(resolver: &Resolver) {
|
||||
// The formatter removes empty lines if the value is larger than 2 but always inserts a empty line after imports.
|
||||
// Two empty lines are okay because `isort` only uses this setting for top-level imports (not in nested blocks).
|
||||
if !matches!(setting.linter.isort.lines_after_imports, 1 | 2 | -1) {
|
||||
warn_user_once!("The isort option `isort.lines-after-imports` with a value other than `-1`, `1` or `2` is incompatible with the formatter. To avoid unexpected behavior, we recommend setting the option to one of: `2`, `1`, or `-1` (default).");
|
||||
warn_user_once!(
|
||||
"The isort option `isort.lines-after-imports` with a value other than `-1`, `1` or `2` is incompatible with the formatter. To avoid unexpected behavior, we recommend setting the option to one of: `2`, `1`, or `-1` (default)."
|
||||
);
|
||||
}
|
||||
|
||||
// Values larger than two get reduced to one line by the formatter if the import is in a nested block.
|
||||
if setting.linter.isort.lines_between_types > 1 {
|
||||
warn_user_once!("The isort option `isort.lines-between-types` with a value greater than 1 is incompatible with the formatter. To avoid unexpected behavior, we recommend setting the option to one of: `1` or `0` (default).");
|
||||
warn_user_once!(
|
||||
"The isort option `isort.lines-between-types` with a value greater than 1 is incompatible with the formatter. To avoid unexpected behavior, we recommend setting the option to one of: `1` or `0` (default)."
|
||||
);
|
||||
}
|
||||
|
||||
// isort inserts a trailing comma which the formatter preserves, but only if `skip-magic-trailing-comma` isn't false.
|
||||
@@ -924,11 +949,15 @@ pub(super) fn warn_incompatible_formatter_settings(resolver: &Resolver) {
|
||||
&& !setting.linter.isort.force_single_line
|
||||
{
|
||||
if setting.linter.isort.force_wrap_aliases {
|
||||
warn_user_once!("The isort option `isort.force-wrap-aliases` is incompatible with the formatter `format.skip-magic-trailing-comma=true` option. To avoid unexpected behavior, we recommend either setting `isort.force-wrap-aliases=false` or `format.skip-magic-trailing-comma=false`.");
|
||||
warn_user_once!(
|
||||
"The isort option `isort.force-wrap-aliases` is incompatible with the formatter `format.skip-magic-trailing-comma=true` option. To avoid unexpected behavior, we recommend either setting `isort.force-wrap-aliases=false` or `format.skip-magic-trailing-comma=false`."
|
||||
);
|
||||
}
|
||||
|
||||
if setting.linter.isort.split_on_trailing_comma {
|
||||
warn_user_once!("The isort option `isort.split-on-trailing-comma` is incompatible with the formatter `format.skip-magic-trailing-comma=true` option. To avoid unexpected behavior, we recommend either setting `isort.split-on-trailing-comma=false` or `format.skip-magic-trailing-comma=false`.");
|
||||
warn_user_once!(
|
||||
"The isort option `isort.split-on-trailing-comma` is incompatible with the formatter `format.skip-magic-trailing-comma=true` option. To avoid unexpected behavior, we recommend either setting `isort.split-on-trailing-comma=false` or `format.skip-magic-trailing-comma=false`."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,17 +6,17 @@ use log::error;
|
||||
|
||||
use ruff_linter::source_kind::SourceKind;
|
||||
use ruff_python_ast::{PySourceType, SourceType};
|
||||
use ruff_workspace::resolver::{match_exclusion, python_file_at_path, Resolver};
|
||||
use ruff_workspace::FormatterSettings;
|
||||
use ruff_workspace::resolver::{Resolver, match_exclusion, python_file_at_path};
|
||||
|
||||
use crate::ExitStatus;
|
||||
use crate::args::{ConfigArguments, FormatArguments, FormatRange};
|
||||
use crate::commands::format::{
|
||||
format_source, warn_incompatible_formatter_settings, FormatCommandError, FormatMode,
|
||||
FormatResult, FormattedSource,
|
||||
FormatCommandError, FormatMode, FormatResult, FormattedSource, format_source,
|
||||
warn_incompatible_formatter_settings,
|
||||
};
|
||||
use crate::resolve::resolve;
|
||||
use crate::stdin::{parrot_stdin, read_from_stdin};
|
||||
use crate::ExitStatus;
|
||||
|
||||
/// Run the formatter over a single file, read from `stdin`.
|
||||
pub(crate) fn format_stdin(
|
||||
|
||||
@@ -6,7 +6,7 @@ use serde::ser::SerializeSeq;
|
||||
use serde::{Serialize, Serializer};
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use ruff_diagnostics::FixAvailability;
|
||||
use ruff_linter::FixAvailability;
|
||||
use ruff_linter::registry::{Linter, Rule, RuleNamespace};
|
||||
|
||||
use crate::args::HelpFormat;
|
||||
@@ -30,7 +30,7 @@ impl<'a> Explanation<'a> {
|
||||
let (linter, _) = Linter::parse_code(&code).unwrap();
|
||||
let fix = rule.fixable().to_string();
|
||||
Self {
|
||||
name: rule.as_ref(),
|
||||
name: rule.name().as_str(),
|
||||
code,
|
||||
linter: linter.name(),
|
||||
summary: rule.message_formats()[0],
|
||||
@@ -44,7 +44,7 @@ impl<'a> Explanation<'a> {
|
||||
|
||||
fn format_rule_text(rule: Rule) -> String {
|
||||
let mut output = String::new();
|
||||
let _ = write!(&mut output, "# {} ({})", rule.as_ref(), rule.noqa_code());
|
||||
let _ = write!(&mut output, "# {} ({})", rule.name(), rule.noqa_code());
|
||||
output.push('\n');
|
||||
output.push('\n');
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ use anyhow::Result;
|
||||
use itertools::Itertools;
|
||||
|
||||
use ruff_linter::warn_user_once;
|
||||
use ruff_workspace::resolver::{python_files_in_path, PyprojectConfig, ResolvedFile};
|
||||
use ruff_workspace::resolver::{PyprojectConfig, ResolvedFile, python_files_in_path};
|
||||
|
||||
use crate::args::ConfigArguments;
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use anyhow::{Result, bail};
|
||||
use itertools::Itertools;
|
||||
|
||||
use ruff_workspace::resolver::{python_files_in_path, PyprojectConfig, ResolvedFile};
|
||||
use ruff_workspace::resolver::{PyprojectConfig, ResolvedFile, python_files_in_path};
|
||||
|
||||
use crate::args::ConfigArguments;
|
||||
|
||||
|
||||
@@ -12,20 +12,20 @@ use colored::Colorize;
|
||||
use log::{debug, warn};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_linter::OldDiagnostic;
|
||||
use ruff_linter::codes::Rule;
|
||||
use ruff_linter::linter::{lint_fix, lint_only, FixTable, FixerResult, LinterResult, ParseSource};
|
||||
use ruff_linter::linter::{FixTable, FixerResult, LinterResult, ParseSource, lint_fix, lint_only};
|
||||
use ruff_linter::message::Message;
|
||||
use ruff_linter::package::PackageRoot;
|
||||
use ruff_linter::pyproject_toml::lint_pyproject_toml;
|
||||
use ruff_linter::settings::types::UnsafeFixes;
|
||||
use ruff_linter::settings::{flags, LinterSettings};
|
||||
use ruff_linter::settings::{LinterSettings, flags};
|
||||
use ruff_linter::source_kind::{SourceError, SourceKind};
|
||||
use ruff_linter::{fs, IOError};
|
||||
use ruff_linter::{IOError, fs};
|
||||
use ruff_notebook::{Notebook, NotebookError, NotebookIndex};
|
||||
use ruff_python_ast::{PySourceType, SourceType, TomlSourceType};
|
||||
use ruff_source_file::SourceFileBuilder;
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
use ruff_text_size::TextRange;
|
||||
use ruff_workspace::Settings;
|
||||
|
||||
use crate::cache::{Cache, FileCacheKey, LintCacheData};
|
||||
@@ -64,14 +64,14 @@ impl Diagnostics {
|
||||
let source_file = SourceFileBuilder::new(name, "").finish();
|
||||
Self::new(
|
||||
vec![Message::from_diagnostic(
|
||||
Diagnostic::new(
|
||||
OldDiagnostic::new(
|
||||
IOError {
|
||||
message: err.to_string(),
|
||||
},
|
||||
TextRange::default(),
|
||||
&source_file,
|
||||
),
|
||||
source_file,
|
||||
TextSize::default(),
|
||||
None,
|
||||
)],
|
||||
FxHashMap::default(),
|
||||
)
|
||||
@@ -165,9 +165,9 @@ impl AddAssign for FixMap {
|
||||
continue;
|
||||
}
|
||||
let fixed_in_file = self.0.entry(filename).or_default();
|
||||
for (rule, count) in fixed {
|
||||
for (rule, name, count) in fixed.iter() {
|
||||
if count > 0 {
|
||||
*fixed_in_file.entry(rule).or_default() += count;
|
||||
*fixed_in_file.entry(rule).or_default(name) += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -235,7 +235,7 @@ pub(crate) fn lint_path(
|
||||
};
|
||||
let source_file =
|
||||
SourceFileBuilder::new(path.to_string_lossy(), contents).finish();
|
||||
lint_pyproject_toml(source_file, settings)
|
||||
lint_pyproject_toml(&source_file, settings)
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
@@ -305,7 +305,7 @@ pub(crate) fn lint_path(
|
||||
ParseSource::None,
|
||||
);
|
||||
let transformed = source_kind;
|
||||
let fixed = FxHashMap::default();
|
||||
let fixed = FixTable::default();
|
||||
(result, transformed, fixed)
|
||||
}
|
||||
} else {
|
||||
@@ -319,7 +319,7 @@ pub(crate) fn lint_path(
|
||||
ParseSource::None,
|
||||
);
|
||||
let transformed = source_kind;
|
||||
let fixed = FxHashMap::default();
|
||||
let fixed = FixTable::default();
|
||||
(result, transformed, fixed)
|
||||
};
|
||||
|
||||
@@ -396,7 +396,7 @@ pub(crate) fn lint_stdin(
|
||||
}
|
||||
|
||||
return Ok(Diagnostics {
|
||||
messages: lint_pyproject_toml(source_file, &settings.linter),
|
||||
messages: lint_pyproject_toml(&source_file, &settings.linter),
|
||||
fixed: FixMap::from_iter([(fs::relativize_path(path), FixTable::default())]),
|
||||
notebook_indexes: FxHashMap::default(),
|
||||
});
|
||||
@@ -473,7 +473,7 @@ pub(crate) fn lint_stdin(
|
||||
}
|
||||
|
||||
let transformed = source_kind;
|
||||
let fixed = FxHashMap::default();
|
||||
let fixed = FixTable::default();
|
||||
(result, transformed, fixed)
|
||||
}
|
||||
} else {
|
||||
@@ -487,7 +487,7 @@ pub(crate) fn lint_stdin(
|
||||
ParseSource::None,
|
||||
);
|
||||
let transformed = source_kind;
|
||||
let fixed = FxHashMap::default();
|
||||
let fixed = FixTable::default();
|
||||
(result, transformed, fixed)
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#![allow(clippy::print_stdout)]
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{self, stdout, BufWriter, Write};
|
||||
use std::io::{self, BufWriter, Write, stdout};
|
||||
use std::num::NonZeroUsize;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::ExitCode;
|
||||
@@ -11,10 +11,10 @@ use anyhow::Result;
|
||||
use clap::CommandFactory;
|
||||
use colored::Colorize;
|
||||
use log::warn;
|
||||
use notify::{recommended_watcher, RecursiveMode, Watcher};
|
||||
use notify::{RecursiveMode, Watcher, recommended_watcher};
|
||||
|
||||
use args::{GlobalConfigArgs, ServerCommand};
|
||||
use ruff_linter::logging::{set_up_logging, LogLevel};
|
||||
use ruff_linter::logging::{LogLevel, set_up_logging};
|
||||
use ruff_linter::settings::flags::FixMode;
|
||||
use ruff_linter::settings::types::OutputFormat;
|
||||
use ruff_linter::{fs, warn_user, warn_user_once};
|
||||
@@ -488,7 +488,7 @@ pub fn check(args: CheckCommand, global_options: GlobalConfigArgs) -> Result<Exi
|
||||
mod test_file_change_detector {
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::{change_detected, ChangeKind};
|
||||
use crate::{ChangeKind, change_detected};
|
||||
|
||||
#[test]
|
||||
fn detect_correct_file_change() {
|
||||
|
||||
@@ -5,7 +5,7 @@ use clap::Parser;
|
||||
use colored::Colorize;
|
||||
|
||||
use ruff::args::Args;
|
||||
use ruff::{run, ExitStatus};
|
||||
use ruff::{ExitStatus, run};
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[global_allocator]
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
use std::cmp::Reverse;
|
||||
use std::fmt::Display;
|
||||
use std::hash::Hash;
|
||||
use std::io::Write;
|
||||
|
||||
use anyhow::Result;
|
||||
use bitflags::bitflags;
|
||||
use colored::Colorize;
|
||||
use itertools::{iterate, Itertools};
|
||||
use itertools::{Itertools, iterate};
|
||||
use ruff_linter::codes::NoqaCode;
|
||||
use ruff_linter::linter::FixTable;
|
||||
use serde::Serialize;
|
||||
|
||||
use ruff_linter::fs::relativize_path;
|
||||
use ruff_linter::logging::LogLevel;
|
||||
use ruff_linter::message::{
|
||||
AzureEmitter, Emitter, EmitterContext, GithubEmitter, GitlabEmitter, GroupedEmitter,
|
||||
JsonEmitter, JsonLinesEmitter, JunitEmitter, Message, MessageKind, PylintEmitter,
|
||||
RdjsonEmitter, SarifEmitter, TextEmitter,
|
||||
JsonEmitter, JsonLinesEmitter, JunitEmitter, Message, PylintEmitter, RdjsonEmitter,
|
||||
SarifEmitter, TextEmitter,
|
||||
};
|
||||
use ruff_linter::notify_user;
|
||||
use ruff_linter::registry::Rule;
|
||||
use ruff_linter::settings::flags::{self};
|
||||
use ruff_linter::settings::types::{OutputFormat, UnsafeFixes};
|
||||
|
||||
@@ -37,59 +37,12 @@ bitflags! {
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ExpandedStatistics {
|
||||
code: Option<SerializeRuleAsCode>,
|
||||
name: SerializeMessageKindAsTitle,
|
||||
code: Option<NoqaCode>,
|
||||
name: &'static str,
|
||||
count: usize,
|
||||
fixable: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct SerializeRuleAsCode(Rule);
|
||||
|
||||
impl Serialize for SerializeRuleAsCode {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_str(&self.0.noqa_code().to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for SerializeRuleAsCode {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0.noqa_code())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Rule> for SerializeRuleAsCode {
|
||||
fn from(rule: Rule) -> Self {
|
||||
Self(rule)
|
||||
}
|
||||
}
|
||||
|
||||
struct SerializeMessageKindAsTitle(MessageKind);
|
||||
|
||||
impl Serialize for SerializeMessageKindAsTitle {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_str(self.0.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for SerializeMessageKindAsTitle {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(self.0.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MessageKind> for SerializeMessageKindAsTitle {
|
||||
fn from(kind: MessageKind) -> Self {
|
||||
Self(kind)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Printer {
|
||||
format: OutputFormat,
|
||||
log_level: LogLevel,
|
||||
@@ -128,7 +81,7 @@ impl Printer {
|
||||
let fixed = diagnostics
|
||||
.fixed
|
||||
.values()
|
||||
.flat_map(std::collections::HashMap::values)
|
||||
.flat_map(FixTable::counts)
|
||||
.sum::<usize>();
|
||||
|
||||
if self.flags.intersects(Flags::SHOW_VIOLATIONS) {
|
||||
@@ -157,7 +110,8 @@ impl Printer {
|
||||
} else {
|
||||
"es"
|
||||
};
|
||||
writeln!(writer,
|
||||
writeln!(
|
||||
writer,
|
||||
"{fix_prefix} {} fixable with the `--fix` option ({} hidden fix{es} can be enabled with the `--unsafe-fixes` option).",
|
||||
fixables.applicable, fixables.inapplicable_unsafe
|
||||
)?;
|
||||
@@ -175,7 +129,8 @@ impl Printer {
|
||||
} else {
|
||||
"es"
|
||||
};
|
||||
writeln!(writer,
|
||||
writeln!(
|
||||
writer,
|
||||
"No fixes available ({} hidden fix{es} can be enabled with the `--unsafe-fixes` option).",
|
||||
fixables.inapplicable_unsafe
|
||||
)?;
|
||||
@@ -205,15 +160,27 @@ impl Printer {
|
||||
if fixed > 0 {
|
||||
let s = if fixed == 1 { "" } else { "s" };
|
||||
if self.fix_mode.is_apply() {
|
||||
writeln!(writer, "Fixed {fixed} error{s} ({unapplied} additional fix{es} available with `--unsafe-fixes`).")?;
|
||||
writeln!(
|
||||
writer,
|
||||
"Fixed {fixed} error{s} ({unapplied} additional fix{es} available with `--unsafe-fixes`)."
|
||||
)?;
|
||||
} else {
|
||||
writeln!(writer, "Would fix {fixed} error{s} ({unapplied} additional fix{es} available with `--unsafe-fixes`).")?;
|
||||
writeln!(
|
||||
writer,
|
||||
"Would fix {fixed} error{s} ({unapplied} additional fix{es} available with `--unsafe-fixes`)."
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
if self.fix_mode.is_apply() {
|
||||
writeln!(writer, "No errors fixed ({unapplied} fix{es} available with `--unsafe-fixes`).")?;
|
||||
writeln!(
|
||||
writer,
|
||||
"No errors fixed ({unapplied} fix{es} available with `--unsafe-fixes`)."
|
||||
)?;
|
||||
} else {
|
||||
writeln!(writer, "No errors would be fixed ({unapplied} fix{es} available with `--unsafe-fixes`).")?;
|
||||
writeln!(
|
||||
writer,
|
||||
"No errors would be fixed ({unapplied} fix{es} available with `--unsafe-fixes`)."
|
||||
)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -336,21 +303,25 @@ impl Printer {
|
||||
let statistics: Vec<ExpandedStatistics> = diagnostics
|
||||
.messages
|
||||
.iter()
|
||||
.sorted_by_key(|message| (message.rule(), message.fixable()))
|
||||
.fold(vec![], |mut acc: Vec<(&Message, usize)>, message| {
|
||||
if let Some((prev_message, count)) = acc.last_mut() {
|
||||
if prev_message.rule() == message.rule() {
|
||||
*count += 1;
|
||||
return acc;
|
||||
.map(|message| (message.noqa_code(), message))
|
||||
.sorted_by_key(|(code, message)| (*code, message.fixable()))
|
||||
.fold(
|
||||
vec![],
|
||||
|mut acc: Vec<((Option<NoqaCode>, &Message), usize)>, (code, message)| {
|
||||
if let Some(((prev_code, _prev_message), count)) = acc.last_mut() {
|
||||
if *prev_code == code {
|
||||
*count += 1;
|
||||
return acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
acc.push((message, 1));
|
||||
acc
|
||||
})
|
||||
acc.push(((code, message), 1));
|
||||
acc
|
||||
},
|
||||
)
|
||||
.iter()
|
||||
.map(|&(message, count)| ExpandedStatistics {
|
||||
code: message.rule().map(std::convert::Into::into),
|
||||
name: message.kind().into(),
|
||||
.map(|&((code, message), count)| ExpandedStatistics {
|
||||
code,
|
||||
name: message.name(),
|
||||
count,
|
||||
fixable: if let Some(fix) = message.fix() {
|
||||
fix.applies(self.unsafe_fixes.required_applicability())
|
||||
@@ -502,13 +473,13 @@ fn show_fix_status(fix_mode: flags::FixMode, fixables: Option<&FixableStatistics
|
||||
fn print_fix_summary(writer: &mut dyn Write, fixed: &FixMap) -> Result<()> {
|
||||
let total = fixed
|
||||
.values()
|
||||
.map(|table| table.values().sum::<usize>())
|
||||
.map(|table| table.counts().sum::<usize>())
|
||||
.sum::<usize>();
|
||||
assert!(total > 0);
|
||||
let num_digits = num_digits(
|
||||
*fixed
|
||||
fixed
|
||||
.values()
|
||||
.filter_map(|table| table.values().max())
|
||||
.filter_map(|table| table.counts().max())
|
||||
.max()
|
||||
.unwrap(),
|
||||
);
|
||||
@@ -528,12 +499,11 @@ fn print_fix_summary(writer: &mut dyn Write, fixed: &FixMap) -> Result<()> {
|
||||
relativize_path(filename).bold(),
|
||||
":".cyan()
|
||||
)?;
|
||||
for (rule, count) in table.iter().sorted_by_key(|(.., count)| Reverse(*count)) {
|
||||
for (code, name, count) in table.iter().sorted_by_key(|(.., count)| Reverse(*count)) {
|
||||
writeln!(
|
||||
writer,
|
||||
" {count:>num_digits$} × {} ({})",
|
||||
rule.noqa_code().to_string().red().bold(),
|
||||
rule.as_ref(),
|
||||
" {count:>num_digits$} × {code} ({name})",
|
||||
code = code.to_string().red().bold(),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use anyhow::{Result, bail};
|
||||
use log::debug;
|
||||
use path_absolutize::path_dedot;
|
||||
|
||||
use ruff_workspace::configuration::Configuration;
|
||||
use ruff_workspace::pyproject::{self, find_fallback_target_version};
|
||||
use ruff_workspace::resolver::{
|
||||
resolve_root_settings, ConfigurationOrigin, ConfigurationTransformer, PyprojectConfig,
|
||||
PyprojectDiscoveryStrategy,
|
||||
ConfigurationOrigin, ConfigurationTransformer, PyprojectConfig, PyprojectDiscoveryStrategy,
|
||||
resolve_root_settings,
|
||||
};
|
||||
|
||||
use ruff_python_ast as ast;
|
||||
|
||||
@@ -566,7 +566,7 @@ fn venv() -> Result<()> {
|
||||
----- stderr -----
|
||||
ruff failed
|
||||
Cause: Invalid search path settings
|
||||
Cause: Failed to discover the site-packages directory: Invalid `--python` argument: `none` could not be canonicalized
|
||||
Cause: Failed to discover the site-packages directory: Invalid `--python` argument `none`: does not point to a Python executable or a directory on disk
|
||||
");
|
||||
});
|
||||
|
||||
|
||||
@@ -862,7 +862,7 @@ if condition:
|
||||
print('Should change quotes')
|
||||
|
||||
----- stderr -----
|
||||
warning: The following rule may cause conflicts when used with the formatter: `COM812`. To avoid unexpected behavior, we recommend disabling this rule, either by removing it from the `select` or `extend-select` configuration, or adding it to the `ignore` configuration.
|
||||
warning: The following rule may cause conflicts when used with the formatter: `COM812`. To avoid unexpected behavior, we recommend disabling this rule, either by removing it from the `lint.select` or `lint.extend-select` configuration, or adding it to the `lint.ignore` configuration.
|
||||
"#);
|
||||
Ok(())
|
||||
}
|
||||
@@ -999,7 +999,7 @@ def say_hy(name: str):
|
||||
1 file reformatted
|
||||
|
||||
----- stderr -----
|
||||
warning: The following rule may cause conflicts when used with the formatter: `COM812`. To avoid unexpected behavior, we recommend disabling this rule, either by removing it from the `select` or `extend-select` configuration, or adding it to the `ignore` configuration.
|
||||
warning: The following rule may cause conflicts when used with the formatter: `COM812`. To avoid unexpected behavior, we recommend disabling this rule, either by removing it from the `lint.select` or `lint.extend-select` configuration, or adding it to the `lint.ignore` configuration.
|
||||
warning: The `format.indent-style="tab"` option is incompatible with `W191`, which lints against all uses of tabs. We recommend disabling these rules when using the formatter, which enforces a consistent indentation style. Alternatively, set the `format.indent-style` option to `"space"`.
|
||||
warning: The `lint.flake8-implicit-str-concat.allow-multiline = false` option is incompatible with the formatter unless `ISC001` is enabled. We recommend enabling `ISC001` or setting `allow-multiline=true`.
|
||||
warning: The `format.indent-style="tab"` option is incompatible with `D206`, with requires space-based indentation. We recommend disabling these rules when using the formatter, which enforces a consistent indentation style. Alternatively, set the `format.indent-style` option to `"space"`.
|
||||
@@ -1059,7 +1059,7 @@ def say_hy(name: str):
|
||||
print(f"Hy {name}")
|
||||
|
||||
----- stderr -----
|
||||
warning: The following rule may cause conflicts when used with the formatter: `COM812`. To avoid unexpected behavior, we recommend disabling this rule, either by removing it from the `select` or `extend-select` configuration, or adding it to the `ignore` configuration.
|
||||
warning: The following rule may cause conflicts when used with the formatter: `COM812`. To avoid unexpected behavior, we recommend disabling this rule, either by removing it from the `lint.select` or `lint.extend-select` configuration, or adding it to the `lint.ignore` configuration.
|
||||
warning: The `format.indent-style="tab"` option is incompatible with `W191`, which lints against all uses of tabs. We recommend disabling these rules when using the formatter, which enforces a consistent indentation style. Alternatively, set the `format.indent-style` option to `"space"`.
|
||||
warning: The `format.indent-style="tab"` option is incompatible with `D206`, with requires space-based indentation. We recommend disabling these rules when using the formatter, which enforces a consistent indentation style. Alternatively, set the `format.indent-style` option to `"space"`.
|
||||
warning: The `flake8-quotes.inline-quotes="single"` option is incompatible with the formatter's `format.quote-style="double"`. We recommend disabling `Q000` and `Q003` when using the formatter, which enforces a consistent quote style. Alternatively, set both options to either `"single"` or `"double"`.
|
||||
@@ -1199,7 +1199,7 @@ def say_hy(name: str):
|
||||
----- stderr -----
|
||||
warning: `incorrect-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are incompatible. Ignoring `incorrect-blank-line-before-class`.
|
||||
warning: `multi-line-summary-first-line` (D212) and `multi-line-summary-second-line` (D213) are incompatible. Ignoring `multi-line-summary-second-line`.
|
||||
warning: The following rule may cause conflicts when used with the formatter: `COM812`. To avoid unexpected behavior, we recommend disabling this rule, either by removing it from the `select` or `extend-select` configuration, or adding it to the `ignore` configuration.
|
||||
warning: The following rule may cause conflicts when used with the formatter: `COM812`. To avoid unexpected behavior, we recommend disabling this rule, either by removing it from the `lint.select` or `lint.extend-select` configuration, or adding it to the `lint.ignore` configuration.
|
||||
");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1157,18 +1157,20 @@ include = ["*.ipy"]
|
||||
|
||||
#[test]
|
||||
fn warn_invalid_noqa_with_no_diagnostics() {
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.args(["--isolated"])
|
||||
.arg("--select")
|
||||
.arg("F401")
|
||||
.arg("-")
|
||||
.pass_stdin(
|
||||
r#"
|
||||
assert_cmd_snapshot!(
|
||||
Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.args(["--isolated"])
|
||||
.arg("--select")
|
||||
.arg("F401")
|
||||
.arg("-")
|
||||
.pass_stdin(
|
||||
r#"
|
||||
# ruff: noqa: AAA101
|
||||
print("Hello world!")
|
||||
"#
|
||||
));
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -4997,30 +4999,34 @@ fn flake8_import_convention_invalid_aliases_config_module_name() -> Result<()> {
|
||||
|
||||
#[test]
|
||||
fn flake8_import_convention_unused_aliased_import() {
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.arg("--config")
|
||||
.arg(r#"lint.isort.required-imports = ["import pandas"]"#)
|
||||
.args(["--select", "I002,ICN001,F401"])
|
||||
.args(["--stdin-filename", "test.py"])
|
||||
.arg("--unsafe-fixes")
|
||||
.arg("--fix")
|
||||
.arg("-")
|
||||
.pass_stdin("1"));
|
||||
assert_cmd_snapshot!(
|
||||
Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.arg("--config")
|
||||
.arg(r#"lint.isort.required-imports = ["import pandas"]"#)
|
||||
.args(["--select", "I002,ICN001,F401"])
|
||||
.args(["--stdin-filename", "test.py"])
|
||||
.arg("--unsafe-fixes")
|
||||
.arg("--fix")
|
||||
.arg("-")
|
||||
.pass_stdin("1")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flake8_import_convention_unused_aliased_import_no_conflict() {
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.arg("--config")
|
||||
.arg(r#"lint.isort.required-imports = ["import pandas as pd"]"#)
|
||||
.args(["--select", "I002,ICN001,F401"])
|
||||
.args(["--stdin-filename", "test.py"])
|
||||
.arg("--unsafe-fixes")
|
||||
.arg("--fix")
|
||||
.arg("-")
|
||||
.pass_stdin("1"));
|
||||
assert_cmd_snapshot!(
|
||||
Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
.arg("--config")
|
||||
.arg(r#"lint.isort.required-imports = ["import pandas as pd"]"#)
|
||||
.args(["--select", "I002,ICN001,F401"])
|
||||
.args(["--stdin-filename", "test.py"])
|
||||
.arg("--unsafe-fixes")
|
||||
.arg("--fix")
|
||||
.arg("-")
|
||||
.pass_stdin("1")
|
||||
);
|
||||
}
|
||||
|
||||
// See: https://github.com/astral-sh/ruff/issues/16177
|
||||
@@ -5430,14 +5436,15 @@ match 2:
|
||||
print("it's one")
|
||||
"#
|
||||
),
|
||||
@r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
All checks passed!
|
||||
test.py:2:1: SyntaxError: Cannot use `match` statement on Python 3.9 (syntax was added in Python 3.10)
|
||||
Found 1 error.
|
||||
|
||||
----- stderr -----
|
||||
"
|
||||
"###
|
||||
);
|
||||
|
||||
// syntax error on 3.9 with preview
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
//!
|
||||
//! The above snippet has been built out of the following structure:
|
||||
use crate::snippet;
|
||||
use std::cmp::{max, min, Reverse};
|
||||
use std::cmp::{Reverse, max, min};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Display;
|
||||
use std::ops::Range;
|
||||
@@ -41,7 +41,7 @@ use std::{cmp, fmt};
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
use crate::renderer::styled_buffer::StyledBuffer;
|
||||
use crate::renderer::{stylesheet::Stylesheet, Margin, Style, DEFAULT_TERM_WIDTH};
|
||||
use crate::renderer::{DEFAULT_TERM_WIDTH, Margin, Style, stylesheet::Stylesheet};
|
||||
|
||||
const ANONYMIZED_LINE_NUM: &str = "LL";
|
||||
const ERROR_TXT: &str = "error";
|
||||
@@ -1273,10 +1273,7 @@ fn fold_body(body: Vec<DisplayLine<'_>>) -> Vec<DisplayLine<'_>> {
|
||||
let inline_marks = lines
|
||||
.last()
|
||||
.and_then(|line| {
|
||||
if let DisplayLine::Source {
|
||||
ref inline_marks, ..
|
||||
} = line
|
||||
{
|
||||
if let DisplayLine::Source { inline_marks, .. } = line {
|
||||
let inline_marks = inline_marks.clone();
|
||||
Some(inline_marks)
|
||||
} else {
|
||||
|
||||
@@ -2,8 +2,8 @@ mod deserialize;
|
||||
|
||||
use crate::deserialize::Fixture;
|
||||
use ruff_annotate_snippets::{Message, Renderer};
|
||||
use snapbox::data::DataFormat;
|
||||
use snapbox::Data;
|
||||
use snapbox::data::DataFormat;
|
||||
use std::error::Error;
|
||||
|
||||
fn main() {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
use std::path::Path;
|
||||
|
||||
use ruff_benchmark::criterion::{
|
||||
criterion_group, criterion_main, BenchmarkId, Criterion, Throughput,
|
||||
BenchmarkId, Criterion, Throughput, criterion_group, criterion_main,
|
||||
};
|
||||
|
||||
use ruff_benchmark::{
|
||||
TestCase, LARGE_DATASET, NUMPY_CTYPESLIB, NUMPY_GLOBALS, PYDANTIC_TYPES, UNICODE_PYPINYIN,
|
||||
LARGE_DATASET, NUMPY_CTYPESLIB, NUMPY_GLOBALS, PYDANTIC_TYPES, TestCase, UNICODE_PYPINYIN,
|
||||
};
|
||||
use ruff_python_formatter::{format_module_ast, PreviewMode, PyFormatOptions};
|
||||
use ruff_python_parser::{parse, Mode, ParseOptions};
|
||||
use ruff_python_formatter::{PreviewMode, PyFormatOptions, format_module_ast};
|
||||
use ruff_python_parser::{Mode, ParseOptions, parse};
|
||||
use ruff_python_trivia::CommentRanges;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use ruff_benchmark::criterion;
|
||||
|
||||
use criterion::{
|
||||
criterion_group, criterion_main, measurement::WallTime, BenchmarkId, Criterion, Throughput,
|
||||
BenchmarkId, Criterion, Throughput, criterion_group, criterion_main, measurement::WallTime,
|
||||
};
|
||||
use ruff_benchmark::{
|
||||
TestCase, LARGE_DATASET, NUMPY_CTYPESLIB, NUMPY_GLOBALS, PYDANTIC_TYPES, UNICODE_PYPINYIN,
|
||||
LARGE_DATASET, NUMPY_CTYPESLIB, NUMPY_GLOBALS, PYDANTIC_TYPES, TestCase, UNICODE_PYPINYIN,
|
||||
};
|
||||
use ruff_python_parser::{lexer, Mode, TokenKind};
|
||||
use ruff_python_parser::{Mode, TokenKind, lexer};
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[global_allocator]
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
use ruff_benchmark::criterion;
|
||||
|
||||
use criterion::{
|
||||
criterion_group, criterion_main, BenchmarkGroup, BenchmarkId, Criterion, Throughput,
|
||||
BenchmarkGroup, BenchmarkId, Criterion, Throughput, criterion_group, criterion_main,
|
||||
};
|
||||
use ruff_benchmark::{
|
||||
TestCase, LARGE_DATASET, NUMPY_CTYPESLIB, NUMPY_GLOBALS, PYDANTIC_TYPES, UNICODE_PYPINYIN,
|
||||
LARGE_DATASET, NUMPY_CTYPESLIB, NUMPY_GLOBALS, PYDANTIC_TYPES, TestCase, UNICODE_PYPINYIN,
|
||||
};
|
||||
use ruff_linter::linter::{lint_only, ParseSource};
|
||||
use ruff_linter::linter::{ParseSource, lint_only};
|
||||
use ruff_linter::rule_selector::PreviewOptions;
|
||||
use ruff_linter::settings::rule_table::RuleTable;
|
||||
use ruff_linter::settings::types::PreviewMode;
|
||||
use ruff_linter::settings::{flags, LinterSettings};
|
||||
use ruff_linter::settings::{LinterSettings, flags};
|
||||
use ruff_linter::source_kind::SourceKind;
|
||||
use ruff_linter::{registry::Rule, RuleSelector};
|
||||
use ruff_linter::{RuleSelector, registry::Rule};
|
||||
use ruff_python_ast::PySourceType;
|
||||
use ruff_python_parser::parse_module;
|
||||
|
||||
@@ -45,8 +45,8 @@ static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
|
||||
target_arch = "powerpc64"
|
||||
)
|
||||
))]
|
||||
#[unsafe(export_name = "_rjem_malloc_conf")]
|
||||
#[expect(non_upper_case_globals)]
|
||||
#[export_name = "_rjem_malloc_conf"]
|
||||
#[expect(unsafe_code)]
|
||||
pub static _rjem_malloc_conf: &[u8] = b"dirty_decay_ms:-1,muzzy_decay_ms:-1\0";
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use ruff_benchmark::criterion;
|
||||
|
||||
use criterion::{
|
||||
criterion_group, criterion_main, measurement::WallTime, BenchmarkId, Criterion, Throughput,
|
||||
BenchmarkId, Criterion, Throughput, criterion_group, criterion_main, measurement::WallTime,
|
||||
};
|
||||
use ruff_benchmark::{
|
||||
TestCase, LARGE_DATASET, NUMPY_CTYPESLIB, NUMPY_GLOBALS, PYDANTIC_TYPES, UNICODE_PYPINYIN,
|
||||
LARGE_DATASET, NUMPY_CTYPESLIB, NUMPY_GLOBALS, PYDANTIC_TYPES, TestCase, UNICODE_PYPINYIN,
|
||||
};
|
||||
use ruff_python_ast::statement_visitor::{walk_stmt, StatementVisitor};
|
||||
use ruff_python_ast::Stmt;
|
||||
use ruff_python_ast::statement_visitor::{StatementVisitor, walk_stmt};
|
||||
use ruff_python_parser::parse_module;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
|
||||
@@ -3,13 +3,13 @@ use ruff_benchmark::criterion;
|
||||
|
||||
use std::ops::Range;
|
||||
|
||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||
use criterion::{BatchSize, Criterion, criterion_group, criterion_main};
|
||||
use rayon::ThreadPoolBuilder;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use ruff_benchmark::TestFile;
|
||||
use ruff_db::diagnostic::{Diagnostic, DiagnosticId, Severity};
|
||||
use ruff_db::files::{system_path_to_file, File};
|
||||
use ruff_db::files::{File, system_path_to_file};
|
||||
use ruff_db::source::source_text;
|
||||
use ruff_db::system::{MemoryFileSystem, SystemPath, SystemPathBuf, TestSystem};
|
||||
use ruff_python_ast::PythonVersion;
|
||||
@@ -78,7 +78,7 @@ fn setup_tomllib_case() -> Case {
|
||||
|
||||
let src_root = SystemPath::new("/src");
|
||||
let mut metadata = ProjectMetadata::discover(src_root, &system).unwrap();
|
||||
metadata.apply_cli_options(Options {
|
||||
metadata.apply_options(Options {
|
||||
environment: Some(EnvironmentOptions {
|
||||
python_version: Some(RangedValue::cli(PythonVersion::PY312)),
|
||||
..EnvironmentOptions::default()
|
||||
@@ -131,7 +131,7 @@ fn benchmark_incremental(criterion: &mut Criterion) {
|
||||
fn setup() -> Case {
|
||||
let case = setup_tomllib_case();
|
||||
|
||||
let result: Vec<_> = case.db.check().unwrap();
|
||||
let result: Vec<_> = case.db.check();
|
||||
|
||||
assert_diagnostics(&case.db, &result, EXPECTED_TOMLLIB_DIAGNOSTICS);
|
||||
|
||||
@@ -159,7 +159,7 @@ fn benchmark_incremental(criterion: &mut Criterion) {
|
||||
None,
|
||||
);
|
||||
|
||||
let result = db.check().unwrap();
|
||||
let result = db.check();
|
||||
|
||||
assert_eq!(result.len(), EXPECTED_TOMLLIB_DIAGNOSTICS.len());
|
||||
}
|
||||
@@ -179,7 +179,7 @@ fn benchmark_cold(criterion: &mut Criterion) {
|
||||
setup_tomllib_case,
|
||||
|case| {
|
||||
let Case { db, .. } = case;
|
||||
let result: Vec<_> = db.check().unwrap();
|
||||
let result: Vec<_> = db.check();
|
||||
|
||||
assert_diagnostics(db, &result, EXPECTED_TOMLLIB_DIAGNOSTICS);
|
||||
},
|
||||
@@ -224,7 +224,7 @@ fn setup_micro_case(code: &str) -> Case {
|
||||
|
||||
let src_root = SystemPath::new("/src");
|
||||
let mut metadata = ProjectMetadata::discover(src_root, &system).unwrap();
|
||||
metadata.apply_cli_options(Options {
|
||||
metadata.apply_options(Options {
|
||||
environment: Some(EnvironmentOptions {
|
||||
python_version: Some(RangedValue::cli(PythonVersion::PY312)),
|
||||
..EnvironmentOptions::default()
|
||||
@@ -293,7 +293,7 @@ fn benchmark_many_string_assignments(criterion: &mut Criterion) {
|
||||
},
|
||||
|case| {
|
||||
let Case { db, .. } = case;
|
||||
let result = db.check().unwrap();
|
||||
let result = db.check();
|
||||
assert_eq!(result.len(), 0);
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
@@ -331,12 +331,6 @@ fn benchmark_many_tuple_assignments(criterion: &mut Criterion) {
|
||||
t += (7,)
|
||||
if flag():
|
||||
t += (8,)
|
||||
if flag():
|
||||
t += (9,)
|
||||
if flag():
|
||||
t += (10,)
|
||||
if flag():
|
||||
t += (11,)
|
||||
|
||||
# Perform some kind of operation on the union type
|
||||
print(1 in t)
|
||||
@@ -345,7 +339,7 @@ fn benchmark_many_tuple_assignments(criterion: &mut Criterion) {
|
||||
},
|
||||
|case| {
|
||||
let Case { db, .. } = case;
|
||||
let result = db.check().unwrap();
|
||||
let result = db.check();
|
||||
assert_eq!(result.len(), 0);
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
|
||||
@@ -2,8 +2,8 @@ use std::borrow::Cow;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::num::{
|
||||
NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroU128, NonZeroU16,
|
||||
NonZeroU32, NonZeroU64, NonZeroU8,
|
||||
NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroU8, NonZeroU16, NonZeroU32,
|
||||
NonZeroU64, NonZeroU128,
|
||||
};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ path-slash = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tracing-subscriber = { workspace = true, optional = true }
|
||||
tracing-tree = { workspace = true, optional = true }
|
||||
rustc-hash = { workspace = true }
|
||||
zip = { workspace = true }
|
||||
|
||||
@@ -55,4 +54,4 @@ cache = ["ruff_cache"]
|
||||
os = ["ignore", "dep:etcetera"]
|
||||
serde = ["dep:serde", "camino/serde1"]
|
||||
# Exposes testing utilities.
|
||||
testing = ["tracing-subscriber", "tracing-tree"]
|
||||
testing = ["tracing-subscriber"]
|
||||
|
||||
@@ -7,7 +7,7 @@ use ruff_annotate_snippets::Level as AnnotateLevel;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
pub use self::render::DisplayDiagnostic;
|
||||
use crate::{files::File, Db};
|
||||
use crate::{Db, files::File};
|
||||
|
||||
mod render;
|
||||
mod stylesheet;
|
||||
@@ -249,6 +249,25 @@ impl Diagnostic {
|
||||
diagnostic: self,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns all annotations, skipping the first primary annotation.
|
||||
pub fn secondary_annotations(&self) -> impl Iterator<Item = &Annotation> {
|
||||
let mut seen_primary = false;
|
||||
self.inner.annotations.iter().filter(move |ann| {
|
||||
if seen_primary {
|
||||
true
|
||||
} else if ann.is_primary {
|
||||
seen_primary = true;
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn sub_diagnostics(&self) -> &[SubDiagnostic] {
|
||||
&self.inner.subs
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
@@ -371,6 +390,57 @@ impl SubDiagnostic {
|
||||
pub fn annotate(&mut self, ann: Annotation) {
|
||||
self.inner.annotations.push(ann);
|
||||
}
|
||||
|
||||
pub fn annotations(&self) -> &[Annotation] {
|
||||
&self.inner.annotations
|
||||
}
|
||||
|
||||
/// Returns a shared borrow of the "primary" annotation of this diagnostic
|
||||
/// if one exists.
|
||||
///
|
||||
/// When there are multiple primary annotations, then the first one that
|
||||
/// was added to this diagnostic is returned.
|
||||
pub fn primary_annotation(&self) -> Option<&Annotation> {
|
||||
self.inner.annotations.iter().find(|ann| ann.is_primary)
|
||||
}
|
||||
|
||||
/// Introspects this diagnostic and returns what kind of "primary" message
|
||||
/// it contains for concise formatting.
|
||||
///
|
||||
/// When we concisely format diagnostics, we likely want to not only
|
||||
/// include the primary diagnostic message but also the message attached
|
||||
/// to the primary annotation. In particular, the primary annotation often
|
||||
/// contains *essential* information or context for understanding the
|
||||
/// diagnostic.
|
||||
///
|
||||
/// The reason why we don't just always return both the main diagnostic
|
||||
/// message and the primary annotation message is because this was written
|
||||
/// in the midst of an incremental migration of ty over to the new
|
||||
/// diagnostic data model. At time of writing, diagnostics were still
|
||||
/// constructed in the old model where the main diagnostic message and the
|
||||
/// primary annotation message were not distinguished from each other. So
|
||||
/// for now, we carefully return what kind of messages this diagnostic
|
||||
/// contains. In effect, if this diagnostic has a non-empty main message
|
||||
/// *and* a non-empty primary annotation message, then the diagnostic is
|
||||
/// 100% using the new diagnostic data model and we can format things
|
||||
/// appropriately.
|
||||
///
|
||||
/// The type returned implements the `std::fmt::Display` trait. In most
|
||||
/// cases, just converting it to a string (or printing it) will do what
|
||||
/// you want.
|
||||
pub fn concise_message(&self) -> ConciseMessage {
|
||||
let main = self.inner.message.as_str();
|
||||
let annotation = self
|
||||
.primary_annotation()
|
||||
.and_then(|ann| ann.get_message())
|
||||
.unwrap_or_default();
|
||||
match (main.is_empty(), annotation.is_empty()) {
|
||||
(false, true) => ConciseMessage::MainDiagnostic(main),
|
||||
(true, false) => ConciseMessage::PrimaryAnnotation(annotation),
|
||||
(false, false) => ConciseMessage::Both { main, annotation },
|
||||
(true, true) => ConciseMessage::Empty,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
@@ -493,6 +563,11 @@ impl Annotation {
|
||||
&self.span
|
||||
}
|
||||
|
||||
/// Sets the span on this annotation.
|
||||
pub fn set_span(&mut self, span: Span) {
|
||||
self.span = span;
|
||||
}
|
||||
|
||||
/// Returns the tags associated with this annotation.
|
||||
pub fn get_tags(&self) -> &[DiagnosticTag] {
|
||||
&self.tags
|
||||
@@ -616,7 +691,7 @@ impl DiagnosticId {
|
||||
///
|
||||
/// Note that this doesn't include the lint's category. It
|
||||
/// only includes the lint's name.
|
||||
pub fn as_str(&self) -> &str {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
DiagnosticId::Panic => "panic",
|
||||
DiagnosticId::Io => "io",
|
||||
|
||||
@@ -7,12 +7,12 @@ use ruff_annotate_snippets::{
|
||||
use ruff_source_file::{LineIndex, OneIndexed, SourceCode};
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
|
||||
use crate::diagnostic::stylesheet::{fmt_styled, DiagnosticStylesheet};
|
||||
use crate::diagnostic::stylesheet::{DiagnosticStylesheet, fmt_styled};
|
||||
use crate::{
|
||||
files::File,
|
||||
source::{line_index, source_text, SourceText},
|
||||
system::SystemPath,
|
||||
Db,
|
||||
files::File,
|
||||
source::{SourceText, line_index, source_text},
|
||||
system::SystemPath,
|
||||
};
|
||||
|
||||
use super::{
|
||||
@@ -708,11 +708,11 @@ fn relativize_path<'p>(cwd: &SystemPath, path: &'p str) -> &'p str {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use crate::Upcast;
|
||||
use crate::diagnostic::{Annotation, DiagnosticId, Severity, Span};
|
||||
use crate::files::system_path_to_file;
|
||||
use crate::system::{DbWithWritableSystem, SystemPath};
|
||||
use crate::tests::TestDb;
|
||||
use crate::Upcast;
|
||||
|
||||
use super::*;
|
||||
|
||||
|
||||
@@ -11,12 +11,13 @@ use ruff_text_size::{Ranged, TextRange};
|
||||
use salsa::plumbing::AsId;
|
||||
use salsa::{Durability, Setter};
|
||||
|
||||
use crate::diagnostic::{Span, UnifiedFile};
|
||||
use crate::file_revision::FileRevision;
|
||||
use crate::files::file_root::FileRoots;
|
||||
use crate::files::private::FileStatus;
|
||||
use crate::system::{SystemPath, SystemPathBuf, SystemVirtualPath, SystemVirtualPathBuf};
|
||||
use crate::vendored::{VendoredPath, VendoredPathBuf};
|
||||
use crate::{vendored, Db, FxDashMap};
|
||||
use crate::{Db, FxDashMap, vendored};
|
||||
|
||||
mod file_root;
|
||||
mod path;
|
||||
@@ -274,7 +275,12 @@ impl fmt::Debug for Files {
|
||||
impl std::panic::RefUnwindSafe for Files {}
|
||||
|
||||
/// A file that's either stored on the host system's file system or in the vendored file system.
|
||||
///
|
||||
/// # Ordering
|
||||
/// Ordering is based on the file's salsa-assigned id and not on its values.
|
||||
/// The id may change between runs.
|
||||
#[salsa::input]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct File {
|
||||
/// The path of the file (immutable).
|
||||
#[returns(ref)]
|
||||
@@ -549,10 +555,33 @@ impl Ranged for FileRange {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Span> for FileRange {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &Span) -> Result<Self, Self::Error> {
|
||||
let UnifiedFile::Ty(file) = value.file() else {
|
||||
return Err(());
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
file: *file,
|
||||
range: value.range().ok_or(())?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Span> for FileRange {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: Span) -> Result<Self, Self::Error> {
|
||||
Self::try_from(&value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::file_revision::FileRevision;
|
||||
use crate::files::{system_path_to_file, vendored_path_to_file, FileError};
|
||||
use crate::files::{FileError, system_path_to_file, vendored_path_to_file};
|
||||
use crate::system::DbWithWritableSystem as _;
|
||||
use crate::tests::TestDb;
|
||||
use crate::vendored::VendoredFileSystemBuilder;
|
||||
|
||||
@@ -3,9 +3,9 @@ use std::fmt::Formatter;
|
||||
use path_slash::PathExt;
|
||||
use salsa::Durability;
|
||||
|
||||
use crate::Db;
|
||||
use crate::file_revision::FileRevision;
|
||||
use crate::system::{SystemPath, SystemPathBuf};
|
||||
use crate::Db;
|
||||
|
||||
/// A root path for files tracked by the database.
|
||||
///
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::files::{system_path_to_file, vendored_path_to_file, File};
|
||||
use crate::Db;
|
||||
use crate::files::{File, system_path_to_file, vendored_path_to_file};
|
||||
use crate::system::{SystemPath, SystemPathBuf, SystemVirtualPath, SystemVirtualPathBuf};
|
||||
use crate::vendored::{VendoredPath, VendoredPathBuf};
|
||||
use crate::Db;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
/// Path to a file.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::any::Any;
|
||||
use std::backtrace::BacktraceStatus;
|
||||
use std::cell::Cell;
|
||||
use std::panic::Location;
|
||||
@@ -24,17 +25,25 @@ impl Payload {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn downcast_ref<R: Any>(&self) -> Option<&R> {
|
||||
self.0.downcast_ref::<R>()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for PanicError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "panicked at")?;
|
||||
write!(f, "panicked at")?;
|
||||
if let Some(location) = &self.location {
|
||||
write!(f, " {location}")?;
|
||||
}
|
||||
if let Some(payload) = self.payload.as_str() {
|
||||
write!(f, ":\n{payload}")?;
|
||||
}
|
||||
if let Some(query_trace) = self.salsa_backtrace.as_ref() {
|
||||
let _ = writeln!(f, "{query_trace}");
|
||||
}
|
||||
|
||||
if let Some(backtrace) = &self.backtrace {
|
||||
match backtrace.status() {
|
||||
BacktraceStatus::Disabled => {
|
||||
@@ -49,6 +58,7 @@ impl std::fmt::Display for PanicError {
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
use std::fmt::Formatter;
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
|
||||
use ruff_python_ast::ModModule;
|
||||
use ruff_python_parser::{parse_unchecked, ParseOptions, Parsed};
|
||||
use ruff_python_parser::{ParseOptions, Parsed, parse_unchecked};
|
||||
|
||||
use crate::Db;
|
||||
use crate::files::File;
|
||||
use crate::source::source_text;
|
||||
use crate::Db;
|
||||
|
||||
/// Returns the parsed AST of `file`, including its token stream.
|
||||
///
|
||||
@@ -18,7 +17,7 @@ use crate::Db;
|
||||
/// The query is only cached when the [`source_text()`] hasn't changed. This is because
|
||||
/// comparing two ASTs is a non-trivial operation and every offset change is directly
|
||||
/// reflected in the changed AST offsets.
|
||||
/// The other reason is that Ruff's AST doesn't implement `Eq` which Sala requires
|
||||
/// 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)]
|
||||
pub fn parsed_module(db: &dyn Db, file: File) -> ParsedModule {
|
||||
@@ -36,7 +35,10 @@ pub fn parsed_module(db: &dyn Db, file: File) -> ParsedModule {
|
||||
ParsedModule::new(parsed)
|
||||
}
|
||||
|
||||
/// Cheap cloneable wrapper around the parsed module.
|
||||
/// A wrapper around a parsed module.
|
||||
///
|
||||
/// This type manages instances of the module AST. A particular instance of the AST
|
||||
/// is represented with the [`ParsedModuleRef`] type.
|
||||
#[derive(Clone)]
|
||||
pub struct ParsedModule {
|
||||
inner: Arc<Parsed<ModModule>>,
|
||||
@@ -49,17 +51,11 @@ impl ParsedModule {
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes `self` and returns the Arc storing the parsed module.
|
||||
pub fn into_arc(self) -> Arc<Parsed<ModModule>> {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ParsedModule {
|
||||
type Target = Parsed<ModModule>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
/// Loads a reference to the parsed module.
|
||||
pub fn load(&self, _db: &dyn Db) -> ParsedModuleRef {
|
||||
ParsedModuleRef {
|
||||
module_ref: self.inner.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,8 +73,33 @@ impl PartialEq for ParsedModule {
|
||||
|
||||
impl Eq for ParsedModule {}
|
||||
|
||||
/// Cheap cloneable wrapper around an instance of a module AST.
|
||||
#[derive(Clone)]
|
||||
pub struct ParsedModuleRef {
|
||||
module_ref: Arc<Parsed<ModModule>>,
|
||||
}
|
||||
|
||||
impl ParsedModuleRef {
|
||||
pub fn as_arc(&self) -> &Arc<Parsed<ModModule>> {
|
||||
&self.module_ref
|
||||
}
|
||||
|
||||
pub fn into_arc(self) -> Arc<Parsed<ModModule>> {
|
||||
self.module_ref
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for ParsedModuleRef {
|
||||
type Target = Parsed<ModModule>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.module_ref
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::Db;
|
||||
use crate::files::{system_path_to_file, vendored_path_to_file};
|
||||
use crate::parsed::parsed_module;
|
||||
use crate::system::{
|
||||
@@ -86,7 +107,6 @@ mod tests {
|
||||
};
|
||||
use crate::tests::TestDb;
|
||||
use crate::vendored::{VendoredFileSystemBuilder, VendoredPath};
|
||||
use crate::Db;
|
||||
use zip::CompressionMethod;
|
||||
|
||||
#[test]
|
||||
@@ -98,7 +118,7 @@ mod tests {
|
||||
|
||||
let file = system_path_to_file(&db, path).unwrap();
|
||||
|
||||
let parsed = parsed_module(&db, file);
|
||||
let parsed = parsed_module(&db, file).load(&db);
|
||||
|
||||
assert!(parsed.has_valid_syntax());
|
||||
|
||||
@@ -114,7 +134,7 @@ mod tests {
|
||||
|
||||
let file = system_path_to_file(&db, path).unwrap();
|
||||
|
||||
let parsed = parsed_module(&db, file);
|
||||
let parsed = parsed_module(&db, file).load(&db);
|
||||
|
||||
assert!(parsed.has_valid_syntax());
|
||||
|
||||
@@ -130,7 +150,7 @@ mod tests {
|
||||
|
||||
let virtual_file = db.files().virtual_file(&db, path);
|
||||
|
||||
let parsed = parsed_module(&db, virtual_file.file());
|
||||
let parsed = parsed_module(&db, virtual_file.file()).load(&db);
|
||||
|
||||
assert!(parsed.has_valid_syntax());
|
||||
|
||||
@@ -146,7 +166,7 @@ mod tests {
|
||||
|
||||
let virtual_file = db.files().virtual_file(&db, path);
|
||||
|
||||
let parsed = parsed_module(&db, virtual_file.file());
|
||||
let parsed = parsed_module(&db, virtual_file.file()).load(&db);
|
||||
|
||||
assert!(parsed.has_valid_syntax());
|
||||
|
||||
@@ -177,7 +197,7 @@ else:
|
||||
|
||||
let file = vendored_path_to_file(&db, VendoredPath::new("path.pyi")).unwrap();
|
||||
|
||||
let parsed = parsed_module(&db, file);
|
||||
let parsed = parsed_module(&db, file).load(&db);
|
||||
|
||||
assert!(parsed.has_valid_syntax());
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ use ruff_notebook::Notebook;
|
||||
use ruff_python_ast::PySourceType;
|
||||
use ruff_source_file::LineIndex;
|
||||
|
||||
use crate::files::{File, FilePath};
|
||||
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]
|
||||
@@ -133,7 +133,7 @@ struct SourceTextInner {
|
||||
#[derive(Eq, PartialEq)]
|
||||
enum SourceTextKind {
|
||||
Text(String),
|
||||
Notebook(Notebook),
|
||||
Notebook(Box<Notebook>),
|
||||
}
|
||||
|
||||
impl From<String> for SourceTextKind {
|
||||
@@ -144,7 +144,7 @@ impl From<String> for SourceTextKind {
|
||||
|
||||
impl From<Notebook> for SourceTextKind {
|
||||
fn from(notebook: Notebook) -> Self {
|
||||
SourceTextKind::Notebook(notebook)
|
||||
SourceTextKind::Notebook(Box::new(notebook))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,9 +216,11 @@ mod tests {
|
||||
|
||||
let events = db.take_salsa_events();
|
||||
|
||||
assert!(!events
|
||||
.iter()
|
||||
.any(|event| matches!(event.kind, EventKind::WillExecute { .. })));
|
||||
assert!(
|
||||
!events
|
||||
.iter()
|
||||
.any(|event| matches!(event.kind, EventKind::WillExecute { .. }))
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ use walk_directory::WalkDirectoryBuilder;
|
||||
use crate::file_revision::FileRevision;
|
||||
|
||||
pub use self::path::{
|
||||
deduplicate_nested_paths, DeduplicatedNestedPathsIter, SystemPath, SystemPathBuf,
|
||||
SystemVirtualPath, SystemVirtualPathBuf,
|
||||
DeduplicatedNestedPathsIter, SystemPath, SystemPathBuf, SystemVirtualPath,
|
||||
SystemVirtualPathBuf, deduplicate_nested_paths,
|
||||
};
|
||||
|
||||
mod memory_fs;
|
||||
@@ -167,10 +167,25 @@ pub trait System: Debug {
|
||||
&self,
|
||||
pattern: &str,
|
||||
) -> std::result::Result<
|
||||
Box<dyn Iterator<Item = std::result::Result<SystemPathBuf, GlobError>>>,
|
||||
Box<dyn Iterator<Item = std::result::Result<SystemPathBuf, GlobError>> + '_>,
|
||||
PatternError,
|
||||
>;
|
||||
|
||||
/// Fetches the environment variable `key` from the current process.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns [`std::env::VarError::NotPresent`] if:
|
||||
/// - The variable is not set.
|
||||
/// - The variable's name contains an equal sign or NUL (`'='` or `'\0'`).
|
||||
///
|
||||
/// Returns [`std::env::VarError::NotUnicode`] if the variable's value is not valid
|
||||
/// Unicode.
|
||||
fn env_var(&self, name: &str) -> std::result::Result<String, std::env::VarError> {
|
||||
let _ = name;
|
||||
Err(std::env::VarError::NotPresent)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any;
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
|
||||
|
||||
@@ -7,8 +7,8 @@ use filetime::FileTime;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::system::{
|
||||
file_time_now, walk_directory, DirectoryEntry, FileType, GlobError, GlobErrorKind, Metadata,
|
||||
Result, SystemPath, SystemPathBuf, SystemVirtualPath, SystemVirtualPathBuf,
|
||||
DirectoryEntry, FileType, GlobError, GlobErrorKind, Metadata, Result, SystemPath,
|
||||
SystemPathBuf, SystemVirtualPath, SystemVirtualPathBuf, file_time_now, walk_directory,
|
||||
};
|
||||
|
||||
use super::walk_directory::{
|
||||
@@ -236,7 +236,7 @@ impl MemoryFileSystem {
|
||||
&self,
|
||||
pattern: &str,
|
||||
) -> std::result::Result<
|
||||
impl Iterator<Item = std::result::Result<SystemPathBuf, GlobError>>,
|
||||
impl Iterator<Item = std::result::Result<SystemPathBuf, GlobError>> + '_,
|
||||
glob::PatternError,
|
||||
> {
|
||||
// Very naive implementation that iterates over all files and collects all that match the given pattern.
|
||||
@@ -463,17 +463,17 @@ fn not_found() -> std::io::Error {
|
||||
fn is_a_directory() -> std::io::Error {
|
||||
// Note: Rust returns `ErrorKind::IsADirectory` for this error but this is a nightly only variant :(.
|
||||
// So we have to use other for now.
|
||||
std::io::Error::new(std::io::ErrorKind::Other, "Is a directory")
|
||||
std::io::Error::other("Is a directory")
|
||||
}
|
||||
|
||||
fn not_a_directory() -> std::io::Error {
|
||||
// Note: Rust returns `ErrorKind::NotADirectory` for this error but this is a nightly only variant :(.
|
||||
// So we have to use `Other` for now.
|
||||
std::io::Error::new(std::io::ErrorKind::Other, "Not a directory")
|
||||
std::io::Error::other("Not a directory")
|
||||
}
|
||||
|
||||
fn directory_not_empty() -> std::io::Error {
|
||||
std::io::Error::new(std::io::ErrorKind::Other, "directory not empty")
|
||||
std::io::Error::other("directory not empty")
|
||||
}
|
||||
|
||||
fn create_dir_all(
|
||||
@@ -701,8 +701,8 @@ mod tests {
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::system::walk_directory::tests::DirectoryEntryToString;
|
||||
use crate::system::walk_directory::WalkState;
|
||||
use crate::system::walk_directory::tests::DirectoryEntryToString;
|
||||
use crate::system::{
|
||||
DirectoryEntry, FileType, MemoryFileSystem, Result, SystemPath, SystemPathBuf,
|
||||
SystemVirtualPath,
|
||||
|
||||
@@ -214,6 +214,10 @@ impl System for OsSystem {
|
||||
})
|
||||
})))
|
||||
}
|
||||
|
||||
fn env_var(&self, name: &str) -> std::result::Result<String, std::env::VarError> {
|
||||
std::env::var(name)
|
||||
}
|
||||
}
|
||||
|
||||
impl OsSystem {
|
||||
@@ -256,7 +260,9 @@ impl OsSystem {
|
||||
let Ok(canonicalized) = SystemPathBuf::from_path_buf(canonicalized) else {
|
||||
// The original path is valid UTF8 but the canonicalized path isn't. This definitely suggests
|
||||
// that a symlink is involved. Fall back to the slow path.
|
||||
tracing::debug!("Falling back to the slow case-sensitive path existence check because the canonicalized path of `{simplified}` is not valid UTF-8");
|
||||
tracing::debug!(
|
||||
"Falling back to the slow case-sensitive path existence check because the canonicalized path of `{simplified}` is not valid UTF-8"
|
||||
);
|
||||
return None;
|
||||
};
|
||||
|
||||
@@ -266,7 +272,9 @@ impl OsSystem {
|
||||
// `path` pointed to a symlink (or some other none reversible path normalization happened).
|
||||
// In this case, fall back to the slow path.
|
||||
if simplified_canonicalized.as_str().to_lowercase() != simplified.as_str().to_lowercase() {
|
||||
tracing::debug!("Falling back to the slow case-sensitive path existence check for `{simplified}` because the canonicalized path `{simplified_canonicalized}` differs not only by casing");
|
||||
tracing::debug!(
|
||||
"Falling back to the slow case-sensitive path existence check for `{simplified}` because the canonicalized path `{simplified_canonicalized}` differs not only by casing"
|
||||
);
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -662,8 +670,8 @@ fn detect_case_sensitivity(path: &SystemPath) -> CaseSensitivity {
|
||||
mod tests {
|
||||
use tempfile::TempDir;
|
||||
|
||||
use crate::system::walk_directory::tests::DirectoryEntryToString;
|
||||
use crate::system::DirectoryEntry;
|
||||
use crate::system::walk_directory::tests::DirectoryEntryToString;
|
||||
|
||||
use super::*;
|
||||
|
||||
|
||||
@@ -596,6 +596,13 @@ impl AsRef<SystemPath> for Utf8PathBuf {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<SystemPath> for camino::Utf8Component<'_> {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &SystemPath {
|
||||
SystemPath::new(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<SystemPath> for str {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &SystemPath {
|
||||
@@ -626,6 +633,22 @@ impl Deref for SystemPathBuf {
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: AsRef<SystemPath>> FromIterator<P> for SystemPathBuf {
|
||||
fn from_iter<I: IntoIterator<Item = P>>(iter: I) -> Self {
|
||||
let mut buf = SystemPathBuf::new();
|
||||
buf.extend(iter);
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: AsRef<SystemPath>> Extend<P> for SystemPathBuf {
|
||||
fn extend<I: IntoIterator<Item = P>>(&mut self, iter: I) {
|
||||
for path in iter {
|
||||
self.push(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for SystemPath {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
|
||||
@@ -3,15 +3,15 @@ use ruff_notebook::{Notebook, NotebookError};
|
||||
use std::panic::RefUnwindSafe;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::Db;
|
||||
use crate::files::File;
|
||||
use crate::system::{
|
||||
CaseSensitivity, DirectoryEntry, GlobError, MemoryFileSystem, Metadata, Result, System,
|
||||
SystemPath, SystemPathBuf, SystemVirtualPath,
|
||||
};
|
||||
use crate::Db;
|
||||
|
||||
use super::walk_directory::WalkDirectoryBuilder;
|
||||
use super::WritableSystem;
|
||||
use super::walk_directory::WalkDirectoryBuilder;
|
||||
|
||||
/// System implementation intended for testing.
|
||||
///
|
||||
@@ -117,7 +117,7 @@ impl System for TestSystem {
|
||||
&self,
|
||||
pattern: &str,
|
||||
) -> std::result::Result<
|
||||
Box<dyn Iterator<Item = std::result::Result<SystemPathBuf, GlobError>>>,
|
||||
Box<dyn Iterator<Item = std::result::Result<SystemPathBuf, GlobError>> + '_>,
|
||||
PatternError,
|
||||
> {
|
||||
self.system().glob(pattern)
|
||||
@@ -343,7 +343,7 @@ impl System for InMemorySystem {
|
||||
&self,
|
||||
pattern: &str,
|
||||
) -> std::result::Result<
|
||||
Box<dyn Iterator<Item = std::result::Result<SystemPathBuf, GlobError>>>,
|
||||
Box<dyn Iterator<Item = std::result::Result<SystemPathBuf, GlobError>> + '_>,
|
||||
PatternError,
|
||||
> {
|
||||
let iterator = self.memory_fs.glob(pattern)?;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Test helpers for working with Salsa databases
|
||||
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
|
||||
pub fn assert_function_query_was_not_run<Db, Q, QDb, I, R>(
|
||||
db: &Db,
|
||||
@@ -13,12 +13,12 @@ pub fn assert_function_query_was_not_run<Db, Q, QDb, I, R>(
|
||||
Q: Fn(QDb, I) -> R,
|
||||
I: salsa::plumbing::AsId + std::fmt::Debug + Copy,
|
||||
{
|
||||
let id = input.as_id().as_u32();
|
||||
let id = input.as_id();
|
||||
let (query_name, will_execute_event) = find_will_execute_event(db, query, input, events);
|
||||
|
||||
db.attach(|_| {
|
||||
if let Some(will_execute_event) = will_execute_event {
|
||||
panic!("Expected query {query_name}({id}) not to have run but it did: {will_execute_event:?}\n\n{events:#?}");
|
||||
panic!("Expected query {query_name}({id:?}) not to have run but it did: {will_execute_event:?}\n\n{events:#?}");
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -65,7 +65,7 @@ pub fn assert_function_query_was_run<Db, Q, QDb, I, R>(
|
||||
Q: Fn(QDb, I) -> R,
|
||||
I: salsa::plumbing::AsId + std::fmt::Debug + Copy,
|
||||
{
|
||||
let id = input.as_id().as_u32();
|
||||
let id = input.as_id();
|
||||
let (query_name, will_execute_event) = find_will_execute_event(db, query, input, events);
|
||||
|
||||
db.attach(|_| {
|
||||
@@ -141,7 +141,6 @@ pub fn setup_logging_with_filter(filter: &str) -> Option<LoggingGuard> {
|
||||
#[derive(Debug)]
|
||||
pub struct LoggingBuilder {
|
||||
filter: EnvFilter,
|
||||
hierarchical: bool,
|
||||
}
|
||||
|
||||
impl LoggingBuilder {
|
||||
@@ -154,50 +153,26 @@ impl LoggingBuilder {
|
||||
.parse()
|
||||
.expect("Hardcoded directive to be valid"),
|
||||
),
|
||||
hierarchical: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_filter(filter: &str) -> Option<Self> {
|
||||
let filter = EnvFilter::builder().parse(filter).ok()?;
|
||||
|
||||
Some(Self {
|
||||
filter,
|
||||
hierarchical: false,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with_hierarchical(mut self, hierarchical: bool) -> Self {
|
||||
self.hierarchical = hierarchical;
|
||||
self
|
||||
Some(Self { filter })
|
||||
}
|
||||
|
||||
pub fn build(self) -> LoggingGuard {
|
||||
let registry = tracing_subscriber::registry().with(self.filter);
|
||||
|
||||
let guard = if self.hierarchical {
|
||||
let subscriber = registry.with(
|
||||
tracing_tree::HierarchicalLayer::default()
|
||||
.with_indent_lines(true)
|
||||
.with_indent_amount(2)
|
||||
.with_bracketed_fields(true)
|
||||
.with_thread_ids(true)
|
||||
.with_targets(true)
|
||||
.with_writer(std::io::stderr)
|
||||
.with_timer(tracing_tree::time::Uptime::default()),
|
||||
);
|
||||
let subscriber = registry.with(
|
||||
tracing_subscriber::fmt::layer()
|
||||
.compact()
|
||||
.with_writer(std::io::stderr)
|
||||
.with_timer(tracing_subscriber::fmt::time()),
|
||||
);
|
||||
|
||||
tracing::subscriber::set_default(subscriber)
|
||||
} else {
|
||||
let subscriber = registry.with(
|
||||
tracing_subscriber::fmt::layer()
|
||||
.compact()
|
||||
.with_writer(std::io::stderr)
|
||||
.with_timer(tracing_subscriber::fmt::time()),
|
||||
);
|
||||
|
||||
tracing::subscriber::set_default(subscriber)
|
||||
};
|
||||
let guard = tracing::subscriber::set_default(subscriber);
|
||||
|
||||
LoggingGuard { _guard: guard }
|
||||
}
|
||||
@@ -249,7 +224,7 @@ fn query_was_not_run() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Expected query len(0) not to have run but it did:")]
|
||||
#[should_panic(expected = "Expected query len(Id(0)) not to have run but it did:")]
|
||||
fn query_was_not_run_fails_if_query_was_run() {
|
||||
use crate::tests::TestDb;
|
||||
use salsa::prelude::*;
|
||||
@@ -312,7 +287,7 @@ fn const_query_was_not_run_fails_if_query_was_run() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Expected query len(0) to have run but it did not:")]
|
||||
#[should_panic(expected = "Expected query len(Id(0)) to have run but it did not:")]
|
||||
fn query_was_run_fails_if_query_was_not_run() {
|
||||
use crate::tests::TestDb;
|
||||
use salsa::prelude::*;
|
||||
|
||||
@@ -7,7 +7,7 @@ use std::sync::{Arc, Mutex, MutexGuard};
|
||||
use crate::file_revision::FileRevision;
|
||||
use zip::result::ZipResult;
|
||||
use zip::write::FileOptions;
|
||||
use zip::{read::ZipFile, CompressionMethod, ZipArchive, ZipWriter};
|
||||
use zip::{CompressionMethod, ZipArchive, ZipWriter, read::ZipFile};
|
||||
|
||||
pub use self::path::{VendoredPath, VendoredPathBuf};
|
||||
|
||||
@@ -503,9 +503,11 @@ pub(crate) mod tests {
|
||||
let path = VendoredPath::new(path);
|
||||
assert!(!mock_typeshed.exists(path));
|
||||
assert!(mock_typeshed.metadata(path).is_err());
|
||||
assert!(mock_typeshed
|
||||
.read_to_string(path)
|
||||
.is_err_and(|err| err.to_string().contains("file not found")));
|
||||
assert!(
|
||||
mock_typeshed
|
||||
.read_to_string(path)
|
||||
.is_err_and(|err| err.to_string().contains("file not found"))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -14,7 +14,6 @@ license = { workspace = true }
|
||||
ty = { workspace = true }
|
||||
ty_project = { workspace = true, features = ["schemars"] }
|
||||
ruff = { workspace = true }
|
||||
ruff_diagnostics = { workspace = true }
|
||||
ruff_formatter = { workspace = true }
|
||||
ruff_linter = { workspace = true, features = ["schemars"] }
|
||||
ruff_notebook = { workspace = true }
|
||||
|
||||
@@ -9,11 +9,11 @@ use std::process::ExitCode;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{fmt, fs, io, iter};
|
||||
|
||||
use anyhow::{bail, format_err, Context, Error};
|
||||
use anyhow::{Context, Error, bail, format_err};
|
||||
use clap::{CommandFactory, FromArgMatches};
|
||||
use imara_diff::intern::InternedInput;
|
||||
use imara_diff::sink::Counter;
|
||||
use imara_diff::{diff, Algorithm};
|
||||
use imara_diff::{Algorithm, diff};
|
||||
use indicatif::ProgressStyle;
|
||||
#[cfg_attr(feature = "singlethreaded", allow(unused_imports))]
|
||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||
@@ -21,11 +21,11 @@ use serde::Deserialize;
|
||||
use similar::{ChangeTag, TextDiff};
|
||||
use tempfile::NamedTempFile;
|
||||
use tracing::{debug, error, info, info_span};
|
||||
use tracing_indicatif::span_ext::IndicatifSpanExt;
|
||||
use tracing_indicatif::IndicatifLayer;
|
||||
use tracing_indicatif::span_ext::IndicatifSpanExt;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
use ruff::args::{ConfigArguments, FormatArguments, FormatCommand, GlobalConfigArgs, LogLevelArgs};
|
||||
use ruff::resolve::resolve;
|
||||
@@ -33,10 +33,10 @@ use ruff_formatter::{FormatError, LineWidth, PrintError};
|
||||
use ruff_linter::logging::LogLevel;
|
||||
use ruff_linter::settings::types::{FilePattern, FilePatternSet};
|
||||
use ruff_python_formatter::{
|
||||
format_module_source, FormatModuleError, MagicTrailingComma, PreviewMode, PyFormatOptions,
|
||||
FormatModuleError, MagicTrailingComma, PreviewMode, PyFormatOptions, format_module_source,
|
||||
};
|
||||
use ruff_python_parser::ParseError;
|
||||
use ruff_workspace::resolver::{python_files_in_path, PyprojectConfig, ResolvedFile, Resolver};
|
||||
use ruff_workspace::resolver::{PyprojectConfig, ResolvedFile, Resolver, python_files_in_path};
|
||||
|
||||
fn parse_cli(dirs: &[PathBuf]) -> anyhow::Result<(FormatArguments, ConfigArguments)> {
|
||||
let args_matches = FormatCommand::command()
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
use std::path::PathBuf;
|
||||
use std::{fs, str};
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use anyhow::{Context, Result, bail};
|
||||
use clap::CommandFactory;
|
||||
use pretty_assertions::StrComparison;
|
||||
|
||||
use ruff::args;
|
||||
|
||||
use crate::generate_all::{Mode, REGENERATE_ALL_COMMAND};
|
||||
use crate::ROOT_DIR;
|
||||
use crate::generate_all::{Mode, REGENERATE_ALL_COMMAND};
|
||||
|
||||
const COMMAND_HELP_BEGIN_PRAGMA: &str = "<!-- Begin auto-generated command help. -->\n";
|
||||
const COMMAND_HELP_END_PRAGMA: &str = "<!-- End auto-generated command help. -->";
|
||||
@@ -140,7 +140,7 @@ mod tests {
|
||||
|
||||
use crate::generate_all::Mode;
|
||||
|
||||
use super::{main, Args};
|
||||
use super::{Args, main};
|
||||
|
||||
#[test]
|
||||
fn test_generate_json_schema() -> Result<()> {
|
||||
|
||||
@@ -10,7 +10,7 @@ use itertools::Itertools;
|
||||
use regex::{Captures, Regex};
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use ruff_diagnostics::FixAvailability;
|
||||
use ruff_linter::FixAvailability;
|
||||
use ruff_linter::registry::{Linter, Rule, RuleNamespace};
|
||||
use ruff_options_metadata::{OptionEntry, OptionsMetadata};
|
||||
use ruff_workspace::options::Options;
|
||||
@@ -29,7 +29,7 @@ pub(crate) fn main(args: &Args) -> Result<()> {
|
||||
if let Some(explanation) = rule.explanation() {
|
||||
let mut output = String::new();
|
||||
|
||||
let _ = writeln!(&mut output, "# {} ({})", rule.as_ref(), rule.noqa_code());
|
||||
let _ = writeln!(&mut output, "# {} ({})", rule.name(), rule.noqa_code());
|
||||
|
||||
let (linter, _) = Linter::parse_code(&rule.noqa_code().to_string()).unwrap();
|
||||
if linter.url().is_some() {
|
||||
@@ -101,7 +101,7 @@ pub(crate) fn main(args: &Args) -> Result<()> {
|
||||
let filename = PathBuf::from(ROOT_DIR)
|
||||
.join("docs")
|
||||
.join("rules")
|
||||
.join(rule.as_ref())
|
||||
.join(&*rule.name())
|
||||
.with_extension("md");
|
||||
|
||||
if args.dry_run {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use anyhow::{Result, bail};
|
||||
use pretty_assertions::StrComparison;
|
||||
use schemars::schema_for;
|
||||
|
||||
use crate::generate_all::{Mode, REGENERATE_ALL_COMMAND};
|
||||
use crate::ROOT_DIR;
|
||||
use crate::generate_all::{Mode, REGENERATE_ALL_COMMAND};
|
||||
use ruff_workspace::options::Options;
|
||||
|
||||
#[derive(clap::Args)]
|
||||
@@ -56,7 +56,7 @@ mod tests {
|
||||
|
||||
use crate::generate_all::Mode;
|
||||
|
||||
use super::{main, Args};
|
||||
use super::{Args, main};
|
||||
|
||||
#[test]
|
||||
fn test_generate_json_schema() -> Result<()> {
|
||||
|
||||
@@ -100,8 +100,8 @@ fn emit_field(output: &mut String, name: &str, field: &OptionField, parents: &[S
|
||||
if parents_anchor.is_empty() {
|
||||
let _ = writeln!(output, "{header_level} [`{name}`](#{name}) {{: #{name} }}");
|
||||
} else {
|
||||
let _ =
|
||||
writeln!(output,
|
||||
let _ = writeln!(
|
||||
output,
|
||||
"{header_level} [`{name}`](#{parents_anchor}_{name}) {{: #{parents_anchor}_{name} }}"
|
||||
);
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::borrow::Cow;
|
||||
use std::fmt::Write;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use ruff_diagnostics::FixAvailability;
|
||||
use ruff_linter::FixAvailability;
|
||||
use ruff_linter::registry::{Linter, Rule, RuleNamespace};
|
||||
use ruff_linter::upstream_categories::UpstreamCategoryAndPrefix;
|
||||
use ruff_options_metadata::OptionsMetadata;
|
||||
@@ -18,43 +18,44 @@ const FIX_SYMBOL: &str = "🛠️";
|
||||
const PREVIEW_SYMBOL: &str = "🧪";
|
||||
const REMOVED_SYMBOL: &str = "❌";
|
||||
const WARNING_SYMBOL: &str = "⚠️";
|
||||
const STABLE_SYMBOL: &str = "✔️";
|
||||
const SPACER: &str = " ";
|
||||
|
||||
/// Style for the rule's fixability and status icons.
|
||||
const SYMBOL_STYLE: &str = "style='width: 1em; display: inline-block;'";
|
||||
/// Style for the container wrapping the fixability and status icons.
|
||||
const SYMBOLS_CONTAINER: &str = "style='display: flex; gap: 0.5rem; justify-content: end;'";
|
||||
|
||||
fn generate_table(table_out: &mut String, rules: impl IntoIterator<Item = Rule>, linter: &Linter) {
|
||||
table_out.push_str("| Code | Name | Message | |");
|
||||
table_out.push_str("| Code | Name | Message | |");
|
||||
table_out.push('\n');
|
||||
table_out.push_str("| ---- | ---- | ------- | ------: |");
|
||||
table_out.push_str("| ---- | ---- | ------- | -: |");
|
||||
table_out.push('\n');
|
||||
for rule in rules {
|
||||
let status_token = match rule.group() {
|
||||
RuleGroup::Removed => {
|
||||
format!("<span title='Rule has been removed'>{REMOVED_SYMBOL}</span>")
|
||||
format!(
|
||||
"<span {SYMBOL_STYLE} title='Rule has been removed'>{REMOVED_SYMBOL}</span>"
|
||||
)
|
||||
}
|
||||
RuleGroup::Deprecated => {
|
||||
format!("<span title='Rule has been deprecated'>{WARNING_SYMBOL}</span>")
|
||||
format!(
|
||||
"<span {SYMBOL_STYLE} title='Rule has been deprecated'>{WARNING_SYMBOL}</span>"
|
||||
)
|
||||
}
|
||||
RuleGroup::Preview => {
|
||||
format!("<span title='Rule is in preview'>{PREVIEW_SYMBOL}</span>")
|
||||
}
|
||||
RuleGroup::Stable => {
|
||||
// A full opacity checkmark is a bit aggressive for indicating stable
|
||||
format!("<span title='Rule is stable' style='opacity: 0.6'>{STABLE_SYMBOL}</span>")
|
||||
format!("<span {SYMBOL_STYLE} title='Rule is in preview'>{PREVIEW_SYMBOL}</span>")
|
||||
}
|
||||
RuleGroup::Stable => format!("<span {SYMBOL_STYLE}></span>"),
|
||||
};
|
||||
|
||||
let fix_token = match rule.fixable() {
|
||||
FixAvailability::Always | FixAvailability::Sometimes => {
|
||||
format!("<span title='Automatic fix available'>{FIX_SYMBOL}</span>")
|
||||
}
|
||||
FixAvailability::None => {
|
||||
format!("<span title='Automatic fix not available' style='opacity: 0.1' aria-hidden='true'>{FIX_SYMBOL}</span>")
|
||||
format!("<span {SYMBOL_STYLE} title='Automatic fix available'>{FIX_SYMBOL}</span>")
|
||||
}
|
||||
FixAvailability::None => format!("<span {SYMBOL_STYLE}></span>"),
|
||||
};
|
||||
|
||||
let tokens = format!("{status_token} {fix_token}");
|
||||
|
||||
let rule_name = rule.as_ref();
|
||||
let rule_name = rule.name();
|
||||
|
||||
// If the message ends in a bracketed expression (like: "Use {replacement}"), escape the
|
||||
// brackets. Otherwise, it'll be interpreted as an HTML attribute via the `attr_list`
|
||||
@@ -80,15 +81,14 @@ fn generate_table(table_out: &mut String, rules: impl IntoIterator<Item = Rule>,
|
||||
#[expect(clippy::or_fun_call)]
|
||||
let _ = write!(
|
||||
table_out,
|
||||
"| {ss}{0}{1}{se} {{ #{0}{1} }} | {ss}{2}{se} | {ss}{3}{se} | {ss}{4}{se} |",
|
||||
linter.common_prefix(),
|
||||
linter.code_for_rule(rule).unwrap(),
|
||||
rule.explanation()
|
||||
"| {ss}{prefix}{code}{se} {{ #{prefix}{code} }} | {ss}{explanation}{se} | {ss}{message}{se} | <div {SYMBOLS_CONTAINER}>{status_token}{fix_token}</div>|",
|
||||
prefix = linter.common_prefix(),
|
||||
code = linter.code_for_rule(rule).unwrap(),
|
||||
explanation = rule
|
||||
.explanation()
|
||||
.is_some()
|
||||
.then_some(format_args!("[{rule_name}](rules/{rule_name}.md)"))
|
||||
.unwrap_or(format_args!("{rule_name}")),
|
||||
message,
|
||||
tokens,
|
||||
);
|
||||
table_out.push('\n');
|
||||
}
|
||||
@@ -104,29 +104,28 @@ pub(crate) fn generate() -> String {
|
||||
|
||||
let _ = write!(
|
||||
&mut table_out,
|
||||
"{SPACER}{STABLE_SYMBOL}{SPACER} The rule is stable."
|
||||
);
|
||||
table_out.push_str("<br />");
|
||||
|
||||
let _ = write!(&mut table_out,
|
||||
"{SPACER}{PREVIEW_SYMBOL}{SPACER} The rule is unstable and is in [\"preview\"](faq.md#what-is-preview)."
|
||||
);
|
||||
table_out.push_str("<br />");
|
||||
|
||||
let _ = write!(&mut table_out,
|
||||
let _ = write!(
|
||||
&mut table_out,
|
||||
"{SPACER}{WARNING_SYMBOL}{SPACER} The rule has been deprecated and will be removed in a future release."
|
||||
);
|
||||
table_out.push_str("<br />");
|
||||
|
||||
let _ = write!(&mut table_out,
|
||||
let _ = write!(
|
||||
&mut table_out,
|
||||
"{SPACER}{REMOVED_SYMBOL}{SPACER} The rule has been removed only the documentation is available."
|
||||
);
|
||||
table_out.push_str("<br />");
|
||||
|
||||
let _ = write!(&mut table_out,
|
||||
let _ = write!(
|
||||
&mut table_out,
|
||||
"{SPACER}{FIX_SYMBOL}{SPACER} The rule is automatically fixable by the `--fix` command-line option."
|
||||
);
|
||||
table_out.push_str("<br />");
|
||||
table_out.push_str("\n\n");
|
||||
table_out.push_str("All rules not marked as preview, deprecated or removed are stable.");
|
||||
table_out.push('\n');
|
||||
|
||||
for linter in Linter::iter() {
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
use std::cmp::max;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use anyhow::{Result, bail};
|
||||
use clap::{Command, CommandFactory};
|
||||
use itertools::Itertools;
|
||||
use pretty_assertions::StrComparison;
|
||||
|
||||
use crate::generate_all::{Mode, REGENERATE_ALL_COMMAND};
|
||||
use crate::ROOT_DIR;
|
||||
use crate::generate_all::{Mode, REGENERATE_ALL_COMMAND};
|
||||
|
||||
use ty::Cli;
|
||||
|
||||
@@ -29,24 +29,24 @@ pub(crate) fn main(args: &Args) -> Result<()> {
|
||||
Mode::DryRun => {
|
||||
println!("{reference_string}");
|
||||
}
|
||||
Mode::Check => {
|
||||
match std::fs::read_to_string(reference_path) {
|
||||
Ok(current) => {
|
||||
if current == reference_string {
|
||||
println!("Up-to-date: {filename}");
|
||||
} else {
|
||||
let comparison = StrComparison::new(¤t, &reference_string);
|
||||
bail!("{filename} changed, please run `{REGENERATE_ALL_COMMAND}`:\n{comparison}");
|
||||
}
|
||||
}
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
||||
bail!("{filename} not found, please run `{REGENERATE_ALL_COMMAND}`");
|
||||
}
|
||||
Err(err) => {
|
||||
bail!("{filename} changed, please run `{REGENERATE_ALL_COMMAND}`:\n{err}");
|
||||
Mode::Check => match std::fs::read_to_string(reference_path) {
|
||||
Ok(current) => {
|
||||
if current == reference_string {
|
||||
println!("Up-to-date: {filename}");
|
||||
} else {
|
||||
let comparison = StrComparison::new(¤t, &reference_string);
|
||||
bail!(
|
||||
"{filename} changed, please run `{REGENERATE_ALL_COMMAND}`:\n{comparison}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
||||
bail!("{filename} not found, please run `{REGENERATE_ALL_COMMAND}`");
|
||||
}
|
||||
Err(err) => {
|
||||
bail!("{filename} changed, please run `{REGENERATE_ALL_COMMAND}`:\n{err}");
|
||||
}
|
||||
},
|
||||
Mode::Write => match std::fs::read_to_string(&reference_path) {
|
||||
Ok(current) => {
|
||||
if current == reference_string {
|
||||
@@ -80,6 +80,7 @@ fn generate() -> String {
|
||||
|
||||
let mut parents = Vec::new();
|
||||
|
||||
output.push_str("<!-- WARNING: This file is auto-generated (cargo dev generate-all). Edit the doc comments in 'crates/ty/src/args.rs' if you want to change anything here. -->\n\n");
|
||||
output.push_str("# CLI Reference\n\n");
|
||||
generate_command(&mut output, &ty, &mut parents);
|
||||
|
||||
@@ -325,7 +326,7 @@ mod tests {
|
||||
|
||||
use crate::generate_all::Mode;
|
||||
|
||||
use super::{main, Args};
|
||||
use super::{Args, main};
|
||||
|
||||
#[test]
|
||||
fn ty_cli_reference_is_up_to_date() -> Result<()> {
|
||||
|
||||
@@ -9,8 +9,8 @@ use ruff_options_metadata::{OptionField, OptionSet, OptionsMetadata, Visit};
|
||||
use ty_project::metadata::Options;
|
||||
|
||||
use crate::{
|
||||
generate_all::{Mode, REGENERATE_ALL_COMMAND},
|
||||
ROOT_DIR,
|
||||
generate_all::{Mode, REGENERATE_ALL_COMMAND},
|
||||
};
|
||||
|
||||
#[derive(clap::Args)]
|
||||
@@ -25,6 +25,10 @@ pub(crate) fn main(args: &Args) -> anyhow::Result<()> {
|
||||
let file_name = "crates/ty/docs/configuration.md";
|
||||
let markdown_path = PathBuf::from(ROOT_DIR).join(file_name);
|
||||
|
||||
output.push_str(
|
||||
"<!-- WARNING: This file is auto-generated (cargo dev generate-all). Update the doc comments on the 'Options' struct in 'crates/ty_project/src/metadata/options.rs' if you want to change anything here. -->\n\n",
|
||||
);
|
||||
|
||||
generate_set(
|
||||
&mut output,
|
||||
Set::Toplevel(Options::metadata()),
|
||||
@@ -247,7 +251,7 @@ mod tests {
|
||||
|
||||
use crate::generate_all::Mode;
|
||||
|
||||
use super::{main, Args};
|
||||
use super::{Args, main};
|
||||
|
||||
#[test]
|
||||
fn ty_configuration_markdown_up_to_date() -> Result<()> {
|
||||
|
||||
@@ -5,12 +5,12 @@ use std::fmt::Write as _;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use anyhow::{Result, bail};
|
||||
use itertools::Itertools as _;
|
||||
use pretty_assertions::StrComparison;
|
||||
|
||||
use crate::generate_all::{Mode, REGENERATE_ALL_COMMAND};
|
||||
use crate::ROOT_DIR;
|
||||
use crate::generate_all::{Mode, REGENERATE_ALL_COMMAND};
|
||||
|
||||
#[derive(clap::Args)]
|
||||
pub(crate) struct Args {
|
||||
@@ -56,6 +56,10 @@ fn generate_markdown() -> String {
|
||||
|
||||
let mut output = String::new();
|
||||
|
||||
let _ = writeln!(
|
||||
&mut output,
|
||||
"<!-- WARNING: This file is auto-generated (cargo dev generate-all). Edit the lint-declarations in 'crates/ty_python_semantic/src/types/diagnostic.rs' if you want to change anything here. -->\n"
|
||||
);
|
||||
let _ = writeln!(&mut output, "# Rules\n");
|
||||
|
||||
let mut lints: Vec<_> = registry.lints().iter().collect();
|
||||
@@ -134,7 +138,7 @@ mod tests {
|
||||
|
||||
use crate::generate_all::Mode;
|
||||
|
||||
use super::{main, Args};
|
||||
use super::{Args, main};
|
||||
|
||||
#[test]
|
||||
fn ty_rules_up_to_date() -> Result<()> {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use anyhow::{Result, bail};
|
||||
use pretty_assertions::StrComparison;
|
||||
use schemars::schema_for;
|
||||
|
||||
use crate::generate_all::{Mode, REGENERATE_ALL_COMMAND};
|
||||
use crate::ROOT_DIR;
|
||||
use crate::generate_all::{Mode, REGENERATE_ALL_COMMAND};
|
||||
use ty_project::metadata::options::Options;
|
||||
|
||||
#[derive(clap::Args)]
|
||||
@@ -56,7 +56,7 @@ mod tests {
|
||||
|
||||
use crate::generate_all::Mode;
|
||||
|
||||
use super::{main, Args};
|
||||
use super::{Args, main};
|
||||
|
||||
#[test]
|
||||
fn test_generate_json_schema() -> Result<()> {
|
||||
|
||||
@@ -6,7 +6,7 @@ use anyhow::Result;
|
||||
|
||||
use ruff_linter::source_kind::SourceKind;
|
||||
use ruff_python_ast::PySourceType;
|
||||
use ruff_python_parser::{parse, ParseOptions};
|
||||
use ruff_python_parser::{ParseOptions, parse};
|
||||
|
||||
#[derive(clap::Args)]
|
||||
pub(crate) struct Args {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use anyhow::{Result, bail};
|
||||
|
||||
#[derive(clap::Args)]
|
||||
pub(crate) struct Args {
|
||||
|
||||
@@ -16,7 +16,5 @@ doctest = false
|
||||
[dependencies]
|
||||
ruff_text_size = { workspace = true }
|
||||
|
||||
anyhow = { workspace = true }
|
||||
log = { workspace = true }
|
||||
is-macro = { workspace = true }
|
||||
serde = { workspace = true, optional = true, features = [] }
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
pub use diagnostic::{Diagnostic, DiagnosticKind};
|
||||
pub use edit::Edit;
|
||||
pub use fix::{Applicability, Fix, IsolationLevel};
|
||||
pub use source_map::{SourceMap, SourceMarker};
|
||||
pub use violation::{AlwaysFixableViolation, FixAvailability, Violation, ViolationMetadata};
|
||||
|
||||
mod diagnostic;
|
||||
mod edit;
|
||||
mod fix;
|
||||
mod source_map;
|
||||
mod violation;
|
||||
|
||||
@@ -17,7 +17,7 @@ impl<'fmt, Context> Argument<'fmt, Context> {
|
||||
/// Called by the [ruff_formatter::format_args] macro.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
pub fn new<F: Format<Context>>(value: &'fmt F) -> Self {
|
||||
pub const fn new<F: Format<Context>>(value: &'fmt F) -> Self {
|
||||
Self { value }
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ pub struct Arguments<'fmt, Context>(pub &'fmt [Argument<'fmt, Context>]);
|
||||
impl<'fmt, Context> Arguments<'fmt, Context> {
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
pub fn new(arguments: &'fmt [Argument<'fmt, Context>]) -> Self {
|
||||
pub const fn new(arguments: &'fmt [Argument<'fmt, Context>]) -> Self {
|
||||
Self(arguments)
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ impl<'fmt, Context> From<&'fmt Argument<'fmt, Context>> for Arguments<'fmt, Cont
|
||||
mod tests {
|
||||
use crate::format_element::tag::Tag;
|
||||
use crate::prelude::*;
|
||||
use crate::{format_args, write, FormatState, VecBuffer};
|
||||
use crate::{FormatState, VecBuffer, format_args, write};
|
||||
|
||||
#[test]
|
||||
fn test_nesting() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::{write, Arguments, FormatElement};
|
||||
use super::{Arguments, FormatElement, write};
|
||||
use crate::format_element::Interned;
|
||||
use crate::prelude::{LineMode, Tag};
|
||||
use crate::{FormatResult, FormatState};
|
||||
|
||||
@@ -2,14 +2,14 @@ use std::cell::Cell;
|
||||
use std::marker::PhantomData;
|
||||
use std::num::NonZeroU8;
|
||||
|
||||
use ruff_text_size::TextRange;
|
||||
#[allow(clippy::enum_glob_use)]
|
||||
use Tag::*;
|
||||
use ruff_text_size::TextRange;
|
||||
|
||||
use crate::format_element::tag::{Condition, Tag};
|
||||
use crate::prelude::tag::{DedentMode, GroupMode, LabelId};
|
||||
use crate::prelude::*;
|
||||
use crate::{write, Argument, Arguments, FormatContext, FormatOptions, GroupId, TextSize};
|
||||
use crate::{Argument, Arguments, FormatContext, FormatOptions, GroupId, TextSize, write};
|
||||
use crate::{Buffer, VecBuffer};
|
||||
|
||||
/// A line break that only gets printed if the enclosing `Group` doesn't fit on a single line.
|
||||
@@ -402,7 +402,10 @@ where
|
||||
}
|
||||
|
||||
fn debug_assert_no_newlines(text: &str) {
|
||||
debug_assert!(!text.contains('\r'), "The content '{text}' contains an unsupported '\\r' line terminator character but text must only use line feeds '\\n' as line separator. Use '\\n' instead of '\\r' and '\\r\\n' to insert a line break in strings.");
|
||||
debug_assert!(
|
||||
!text.contains('\r'),
|
||||
"The content '{text}' contains an unsupported '\\r' line terminator character but text must only use line feeds '\\n' as line separator. Use '\\n' instead of '\\r' and '\\r\\n' to insert a line break in strings."
|
||||
);
|
||||
}
|
||||
|
||||
/// Pushes some content to the end of the current line.
|
||||
@@ -1388,7 +1391,7 @@ pub fn soft_space_or_block_indent<Context>(content: &impl Format<Context>) -> Bl
|
||||
pub fn group<Context>(content: &impl Format<Context>) -> Group<Context> {
|
||||
Group {
|
||||
content: Argument::new(content),
|
||||
group_id: None,
|
||||
id: None,
|
||||
should_expand: false,
|
||||
}
|
||||
}
|
||||
@@ -1396,14 +1399,14 @@ pub fn group<Context>(content: &impl Format<Context>) -> Group<Context> {
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Group<'a, Context> {
|
||||
content: Argument<'a, Context>,
|
||||
group_id: Option<GroupId>,
|
||||
id: Option<GroupId>,
|
||||
should_expand: bool,
|
||||
}
|
||||
|
||||
impl<Context> Group<'_, Context> {
|
||||
#[must_use]
|
||||
pub fn with_group_id(mut self, group_id: Option<GroupId>) -> Self {
|
||||
self.group_id = group_id;
|
||||
pub fn with_id(mut self, group_id: Option<GroupId>) -> Self {
|
||||
self.id = group_id;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -1429,7 +1432,7 @@ impl<Context> Format<Context> for Group<'_, Context> {
|
||||
};
|
||||
|
||||
f.write_element(FormatElement::Tag(StartGroup(
|
||||
tag::Group::new().with_id(self.group_id).with_mode(mode),
|
||||
tag::Group::new().with_id(self.id).with_mode(mode),
|
||||
)));
|
||||
|
||||
Arguments::from(&self.content).fmt(f)?;
|
||||
@@ -1443,7 +1446,7 @@ impl<Context> Format<Context> for Group<'_, Context> {
|
||||
impl<Context> std::fmt::Debug for Group<'_, Context> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Group")
|
||||
.field("group_id", &self.group_id)
|
||||
.field("id", &self.id)
|
||||
.field("should_expand", &self.should_expand)
|
||||
.field("content", &"{{content}}")
|
||||
.finish()
|
||||
@@ -1642,7 +1645,7 @@ impl<Context> std::fmt::Debug for BestFitParenthesize<'_, Context> {
|
||||
/// soft_line_break(),
|
||||
/// if_group_breaks(&token(")"))
|
||||
/// ])
|
||||
/// .with_group_id(Some(parentheses_id))
|
||||
/// .with_id(Some(parentheses_id))
|
||||
/// .fmt(f)
|
||||
/// });
|
||||
///
|
||||
@@ -1991,7 +1994,7 @@ impl<Context> IfGroupBreaks<'_, Context> {
|
||||
/// })),
|
||||
/// token("]")
|
||||
/// ],
|
||||
/// ).with_group_id(Some(group_id))
|
||||
/// ).with_id(Some(group_id))
|
||||
/// ])
|
||||
/// })])?;
|
||||
///
|
||||
@@ -2046,7 +2049,7 @@ impl<Context> std::fmt::Debug for IfGroupBreaks<'_, Context> {
|
||||
/// let id = f.group_id("head");
|
||||
///
|
||||
/// write!(f, [
|
||||
/// group(&token("Head")).with_group_id(Some(id)),
|
||||
/// group(&token("Head")).with_id(Some(id)),
|
||||
/// if_group_breaks(&indent(&token("indented"))).with_group_id(Some(id)),
|
||||
/// if_group_fits_on_line(&token("indented")).with_group_id(Some(id))
|
||||
/// ])
|
||||
@@ -2071,7 +2074,7 @@ impl<Context> std::fmt::Debug for IfGroupBreaks<'_, Context> {
|
||||
/// let group_id = f.group_id("header");
|
||||
///
|
||||
/// write!(f, [
|
||||
/// group(&token("(aLongHeaderThatBreaksForSomeReason) =>")).with_group_id(Some(group_id)),
|
||||
/// group(&token("(aLongHeaderThatBreaksForSomeReason) =>")).with_id(Some(group_id)),
|
||||
/// indent_if_group_breaks(&format_args![hard_line_break(), token("a => b")], group_id)
|
||||
/// ])
|
||||
/// });
|
||||
@@ -2101,7 +2104,7 @@ impl<Context> std::fmt::Debug for IfGroupBreaks<'_, Context> {
|
||||
/// let group_id = f.group_id("header");
|
||||
///
|
||||
/// write!(f, [
|
||||
/// group(&token("(aLongHeaderThatBreaksForSomeReason) =>")).with_group_id(Some(group_id)),
|
||||
/// group(&token("(aLongHeaderThatBreaksForSomeReason) =>")).with_id(Some(group_id)),
|
||||
/// indent_if_group_breaks(&format_args![hard_line_break(), token("a => b")], group_id)
|
||||
/// ])
|
||||
/// });
|
||||
@@ -2564,7 +2567,7 @@ impl<'a, Context> BestFitting<'a, Context> {
|
||||
/// # Panics
|
||||
///
|
||||
/// When the slice contains less than two variants.
|
||||
pub fn from_arguments_unchecked(variants: Arguments<'a, Context>) -> Self {
|
||||
pub const fn from_arguments_unchecked(variants: Arguments<'a, Context>) -> Self {
|
||||
assert!(
|
||||
variants.0.len() >= 2,
|
||||
"Requires at least the least expanded and most expanded variants"
|
||||
@@ -2572,7 +2575,7 @@ impl<'a, Context> BestFitting<'a, Context> {
|
||||
|
||||
Self {
|
||||
variants,
|
||||
mode: BestFittingMode::default(),
|
||||
mode: BestFittingMode::FirstLine,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::prelude::TagKind;
|
||||
use crate::GroupId;
|
||||
use crate::prelude::TagKind;
|
||||
use ruff_text_size::TextRange;
|
||||
use std::error::Error;
|
||||
|
||||
@@ -29,16 +29,22 @@ pub enum FormatError {
|
||||
impl std::fmt::Display for FormatError {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
FormatError::SyntaxError {message} => {
|
||||
FormatError::SyntaxError { message } => {
|
||||
std::write!(fmt, "syntax error: {message}")
|
||||
},
|
||||
}
|
||||
FormatError::RangeError { input, tree } => std::write!(
|
||||
fmt,
|
||||
"formatting range {input:?} is larger than syntax tree {tree:?}"
|
||||
),
|
||||
FormatError::InvalidDocument(error) => std::write!(fmt, "Invalid document: {error}\n\n This is an internal Rome error. Please report if necessary."),
|
||||
FormatError::InvalidDocument(error) => std::write!(
|
||||
fmt,
|
||||
"Invalid document: {error}\n\n This is an internal Rome error. Please report if necessary."
|
||||
),
|
||||
FormatError::PoorLayout => {
|
||||
std::write!(fmt, "Poor layout: The formatter wasn't able to pick a good layout for your document. This is an internal Rome error. Please report if necessary.")
|
||||
std::write!(
|
||||
fmt,
|
||||
"Poor layout: The formatter wasn't able to pick a good layout for your document. This is an internal Rome error. Please report if necessary."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,24 +145,37 @@ impl std::fmt::Display for InvalidDocumentError {
|
||||
InvalidDocumentError::ExpectedStart {
|
||||
expected_start,
|
||||
actual,
|
||||
} => {
|
||||
match actual {
|
||||
ActualStart::EndOfDocument => {
|
||||
std::write!(f, "Expected start tag of kind {expected_start:?} but at the end of document.")
|
||||
}
|
||||
ActualStart::Start(start) => {
|
||||
std::write!(f, "Expected start tag of kind {expected_start:?} but found start tag of kind {start:?}.")
|
||||
}
|
||||
ActualStart::End(end) => {
|
||||
std::write!(f, "Expected start tag of kind {expected_start:?} but found end tag of kind {end:?}.")
|
||||
}
|
||||
ActualStart::Content => {
|
||||
std::write!(f, "Expected start tag of kind {expected_start:?} but found non-tag element.")
|
||||
}
|
||||
} => match actual {
|
||||
ActualStart::EndOfDocument => {
|
||||
std::write!(
|
||||
f,
|
||||
"Expected start tag of kind {expected_start:?} but at the end of document."
|
||||
)
|
||||
}
|
||||
}
|
||||
ActualStart::Start(start) => {
|
||||
std::write!(
|
||||
f,
|
||||
"Expected start tag of kind {expected_start:?} but found start tag of kind {start:?}."
|
||||
)
|
||||
}
|
||||
ActualStart::End(end) => {
|
||||
std::write!(
|
||||
f,
|
||||
"Expected start tag of kind {expected_start:?} but found end tag of kind {end:?}."
|
||||
)
|
||||
}
|
||||
ActualStart::Content => {
|
||||
std::write!(
|
||||
f,
|
||||
"Expected start tag of kind {expected_start:?} but found non-tag element."
|
||||
)
|
||||
}
|
||||
},
|
||||
InvalidDocumentError::UnknownGroupId { group_id } => {
|
||||
std::write!(f, "Encountered unknown group id {group_id:?}. Ensure that the group with the id {group_id:?} exists and that the group is a parent of or comes before the element referring to it.")
|
||||
std::write!(
|
||||
f,
|
||||
"Encountered unknown group id {group_id:?}. Ensure that the group with the id {group_id:?} exists and that the group is a parent of or comes before the element referring to it."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -543,7 +543,7 @@ impl TextWidth {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use crate::format_element::{normalize_newlines, LINE_TERMINATORS};
|
||||
use crate::format_element::{LINE_TERMINATORS, normalize_newlines};
|
||||
|
||||
#[test]
|
||||
fn test_normalize_newlines() {
|
||||
|
||||
@@ -8,8 +8,8 @@ use crate::prelude::tag::GroupMode;
|
||||
use crate::prelude::*;
|
||||
use crate::source_code::SourceCode;
|
||||
use crate::{
|
||||
format, write, BufferExtensions, Format, FormatContext, FormatElement, FormatOptions,
|
||||
FormatResult, Formatter, IndentStyle, IndentWidth, LineWidth, PrinterOptions,
|
||||
BufferExtensions, Format, FormatContext, FormatElement, FormatOptions, FormatResult, Formatter,
|
||||
IndentStyle, IndentWidth, LineWidth, PrinterOptions, format, write,
|
||||
};
|
||||
|
||||
use super::tag::Tag;
|
||||
@@ -811,8 +811,8 @@ mod tests {
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{format, format_args, write};
|
||||
use crate::{SimpleFormatContext, SourceCode};
|
||||
use crate::{format, format_args, write};
|
||||
|
||||
#[test]
|
||||
fn display_elements() {
|
||||
|
||||
@@ -370,7 +370,10 @@ impl PartialEq for LabelId {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
if is_equal {
|
||||
assert_eq!(self.name, other.name, "Two `LabelId`s with different names have the same `value`. Are you mixing labels of two different `LabelDefinition` or are the values returned by the `LabelDefinition` not unique?");
|
||||
assert_eq!(
|
||||
self.name, other.name,
|
||||
"Two `LabelId`s with different names have the same `value`. Are you mixing labels of two different `LabelDefinition` or are the values returned by the `LabelDefinition` not unique?"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ use crate::prelude::TagKind;
|
||||
use std::fmt;
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::marker::PhantomData;
|
||||
use std::num::{NonZeroU16, NonZeroU8, TryFromIntError};
|
||||
use std::num::{NonZeroU8, NonZeroU16, TryFromIntError};
|
||||
|
||||
use crate::format_element::document::Document;
|
||||
use crate::printer::{Printer, PrinterOptions};
|
||||
@@ -50,7 +50,7 @@ pub use builders::BestFitting;
|
||||
pub use source_code::{SourceCode, SourceCodeSlice};
|
||||
|
||||
pub use crate::diagnostics::{ActualStart, FormatError, InvalidDocumentError, PrintError};
|
||||
pub use format_element::{normalize_newlines, FormatElement, LINE_TERMINATORS};
|
||||
pub use format_element::{FormatElement, LINE_TERMINATORS, normalize_newlines};
|
||||
pub use group_id::GroupId;
|
||||
use ruff_macros::CacheKey;
|
||||
use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||
|
||||
@@ -328,16 +328,16 @@ macro_rules! format {
|
||||
/// [`MostExpanded`]: crate::format_element::BestFittingVariants::most_expanded
|
||||
#[macro_export]
|
||||
macro_rules! best_fitting {
|
||||
($least_expanded:expr, $($tail:expr),+ $(,)?) => {{
|
||||
($least_expanded:expr, $($tail:expr),+ $(,)?) => {
|
||||
// OK because the macro syntax requires at least two variants.
|
||||
$crate::BestFitting::from_arguments_unchecked($crate::format_args!($least_expanded, $($tail),+))
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::prelude::*;
|
||||
use crate::{write, FormatState, SimpleFormatOptions, VecBuffer};
|
||||
use crate::{FormatState, SimpleFormatOptions, VecBuffer, write};
|
||||
|
||||
struct TestFormat;
|
||||
|
||||
@@ -386,7 +386,7 @@ mod tests {
|
||||
#[test]
|
||||
fn best_fitting_variants_print_as_lists() {
|
||||
use crate::prelude::*;
|
||||
use crate::{format, format_args, Formatted};
|
||||
use crate::{Formatted, format, format_args};
|
||||
|
||||
// The second variant below should be selected when printing at a width of 30
|
||||
let formatted_best_fitting = format!(
|
||||
@@ -398,34 +398,36 @@ mod tests {
|
||||
format_args![token(
|
||||
"Something that will not fit on a line with 30 character print width."
|
||||
)],
|
||||
format_args![group(&format_args![
|
||||
token("Start"),
|
||||
soft_line_break(),
|
||||
group(&soft_block_indent(&format_args![
|
||||
token("1,"),
|
||||
soft_line_break_or_space(),
|
||||
token("2,"),
|
||||
soft_line_break_or_space(),
|
||||
token("3"),
|
||||
])),
|
||||
soft_line_break_or_space(),
|
||||
soft_block_indent(&format_args![
|
||||
token("1,"),
|
||||
soft_line_break_or_space(),
|
||||
token("2,"),
|
||||
soft_line_break_or_space(),
|
||||
group(&format_args!(
|
||||
token("A,"),
|
||||
format_args![
|
||||
group(&format_args![
|
||||
token("Start"),
|
||||
soft_line_break(),
|
||||
group(&soft_block_indent(&format_args![
|
||||
token("1,"),
|
||||
soft_line_break_or_space(),
|
||||
token("B")
|
||||
)),
|
||||
token("2,"),
|
||||
soft_line_break_or_space(),
|
||||
token("3"),
|
||||
])),
|
||||
soft_line_break_or_space(),
|
||||
token("3")
|
||||
]),
|
||||
soft_line_break_or_space(),
|
||||
token("End")
|
||||
])
|
||||
.should_expand(true)],
|
||||
soft_block_indent(&format_args![
|
||||
token("1,"),
|
||||
soft_line_break_or_space(),
|
||||
token("2,"),
|
||||
soft_line_break_or_space(),
|
||||
group(&format_args!(
|
||||
token("A,"),
|
||||
soft_line_break_or_space(),
|
||||
token("B")
|
||||
)),
|
||||
soft_line_break_or_space(),
|
||||
token("3")
|
||||
]),
|
||||
soft_line_break_or_space(),
|
||||
token("End")
|
||||
])
|
||||
.should_expand(true)
|
||||
],
|
||||
format_args!(token("Most"), hard_line_break(), token("Expanded"))
|
||||
]
|
||||
]
|
||||
|
||||
@@ -7,6 +7,6 @@ pub use crate::formatter::Formatter;
|
||||
pub use crate::printer::PrinterOptions;
|
||||
|
||||
pub use crate::{
|
||||
best_fitting, dbg_write, format, format_args, write, Buffer as _, BufferExtensions, Format,
|
||||
Format as _, FormatResult, FormatRule, FormatWithRule as _, SimpleFormatContext,
|
||||
Buffer as _, BufferExtensions, Format, Format as _, FormatResult, FormatRule,
|
||||
FormatWithRule as _, SimpleFormatContext, best_fitting, dbg_write, format, format_args, write,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::format_element::tag::TagKind;
|
||||
use crate::format_element::PrintMode;
|
||||
use crate::format_element::tag::TagKind;
|
||||
use crate::printer::stack::{Stack, StackedStack};
|
||||
use crate::printer::{Indentation, MeasureMode};
|
||||
use crate::{IndentStyle, InvalidDocumentError, PrintError, PrintResult};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::printer::call_stack::PrintElementArgs;
|
||||
use crate::FormatElement;
|
||||
use crate::printer::call_stack::PrintElementArgs;
|
||||
|
||||
/// Stores the queued line suffixes.
|
||||
#[derive(Debug, Default)]
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::format_element::document::Document;
|
||||
use crate::format_element::tag::{Condition, GroupMode};
|
||||
use crate::format_element::{BestFittingMode, BestFittingVariants, LineMode, PrintMode};
|
||||
use crate::prelude::tag::{DedentMode, Tag, TagKind, VerbatimKind};
|
||||
use crate::prelude::{tag, TextWidth};
|
||||
use crate::prelude::{TextWidth, tag};
|
||||
use crate::printer::call_stack::{
|
||||
CallStack, FitsCallStack, PrintCallStack, PrintElementArgs, StackFrame,
|
||||
};
|
||||
@@ -1199,7 +1199,7 @@ impl<'a, 'print> FitsMeasurer<'a, 'print> {
|
||||
text_width: *text_width,
|
||||
},
|
||||
args,
|
||||
))
|
||||
));
|
||||
}
|
||||
FormatElement::SourceCodeSlice { slice, text_width } => {
|
||||
let text = slice.text(self.printer.source_code);
|
||||
@@ -1597,11 +1597,7 @@ enum Fits {
|
||||
|
||||
impl From<bool> for Fits {
|
||||
fn from(value: bool) -> Self {
|
||||
if value {
|
||||
Fits::Yes
|
||||
} else {
|
||||
Fits::No
|
||||
}
|
||||
if value { Fits::Yes } else { Fits::No }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1662,8 +1658,8 @@ mod tests {
|
||||
use crate::printer::{LineEnding, Printer, PrinterOptions};
|
||||
use crate::source_code::SourceCode;
|
||||
use crate::{
|
||||
format_args, write, Document, FormatState, IndentStyle, IndentWidth, LineWidth, Printed,
|
||||
VecBuffer,
|
||||
Document, FormatState, IndentStyle, IndentWidth, LineWidth, Printed, VecBuffer,
|
||||
format_args, write,
|
||||
};
|
||||
|
||||
fn format(root: &dyn Format<SimpleFormatContext>) -> Printed {
|
||||
@@ -1985,10 +1981,21 @@ two lines`,
|
||||
token("]")
|
||||
]),
|
||||
token(";"),
|
||||
line_suffix(&format_args![space(), token("// Using reserved width causes this content to not fit even though it's a line suffix element")], 93)
|
||||
line_suffix(
|
||||
&format_args![
|
||||
space(),
|
||||
token(
|
||||
"// Using reserved width causes this content to not fit even though it's a line suffix element"
|
||||
)
|
||||
],
|
||||
93
|
||||
)
|
||||
]);
|
||||
|
||||
assert_eq!(printed.as_code(), "[\n 1, 2, 3\n]; // Using reserved width causes this content to not fit even though it's a line suffix element");
|
||||
assert_eq!(
|
||||
printed.as_code(),
|
||||
"[\n 1, 2, 3\n]; // Using reserved width causes this content to not fit even though it's a line suffix element"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2002,7 +2009,7 @@ two lines`,
|
||||
token("The referenced group breaks."),
|
||||
hard_line_break()
|
||||
])
|
||||
.with_group_id(Some(group_id)),
|
||||
.with_id(Some(group_id)),
|
||||
group(&format_args![
|
||||
token("This group breaks because:"),
|
||||
soft_line_break_or_space(),
|
||||
@@ -2015,7 +2022,10 @@ two lines`,
|
||||
|
||||
let printed = format(&content);
|
||||
|
||||
assert_eq!(printed.as_code(), "The referenced group breaks.\nThis group breaks because:\nIt measures with the 'if_group_breaks' variant because the referenced group breaks and that's just way too much text.");
|
||||
assert_eq!(
|
||||
printed.as_code(),
|
||||
"The referenced group breaks.\nThis group breaks because:\nIt measures with the 'if_group_breaks' variant because the referenced group breaks and that's just way too much text."
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2027,7 +2037,7 @@ two lines`,
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
group(&token("Group with id-2")).with_group_id(Some(id_2)),
|
||||
group(&token("Group with id-2")).with_id(Some(id_2)),
|
||||
hard_line_break()
|
||||
]
|
||||
)?;
|
||||
@@ -2035,7 +2045,7 @@ two lines`,
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
group(&token("Group with id-1 does not fit on the line because it exceeds the line width of 80 characters by")).with_group_id(Some(id_1)),
|
||||
group(&token("Group with id-1 does not fit on the line because it exceeds the line width of 80 characters by")).with_id(Some(id_1)),
|
||||
hard_line_break()
|
||||
]
|
||||
)?;
|
||||
|
||||
@@ -325,10 +325,10 @@ impl FitsEndPredicate for SingleEntryPredicate {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::FormatElement;
|
||||
use crate::format_element::LineMode;
|
||||
use crate::prelude::Tag;
|
||||
use crate::printer::queue::{PrintQueue, Queue};
|
||||
use crate::FormatElement;
|
||||
|
||||
#[test]
|
||||
fn extend_back_pop_last() {
|
||||
|
||||
@@ -65,7 +65,10 @@ pub struct SourceCodeSlice {
|
||||
impl SourceCodeSlice {
|
||||
/// Returns the slice's text.
|
||||
pub fn text<'a>(&self, code: SourceCode<'a>) -> &'a str {
|
||||
assert!(usize::from(self.range.end()) <= code.text.len(), "The range of this slice is out of bounds. Did you provide the correct source code for this slice?");
|
||||
assert!(
|
||||
usize::from(self.range.end()) <= code.text.len(),
|
||||
"The range of this slice is out of bounds. Did you provide the correct source code for this slice?"
|
||||
);
|
||||
&code.text[self.range]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use ruff_python_ast::visitor::source_order::{
|
||||
walk_expr, walk_module, walk_stmt, SourceOrderVisitor,
|
||||
SourceOrderVisitor, walk_expr, walk_module, walk_stmt,
|
||||
};
|
||||
use ruff_python_ast::{self as ast, Expr, Mod, Stmt};
|
||||
use ty_python_semantic::ModuleName;
|
||||
|
||||
@@ -9,8 +9,8 @@ use ruff_db::{Db as SourceDb, Upcast};
|
||||
use ruff_python_ast::PythonVersion;
|
||||
use ty_python_semantic::lint::{LintRegistry, RuleSelection};
|
||||
use ty_python_semantic::{
|
||||
default_lint_registry, Db, Program, ProgramSettings, PythonPath, PythonPlatform,
|
||||
SearchPathSettings,
|
||||
Db, Program, ProgramSettings, PythonPath, PythonPlatform, PythonVersionSource,
|
||||
PythonVersionWithSource, SearchPathSettings, SysPrefixPathOrigin, default_lint_registry,
|
||||
};
|
||||
|
||||
static EMPTY_VENDORED: std::sync::LazyLock<VendoredFileSystem> = std::sync::LazyLock::new(|| {
|
||||
@@ -37,14 +37,18 @@ impl ModuleDb {
|
||||
) -> Result<Self> {
|
||||
let mut search_paths = SearchPathSettings::new(src_roots);
|
||||
if let Some(venv_path) = venv_path {
|
||||
search_paths.python_path = PythonPath::from_cli_flag(venv_path);
|
||||
search_paths.python_path =
|
||||
PythonPath::sys_prefix(venv_path, SysPrefixPathOrigin::PythonCliFlag);
|
||||
}
|
||||
|
||||
let db = Self::default();
|
||||
Program::from_settings(
|
||||
&db,
|
||||
ProgramSettings {
|
||||
python_version,
|
||||
python_version: Some(PythonVersionWithSource {
|
||||
version: python_version,
|
||||
source: PythonVersionSource::default(),
|
||||
}),
|
||||
python_platform: PythonPlatform::default(),
|
||||
search_paths,
|
||||
},
|
||||
|
||||
@@ -4,7 +4,7 @@ use anyhow::Result;
|
||||
|
||||
use ruff_db::system::{SystemPath, SystemPathBuf};
|
||||
use ruff_python_ast::helpers::to_module_path;
|
||||
use ruff_python_parser::{parse, Mode, ParseOptions};
|
||||
use ruff_python_parser::{Mode, ParseOptions, parse};
|
||||
|
||||
use crate::collector::Collector;
|
||||
pub use crate::db::ModuleDb;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use ruff_db::files::FilePath;
|
||||
use ty_python_semantic::resolve_module;
|
||||
|
||||
use crate::collector::CollectedImport;
|
||||
use crate::ModuleDb;
|
||||
use crate::collector::CollectedImport;
|
||||
|
||||
/// Collect all imports for a given Python file.
|
||||
pub(crate) struct Resolver<'a> {
|
||||
@@ -19,19 +19,20 @@ impl<'a> Resolver<'a> {
|
||||
pub(crate) fn resolve(&self, import: CollectedImport) -> Option<&'a FilePath> {
|
||||
match import {
|
||||
CollectedImport::Import(import) => {
|
||||
resolve_module(self.db, &import).map(|module| module.file().path(self.db))
|
||||
let module = resolve_module(self.db, &import)?;
|
||||
Some(module.file()?.path(self.db))
|
||||
}
|
||||
CollectedImport::ImportFrom(import) => {
|
||||
// Attempt to resolve the member (e.g., given `from foo import bar`, look for `foo.bar`).
|
||||
let parent = import.parent();
|
||||
|
||||
resolve_module(self.db, &import)
|
||||
.map(|module| module.file().path(self.db))
|
||||
.or_else(|| {
|
||||
// Attempt to resolve the module (e.g., given `from foo import bar`, look for `foo`).
|
||||
let module = resolve_module(self.db, &import).or_else(|| {
|
||||
// Attempt to resolve the module (e.g., given `from foo import bar`, look for `foo`).
|
||||
|
||||
resolve_module(self.db, &parent?).map(|module| module.file().path(self.db))
|
||||
})
|
||||
resolve_module(self.db, &parent?)
|
||||
})?;
|
||||
|
||||
Some(module.file()?.path(self.db))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::vec::IndexVec;
|
||||
use crate::Idx;
|
||||
use crate::vec::IndexVec;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{Index, IndexMut, Range};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user