Compare commits

..

1 Commits

Author SHA1 Message Date
Charlie Marsh
e187c66573 Try a bit set 2022-11-18 22:20:41 -05:00
341 changed files with 4382 additions and 11131 deletions

View File

@@ -82,7 +82,7 @@ jobs:
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- run: cargo clippy --workspace --all-targets --all-features -- -D warnings -W clippy::pedantic
- run: cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo_test:
name: "cargo test"

View File

@@ -34,6 +34,7 @@ jobs:
with:
target: x86_64
args: --release --out dist --sdist -m ./${{ env.CRATE_NAME }}/Cargo.toml
maturin-version: "v0.13.0"
- name: Install built wheel - x86_64
run: |
pip install dist/${{ env.CRATE_NAME }}-*.whl --force-reinstall
@@ -60,6 +61,7 @@ jobs:
uses: messense/maturin-action@v1
with:
args: --release --universal2 --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
maturin-version: "v0.13.0"
- name: Install built wheel - universal2
run: |
pip install dist/${{ env.CRATE_NAME }}-*universal2.whl --force-reinstall
@@ -91,6 +93,7 @@ jobs:
with:
target: ${{ matrix.target }}
args: --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
maturin-version: "v0.13.0"
- name: Install built wheel
shell: bash
run: |
@@ -118,6 +121,7 @@ jobs:
target: ${{ matrix.target }}
manylinux: auto
args: --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
maturin-version: "v0.13.0"
- name: Install built wheel
if: matrix.target == 'x86_64'
run: |
@@ -144,6 +148,7 @@ jobs:
target: ${{ matrix.target }}
manylinux: auto
args: --no-default-features --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
maturin-version: "v0.13.0"
- uses: uraimo/run-on-arch-action@v2.0.5
if: matrix.target != 'ppc64'
name: Install built wheel
@@ -182,6 +187,7 @@ jobs:
target: ${{ matrix.target }}
manylinux: musllinux_1_2
args: --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
maturin-version: "v0.13.0"
- name: Install built wheel
if: matrix.target == 'x86_64-unknown-linux-musl'
uses: addnab/docker-run-action@v3
@@ -217,6 +223,7 @@ jobs:
target: ${{ matrix.platform.target }}
manylinux: musllinux_1_2
args: --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
maturin-version: "v0.13.0"
- uses: uraimo/run-on-arch-action@master
name: Install built wheel
with:
@@ -254,6 +261,7 @@ jobs:
- name: Build wheels
uses: messense/maturin-action@v1
with:
maturin-version: "v0.13.0"
target: ${{ matrix.target }}
manylinux: auto
args: --release --out dist -i pypy${{ matrix.python-version }} -m ./${{ env.CRATE_NAME }}/Cargo.toml

View File

@@ -36,6 +36,7 @@ jobs:
with:
target: x86_64
args: --release --out dist --sdist
maturin-version: "v0.13.0"
- name: Install built wheel - x86_64
run: |
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
@@ -62,6 +63,7 @@ jobs:
uses: messense/maturin-action@v1
with:
args: --release --universal2 --out dist
maturin-version: "v0.13.0"
- name: Install built wheel - universal2
run: |
pip install dist/${{ env.PACKAGE_NAME }}-*universal2.whl --force-reinstall
@@ -93,6 +95,7 @@ jobs:
with:
target: ${{ matrix.target }}
args: --release --out dist
maturin-version: "v0.13.0"
- name: Install built wheel
shell: bash
run: |
@@ -120,6 +123,7 @@ jobs:
target: ${{ matrix.target }}
manylinux: auto
args: --release --out dist
maturin-version: "v0.13.0"
- name: Install built wheel
if: matrix.target == 'x86_64'
run: |
@@ -146,6 +150,7 @@ jobs:
target: ${{ matrix.target }}
manylinux: auto
args: --no-default-features --release --out dist
maturin-version: "v0.13.0"
- uses: uraimo/run-on-arch-action@v2.0.5
if: matrix.target != 'ppc64'
name: Install built wheel
@@ -184,6 +189,7 @@ jobs:
target: ${{ matrix.target }}
manylinux: musllinux_1_2
args: --release --out dist
maturin-version: "v0.13.0"
- name: Install built wheel
if: matrix.target == 'x86_64-unknown-linux-musl'
uses: addnab/docker-run-action@v3
@@ -219,6 +225,7 @@ jobs:
target: ${{ matrix.platform.target }}
manylinux: musllinux_1_2
args: --release --out dist
maturin-version: "v0.13.0"
- uses: uraimo/run-on-arch-action@master
name: Install built wheel
with:
@@ -256,6 +263,7 @@ jobs:
- name: Build wheels
uses: messense/maturin-action@v1
with:
maturin-version: "v0.13.0"
target: ${{ matrix.target }}
manylinux: auto
args: --release --out dist -i pypy${{ matrix.python-version }}

View File

@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.145
rev: v0.0.128
hooks:
- id: ruff

698
Cargo.lock generated
View File

@@ -94,6 +94,133 @@ dependencies = [
"wait-timeout",
]
[[package]]
name = "async-channel"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28"
dependencies = [
"concurrent-queue",
"event-listener",
"futures-core",
]
[[package]]
name = "async-executor"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965"
dependencies = [
"async-task",
"concurrent-queue",
"fastrand",
"futures-lite",
"once_cell",
"slab",
]
[[package]]
name = "async-global-executor"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776"
dependencies = [
"async-channel",
"async-executor",
"async-io",
"async-lock",
"blocking",
"futures-lite",
"once_cell",
]
[[package]]
name = "async-io"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8121296a9f05be7f34aa4196b1747243b3b62e048bb7906f644f3fbfc490cf7"
dependencies = [
"async-lock",
"autocfg",
"concurrent-queue",
"futures-lite",
"libc",
"log",
"parking",
"polling",
"slab",
"socket2",
"waker-fn",
"winapi 0.3.9",
]
[[package]]
name = "async-lock"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685"
dependencies = [
"event-listener",
"futures-lite",
]
[[package]]
name = "async-process"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02111fd8655a613c25069ea89fc8d9bb89331fa77486eb3bc059ee757cfa481c"
dependencies = [
"async-io",
"autocfg",
"blocking",
"cfg-if 1.0.0",
"event-listener",
"futures-lite",
"libc",
"once_cell",
"signal-hook",
"winapi 0.3.9",
]
[[package]]
name = "async-std"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d"
dependencies = [
"async-channel",
"async-global-executor",
"async-io",
"async-lock",
"async-process",
"crossbeam-utils",
"futures-channel",
"futures-core",
"futures-io",
"futures-lite",
"gloo-timers",
"kv-log-macro",
"log",
"memchr",
"once_cell",
"pin-project-lite",
"pin-utils",
"slab",
"wasm-bindgen-futures",
]
[[package]]
name = "async-task"
version = "4.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524"
[[package]]
name = "atomic-waker"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a"
[[package]]
name = "atty"
version = "0.2.14"
@@ -111,6 +238,15 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "base64"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
dependencies = [
"byteorder",
]
[[package]]
name = "base64"
version = "0.13.1"
@@ -147,6 +283,50 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "block-buffer"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
dependencies = [
"block-padding",
"byte-tools",
"byteorder",
"generic-array 0.12.4",
]
[[package]]
name = "block-buffer"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
dependencies = [
"generic-array 0.14.6",
]
[[package]]
name = "block-padding"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
dependencies = [
"byte-tools",
]
[[package]]
name = "blocking"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc"
dependencies = [
"async-channel",
"async-task",
"atomic-waker",
"fastrand",
"futures-lite",
"once_cell",
]
[[package]]
name = "bstr"
version = "0.2.17"
@@ -177,14 +357,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
[[package]]
name = "cachedir"
version = "0.3.0"
name = "byte-tools"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e236bf5873ea57ec2877445297f4da008916bfae51567131acfc54a073d694f3"
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cacache"
version = "10.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c13caedf5b624de6448b78e980320a27266557350198dc67cf64bc8561c27e8"
dependencies = [
"async-std",
"digest 0.9.0",
"either",
"futures",
"hex 0.4.3",
"memmap2",
"serde",
"serde_derive",
"serde_json",
"sha-1 0.9.8",
"sha2 0.9.9",
"ssri",
"tempfile",
"thiserror",
"walkdir",
]
[[package]]
name = "cache-padded"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c"
[[package]]
name = "cast"
version = "0.3.0"
@@ -290,36 +502,6 @@ dependencies = [
"termcolor",
]
[[package]]
name = "clap_complete"
version = "4.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7b3c9eae0de7bf8e3f904a5e40612b21fb2e2e566456d177809a48b892d24da"
dependencies = [
"clap 4.0.22",
]
[[package]]
name = "clap_complete_command"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4160b4a4f72ef58bd766bad27c09e6ef1cc9d82a22f6a0f55d152985a4a48e31"
dependencies = [
"clap 4.0.22",
"clap_complete",
"clap_complete_fig",
]
[[package]]
name = "clap_complete_fig"
version = "4.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46b30e010e669cd021e5004f3be26cff6b7c08d2a8a0d65b48d43a8cc0efd6c3"
dependencies = [
"clap 4.0.22",
"clap_complete",
]
[[package]]
name = "clap_derive"
version = "4.0.21"
@@ -400,6 +582,15 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2382f75942f4b3be3690fe4f86365e9c853c1587d6ee58212cebf6e2a9ccd101"
[[package]]
name = "concurrent-queue"
version = "1.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c"
dependencies = [
"cache-padded",
]
[[package]]
name = "configparser"
version = "3.0.2"
@@ -425,6 +616,15 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]]
name = "cpufeatures"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.3.2"
@@ -519,6 +719,16 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
name = "ctor"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "cxx"
version = "1.0.81"
@@ -575,6 +785,24 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
[[package]]
name = "digest"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
dependencies = [
"generic-array 0.12.4",
]
[[package]]
name = "digest"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
dependencies = [
"generic-array 0.14.6",
]
[[package]]
name = "directories"
version = "4.0.1"
@@ -662,6 +890,18 @@ version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
name = "event-listener"
version = "2.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]]
name = "fake-simd"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]]
name = "fastrand"
version = "1.8.0"
@@ -700,15 +940,15 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8-to-ruff"
version = "0.0.145-dev.0"
version = "0.0.128-dev.0"
dependencies = [
"anyhow",
"clap 4.0.22",
"configparser",
"fnv",
"once_cell",
"regex",
"ruff",
"rustc-hash",
"serde",
"serde_json",
"toml",
@@ -774,6 +1014,129 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
[[package]]
name = "futures"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
[[package]]
name = "futures-executor"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
[[package]]
name = "futures-lite"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48"
dependencies = [
"fastrand",
"futures-core",
"futures-io",
"memchr",
"parking",
"pin-project-lite",
"waker-fn",
]
[[package]]
name = "futures-macro"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
[[package]]
name = "futures-task"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
[[package]]
name = "futures-util"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
name = "generic-array"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
dependencies = [
"typenum",
]
[[package]]
name = "generic-array"
version = "0.14.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.1.16"
@@ -799,16 +1162,21 @@ dependencies = [
]
[[package]]
name = "globset"
version = "0.4.9"
name = "glob"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "gloo-timers"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9"
dependencies = [
"aho-corasick",
"bstr 0.2.17",
"fnv",
"log",
"regex",
"futures-channel",
"futures-core",
"js-sys",
"wasm-bindgen",
]
[[package]]
@@ -841,6 +1209,18 @@ dependencies = [
"libc",
]
[[package]]
name = "hex"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hexf-parse"
version = "0.2.1"
@@ -983,6 +1363,15 @@ dependencies = [
"winapi-build",
]
[[package]]
name = "kv-log-macro"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
dependencies = [
"log",
]
[[package]]
name = "lalrpop"
version = "0.19.8"
@@ -1066,7 +1455,7 @@ checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
[[package]]
name = "libcst"
version = "0.1.0"
source = "git+https://github.com/charliermarsh/LibCST?rev=f2f0b7a487a8725d161fe8b3ed73a6758b21e177#f2f0b7a487a8725d161fe8b3ed73a6758b21e177"
source = "git+https://github.com/charliermarsh/LibCST?rev=a13ec97dd4eb925bde4d426c6e422582793b260c#a13ec97dd4eb925bde4d426c6e422582793b260c"
dependencies = [
"chic",
"itertools",
@@ -1081,7 +1470,7 @@ dependencies = [
[[package]]
name = "libcst_derive"
version = "0.1.0"
source = "git+https://github.com/charliermarsh/LibCST?rev=f2f0b7a487a8725d161fe8b3ed73a6758b21e177#f2f0b7a487a8725d161fe8b3ed73a6758b21e177"
source = "git+https://github.com/charliermarsh/LibCST?rev=a13ec97dd4eb925bde4d426c6e422582793b260c#a13ec97dd4eb925bde4d426c6e422582793b260c"
dependencies = [
"quote",
"syn",
@@ -1119,6 +1508,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if 1.0.0",
"value-bag",
]
[[package]]
@@ -1142,6 +1532,15 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "memmap2"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95af15f345b17af2efc8ead6080fb8bc376f8cec1b35277b935637595fe77498"
dependencies = [
"libc",
]
[[package]]
name = "memoffset"
version = "0.6.5"
@@ -1328,12 +1727,30 @@ version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "opaque-debug"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
[[package]]
name = "opaque-debug"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "os_str_bytes"
version = "6.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3baf96e39c5359d2eb0dd6ccb42c62b91d9678aa68160d261b9e0ccbf9e9dea9"
[[package]]
name = "parking"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
[[package]]
name = "parking_lot"
version = "0.12.1"
@@ -1506,6 +1923,18 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468"
[[package]]
name = "pin-project-lite"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "plotters"
version = "0.3.4"
@@ -1534,6 +1963,20 @@ dependencies = [
"plotters-backend",
]
[[package]]
name = "polling"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab4609a838d88b73d8238967b60dd115cc08d38e2bbaf51ee1e4b695f89122e2"
dependencies = [
"autocfg",
"cfg-if 1.0.0",
"libc",
"log",
"wepoll-ffi",
"winapi 0.3.9",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
@@ -1805,7 +2248,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.145"
version = "0.0.128"
dependencies = [
"annotate-snippets 0.9.1",
"anyhow",
@@ -1813,10 +2256,9 @@ dependencies = [
"atty",
"bincode",
"bitflags",
"cachedir",
"cacache",
"chrono",
"clap 4.0.22",
"clap_complete_command",
"clearscreen",
"colored",
"common-path",
@@ -1824,8 +2266,9 @@ dependencies = [
"dirs 4.0.0",
"fern",
"filetime",
"fnv",
"getrandom 0.2.8",
"globset",
"glob",
"insta",
"itertools",
"libcst",
@@ -1838,7 +2281,6 @@ dependencies = [
"rayon",
"regex",
"ropey",
"rustc-hash",
"rustpython-ast",
"rustpython-common",
"rustpython-parser",
@@ -1856,7 +2298,7 @@ dependencies = [
[[package]]
name = "ruff_dev"
version = "0.0.145"
version = "0.0.128"
dependencies = [
"anyhow",
"clap 4.0.22",
@@ -1871,12 +2313,6 @@ dependencies = [
"strum_macros",
]
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustls"
version = "0.20.7"
@@ -1892,7 +2328,7 @@ dependencies = [
[[package]]
name = "rustpython-ast"
version = "0.1.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
source = "git+https://github.com/RustPython/RustPython.git?rev=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
dependencies = [
"num-bigint",
"rustpython-common",
@@ -1902,7 +2338,7 @@ dependencies = [
[[package]]
name = "rustpython-common"
version = "0.0.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
source = "git+https://github.com/RustPython/RustPython.git?rev=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
dependencies = [
"ascii",
"cfg-if 1.0.0",
@@ -1925,7 +2361,7 @@ dependencies = [
[[package]]
name = "rustpython-compiler-core"
version = "0.1.2"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
source = "git+https://github.com/RustPython/RustPython.git?rev=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
dependencies = [
"bincode",
"bitflags",
@@ -1942,7 +2378,7 @@ dependencies = [
[[package]]
name = "rustpython-parser"
version = "0.1.2"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
source = "git+https://github.com/RustPython/RustPython.git?rev=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
dependencies = [
"ahash",
"anyhow",
@@ -1954,7 +2390,6 @@ dependencies = [
"num-traits",
"phf 0.10.1",
"phf_codegen 0.10.0",
"rustc-hash",
"rustpython-ast",
"rustpython-compiler-core",
"thiserror",
@@ -2044,6 +2479,75 @@ dependencies = [
"serde",
]
[[package]]
name = "sha-1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
dependencies = [
"block-buffer 0.7.3",
"digest 0.8.1",
"fake-simd",
"opaque-debug 0.2.3",
]
[[package]]
name = "sha-1"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6"
dependencies = [
"block-buffer 0.9.0",
"cfg-if 1.0.0",
"cpufeatures",
"digest 0.9.0",
"opaque-debug 0.3.0",
]
[[package]]
name = "sha2"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69"
dependencies = [
"block-buffer 0.7.3",
"digest 0.8.1",
"fake-simd",
"opaque-debug 0.2.3",
]
[[package]]
name = "sha2"
version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
dependencies = [
"block-buffer 0.9.0",
"cfg-if 1.0.0",
"cpufeatures",
"digest 0.9.0",
"opaque-debug 0.3.0",
]
[[package]]
name = "signal-hook"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
dependencies = [
"libc",
]
[[package]]
name = "similar"
version = "2.2.0"
@@ -2077,12 +2581,37 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043"
[[package]]
name = "socket2"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
dependencies = [
"libc",
"winapi 0.3.9",
]
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "ssri"
version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9cec0d388f39fbe79d7aa600e8d38053bf97b1bc8d350da7c0ba800d0f423f2"
dependencies = [
"base64 0.10.1",
"digest 0.8.1",
"hex 0.3.2",
"serde",
"sha-1 0.8.2",
"sha2 0.8.2",
"thiserror",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
@@ -2327,6 +2856,12 @@ dependencies = [
"static_assertions",
]
[[package]]
name = "typenum"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
[[package]]
name = "unic-char-property"
version = "0.9.0"
@@ -2465,7 +3000,7 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97acb4c28a254fd7a4aeec976c46a7fa404eac4d7c134b30c75144846d7cb8f"
dependencies = [
"base64",
"base64 0.13.1",
"chunked_transfer",
"flate2",
"log",
@@ -2489,6 +3024,16 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "value-bag"
version = "1.0.0-alpha.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55"
dependencies = [
"ctor",
"version_check",
]
[[package]]
name = "version_check"
version = "0.9.4"
@@ -2510,6 +3055,12 @@ dependencies = [
"libc",
]
[[package]]
name = "waker-fn"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
[[package]]
name = "walkdir"
version = "2.3.2"
@@ -2558,6 +3109,18 @@ dependencies = [
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d"
dependencies = [
"cfg-if 1.0.0",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.83"
@@ -2616,6 +3179,15 @@ dependencies = [
"webpki",
]
[[package]]
name = "wepoll-ffi"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb"
dependencies = [
"cc",
]
[[package]]
name = "which"
version = "4.3.0"

View File

@@ -6,9 +6,8 @@ members = [
[package]
name = "ruff"
version = "0.0.145"
version = "0.0.128"
edition = "2021"
rust-version = "1.65.0"
[lib]
name = "ruff"
@@ -19,18 +18,17 @@ anyhow = { version = "1.0.66" }
atty = { version = "0.2.14" }
bincode = { version = "1.3.3" }
bitflags = { version = "1.3.2" }
cachedir = { version = "0.3.0" }
chrono = { version = "0.4.21", default-features = false, features = ["clock"] }
clap = { version = "4.0.1", features = ["derive"] }
clap_complete_command = "0.4.0"
colored = { version = "2.0.0" }
common-path = { version = "1.0.0" }
dirs = { version = "4.0.0" }
fern = { version = "0.6.1" }
filetime = { version = "0.2.17" }
globset = {version = "0.4.9" }
fnv = { version = "1.0.7" }
glob = { version = "0.3.0" }
itertools = { version = "0.10.5" }
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a8725d161fe8b3ed73a6758b21e177" }
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "a13ec97dd4eb925bde4d426c6e422582793b260c" }
log = { version = "0.4.17" }
nohash-hasher = { version = "0.2.0" }
notify = { version = "4.0.17" }
@@ -40,10 +38,9 @@ path-absolutize = { version = "3.0.14", features = ["once_cell_cache", "use_unix
rayon = { version = "1.5.3" }
regex = { version = "1.6.0" }
ropey = { version = "1.5.0", features = ["cr_lines", "simd"], default-features = false }
rustc-hash = { version = "1.1.0" }
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "f885db8c61514f069979861f6b3bd83292086231" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "f885db8c61514f069979861f6b3bd83292086231" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "f885db8c61514f069979861f6b3bd83292086231" }
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb" }
serde = { version = "1.0.147", features = ["derive"] }
serde_json = { version = "1.0.87" }
strum = { version = "0.24.1", features = ["strum_macros"] }
@@ -55,6 +52,7 @@ update-informer = { version = "0.5.0", default-features = false, features = ["py
walkdir = { version = "2.3.2" }
[target.'cfg(not(target_family = "wasm"))'.dependencies]
cacache = { version = "10.0.1" } # uses async-std
clearscreen = { version = "1.0.10" } # uses which
# https://docs.rs/getrandom/0.2.7/getrandom/#webassembly-support

75
LICENSE
View File

@@ -242,56 +242,6 @@ are:
SOFTWARE.
"""
- flake8-debugger, licensed as follows:
"""
MIT License
Copyright (c) 2016 Joseph Kahn
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
- flake8-eradicate, licensed as follows:
"""
MIT License
Copyright (c) 2018 Nikita Sobolev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
- flake8-tidy-imports, licensed as follows:
"""
MIT License
@@ -493,28 +443,3 @@ are:
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""
- RustPython, licensed as follows:
"""
MIT License
Copyright (c) 2020 RustPython Team
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""

814
README.md

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,7 @@ fn criterion_benchmark(c: &mut Criterion) {
b.iter(|| {
let rope = Rope::from_str(black_box(&contents));
rope.line_to_char(black_box(4));
});
})
});
}

View File

@@ -771,7 +771,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8_to_ruff"
version = "0.0.145"
version = "0.0.128"
dependencies = [
"anyhow",
"clap",
@@ -1265,7 +1265,7 @@ checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
[[package]]
name = "libcst"
version = "0.1.0"
source = "git+https://github.com/charliermarsh/LibCST?rev=f2f0b7a487a8725d161fe8b3ed73a6758b21e177#f2f0b7a487a8725d161fe8b3ed73a6758b21e177"
source = "git+https://github.com/charliermarsh/LibCST?rev=a13ec97dd4eb925bde4d426c6e422582793b260c#a13ec97dd4eb925bde4d426c6e422582793b260c"
dependencies = [
"chic",
"itertools",
@@ -1280,7 +1280,7 @@ dependencies = [
[[package]]
name = "libcst_derive"
version = "0.1.0"
source = "git+https://github.com/charliermarsh/LibCST?rev=f2f0b7a487a8725d161fe8b3ed73a6758b21e177#f2f0b7a487a8725d161fe8b3ed73a6758b21e177"
source = "git+https://github.com/charliermarsh/LibCST?rev=a13ec97dd4eb925bde4d426c6e422582793b260c#a13ec97dd4eb925bde4d426c6e422582793b260c"
dependencies = [
"quote",
"syn",
@@ -1975,7 +1975,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.145"
version = "0.0.128"
dependencies = [
"anyhow",
"bincode",
@@ -2028,7 +2028,7 @@ dependencies = [
[[package]]
name = "rustpython-ast"
version = "0.1.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
source = "git+https://github.com/RustPython/RustPython.git?rev=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
dependencies = [
"num-bigint",
"rustpython-common",
@@ -2038,7 +2038,7 @@ dependencies = [
[[package]]
name = "rustpython-common"
version = "0.0.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
source = "git+https://github.com/RustPython/RustPython.git?rev=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
dependencies = [
"ascii",
"cfg-if 1.0.0",
@@ -2061,7 +2061,7 @@ dependencies = [
[[package]]
name = "rustpython-compiler-core"
version = "0.1.2"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
source = "git+https://github.com/RustPython/RustPython.git?rev=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
dependencies = [
"bincode",
"bitflags",
@@ -2078,7 +2078,7 @@ dependencies = [
[[package]]
name = "rustpython-parser"
version = "0.1.2"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
source = "git+https://github.com/RustPython/RustPython.git?rev=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
dependencies = [
"ahash",
"anyhow",

View File

@@ -1,6 +1,6 @@
[package]
name = "flake8-to-ruff"
version = "0.0.145-dev.0"
version = "0.0.128-dev.0"
edition = "2021"
[lib]
@@ -10,10 +10,10 @@ name = "flake8_to_ruff"
anyhow = { version = "1.0.66" }
clap = { version = "4.0.1", features = ["derive"] }
configparser = { version = "3.0.2" }
fnv = { version = "1.0.7" }
once_cell = { version = "1.16.0" }
regex = { version = "1.6.0" }
ruff = { path = "..", default-features = false }
rustc-hash = { version = "1.1.0" }
serde = { version = "1.0.147", features = ["derive"] }
serde_json = { version = "1.0.87" }
toml = { version = "0.5.9" }

View File

@@ -25,7 +25,7 @@ requires-python = ">=3.7"
repository = "https://github.com/charliermarsh/ruff#subdirectory=crates/flake8_to_ruff"
[build-system]
requires = ["maturin>=0.14,<0.15"]
requires = ["maturin>=0.13,<0.14"]
build-backend = "maturin"
[tool.maturin]

View File

@@ -18,7 +18,7 @@ pub fn convert(
plugins: Option<Vec<Plugin>>,
) -> Result<Pyproject> {
// Extract all referenced check code prefixes, to power plugin inference.
let mut referenced_codes: BTreeSet<CheckCodePrefix> = BTreeSet::default();
let mut referenced_codes: BTreeSet<CheckCodePrefix> = Default::default();
for (key, value) in flake8 {
if let Some(value) = value {
match key.as_str() {
@@ -70,13 +70,13 @@ pub fn convert(
.unwrap_or_default();
// Parse each supported option.
let mut options = Options::default();
let mut flake8_annotations = flake8_annotations::settings::Options::default();
let mut flake8_bugbear = flake8_bugbear::settings::Options::default();
let mut flake8_quotes = flake8_quotes::settings::Options::default();
let mut flake8_tidy_imports = flake8_tidy_imports::settings::Options::default();
let mut mccabe = mccabe::settings::Options::default();
let mut pep8_naming = pep8_naming::settings::Options::default();
let mut options: Options = Default::default();
let mut flake8_annotations: flake8_annotations::settings::Options = Default::default();
let mut flake8_bugbear: flake8_bugbear::settings::Options = Default::default();
let mut flake8_quotes: flake8_quotes::settings::Options = Default::default();
let mut flake8_tidy_imports: flake8_tidy_imports::settings::Options = Default::default();
let mut mccabe: mccabe::settings::Options = Default::default();
let mut pep8_naming: pep8_naming::settings::Options = Default::default();
for (key, value) in flake8 {
if let Some(value) = value {
match key.as_str() {
@@ -110,7 +110,7 @@ pub fn convert(
match parser::parse_files_to_codes_mapping(value.as_ref()) {
Ok(per_file_ignores) => {
options.per_file_ignores =
Some(parser::collect_per_file_ignores(per_file_ignores));
Some(parser::collect_per_file_ignores(per_file_ignores))
}
Err(e) => eprintln!("Unable to parse '{key}' property: {e}"),
}
@@ -181,7 +181,7 @@ pub fn convert(
"ban-relative-imports" | "ban_relative_imports" => match value.trim() {
"true" => flake8_tidy_imports.ban_relative_imports = Some(Strictness::All),
"parents" => {
flake8_tidy_imports.ban_relative_imports = Some(Strictness::Parents);
flake8_tidy_imports.ban_relative_imports = Some(Strictness::Parents)
}
_ => eprintln!("Unexpected '{key}' value: {value}"),
},
@@ -203,22 +203,22 @@ pub fn convert(
// Deduplicate and sort.
options.select = Some(Vec::from_iter(select));
options.ignore = Some(Vec::from_iter(ignore));
if flake8_annotations != flake8_annotations::settings::Options::default() {
if flake8_annotations != Default::default() {
options.flake8_annotations = Some(flake8_annotations);
}
if flake8_bugbear != flake8_bugbear::settings::Options::default() {
if flake8_bugbear != Default::default() {
options.flake8_bugbear = Some(flake8_bugbear);
}
if flake8_quotes != flake8_quotes::settings::Options::default() {
if flake8_quotes != Default::default() {
options.flake8_quotes = Some(flake8_quotes);
}
if flake8_tidy_imports != flake8_tidy_imports::settings::Options::default() {
if flake8_tidy_imports != Default::default() {
options.flake8_tidy_imports = Some(flake8_tidy_imports);
}
if mccabe != mccabe::settings::Options::default() {
if mccabe != Default::default() {
options.mccabe = Some(mccabe);
}
if pep8_naming != pep8_naming::settings::Options::default() {
if pep8_naming != Default::default() {
options.pep8_naming = Some(pep8_naming);
}

View File

@@ -1,15 +1,4 @@
#![allow(
clippy::collapsible_else_if,
clippy::collapsible_if,
clippy::implicit_hasher,
clippy::match_same_arms,
clippy::missing_errors_doc,
clippy::missing_panics_doc,
clippy::module_name_repetitions,
clippy::must_use_candidate,
clippy::similar_names,
clippy::too_many_lines
)]
#![allow(clippy::collapsible_if, clippy::collapsible_else_if)]
pub mod converter;
mod parser;

View File

@@ -1,16 +1,4 @@
//! Utility to generate Ruff's pyproject.toml section from a Flake8 INI file.
#![allow(
clippy::collapsible_else_if,
clippy::collapsible_if,
clippy::implicit_hasher,
clippy::match_same_arms,
clippy::missing_errors_doc,
clippy::missing_panics_doc,
clippy::module_name_repetitions,
clippy::must_use_candidate,
clippy::similar_names,
clippy::too_many_lines
)]
use std::path::PathBuf;

View File

@@ -1,11 +1,11 @@
use std::str::FromStr;
use anyhow::Result;
use fnv::FnvHashMap;
use once_cell::sync::Lazy;
use regex::Regex;
use ruff::checks_gen::CheckCodePrefix;
use ruff::settings::types::PatternPrefixPair;
use rustc_hash::FxHashMap;
static COMMA_SEPARATED_LIST_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"[,\s]").unwrap());
@@ -31,7 +31,7 @@ pub fn parse_prefix_codes(value: &str) -> Vec<CheckCodePrefix> {
pub fn parse_strings(value: &str) -> Vec<String> {
COMMA_SEPARATED_LIST_RE
.split(value)
.map(str::trim)
.map(|part| part.trim())
.filter(|part| !part.is_empty())
.map(String::from)
.collect()
@@ -92,7 +92,7 @@ impl State {
});
}
}
Err(_) => eprintln!("Skipping unrecognized prefix: {code}"),
Err(_) => eprintln!("Skipping unrecognized prefix: {}", code),
}
}
codes
@@ -129,14 +129,14 @@ fn tokenize_files_to_codes_mapping(value: &str) -> Vec<Token> {
}
tokens.push(Token {
token_name: TokenType::Eof,
src: String::new(),
src: "".to_string(),
});
tokens
}
/// Parse a 'files-to-codes' mapping, mimicking Flake8's internal logic.
///
/// See: <https://github.com/PyCQA/flake8/blob/7dfe99616fc2f07c0017df2ba5fa884158f3ea8a/src/flake8/utils.py#L45>
/// See: https://github.com/PyCQA/flake8/blob/7dfe99616fc2f07c0017df2ba5fa884158f3ea8a/src/flake8/utils.py#L45
pub fn parse_files_to_codes_mapping(value: &str) -> Result<Vec<PatternPrefixPair>> {
if value.trim().is_empty() {
return Ok(vec![]);
@@ -179,8 +179,8 @@ pub fn parse_files_to_codes_mapping(value: &str) -> Result<Vec<PatternPrefixPair
/// Collect a list of `PatternPrefixPair` structs as a `BTreeMap`.
pub fn collect_per_file_ignores(
pairs: Vec<PatternPrefixPair>,
) -> FxHashMap<String, Vec<CheckCodePrefix>> {
let mut per_file_ignores: FxHashMap<String, Vec<CheckCodePrefix>> = FxHashMap::default();
) -> FnvHashMap<String, Vec<CheckCodePrefix>> {
let mut per_file_ignores: FnvHashMap<String, Vec<CheckCodePrefix>> = FnvHashMap::default();
for pair in pairs {
per_file_ignores
.entry(pair.pattern)

View File

@@ -6,19 +6,17 @@ use ruff::checks_gen::CheckCodePrefix;
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum Plugin {
Flake8Annotations,
Flake8Bandit,
Flake8BlindExcept,
Flake8Bugbear,
Flake8Builtins,
Flake8Comprehensions,
Flake8Debugger,
Flake8Docstrings,
Flake8Eradicate,
Flake8TidyImports,
Flake8Print,
Flake8Quotes,
Flake8TidyImports,
Flake8Annotations,
McCabe,
Flake8BlindExcept,
PEP8Naming,
Pyupgrade,
}
@@ -28,18 +26,16 @@ impl FromStr for Plugin {
fn from_str(string: &str) -> Result<Self, Self::Err> {
match string {
"flake8-annotations" => Ok(Plugin::Flake8Annotations),
"flake8-bandit" => Ok(Plugin::Flake8Bandit),
"flake8-blind-except" => Ok(Plugin::Flake8BlindExcept),
"flake8-bugbear" => Ok(Plugin::Flake8Bugbear),
"flake8-builtins" => Ok(Plugin::Flake8Builtins),
"flake8-comprehensions" => Ok(Plugin::Flake8Comprehensions),
"flake8-debugger" => Ok(Plugin::Flake8Debugger),
"flake8-docstrings" => Ok(Plugin::Flake8Docstrings),
"flake8-eradicate" => Ok(Plugin::Flake8BlindExcept),
"flake8-tidy-imports" => Ok(Plugin::Flake8TidyImports),
"flake8-print" => Ok(Plugin::Flake8Print),
"flake8-quotes" => Ok(Plugin::Flake8Quotes),
"flake8-tidy-imports" => Ok(Plugin::Flake8TidyImports),
"flake8-annotations" => Ok(Plugin::Flake8Annotations),
"flake8-blind-except" => Ok(Plugin::Flake8BlindExcept),
"mccabe" => Ok(Plugin::McCabe),
"pep8-naming" => Ok(Plugin::PEP8Naming),
"pyupgrade" => Ok(Plugin::Pyupgrade),
@@ -51,18 +47,16 @@ impl FromStr for Plugin {
impl Plugin {
pub fn default(&self) -> CheckCodePrefix {
match self {
Plugin::Flake8Annotations => CheckCodePrefix::ANN,
Plugin::Flake8Bandit => CheckCodePrefix::S,
Plugin::Flake8BlindExcept => CheckCodePrefix::BLE,
Plugin::Flake8Bugbear => CheckCodePrefix::B,
Plugin::Flake8Builtins => CheckCodePrefix::A,
Plugin::Flake8Comprehensions => CheckCodePrefix::C4,
Plugin::Flake8Debugger => CheckCodePrefix::T1,
Plugin::Flake8Docstrings => CheckCodePrefix::D,
Plugin::Flake8Eradicate => CheckCodePrefix::ERA,
Plugin::Flake8Print => CheckCodePrefix::T2,
Plugin::Flake8Quotes => CheckCodePrefix::Q,
Plugin::Flake8TidyImports => CheckCodePrefix::I25,
Plugin::Flake8Print => CheckCodePrefix::T,
Plugin::Flake8Quotes => CheckCodePrefix::Q,
Plugin::Flake8Annotations => CheckCodePrefix::ANN,
Plugin::Flake8BlindExcept => CheckCodePrefix::BLE,
Plugin::McCabe => CheckCodePrefix::C9,
Plugin::PEP8Naming => CheckCodePrefix::N,
Plugin::Pyupgrade => CheckCodePrefix::U,
@@ -71,13 +65,10 @@ impl Plugin {
pub fn select(&self, flake8: &HashMap<String, Option<String>>) -> Vec<CheckCodePrefix> {
match self {
Plugin::Flake8Annotations => vec![CheckCodePrefix::ANN],
Plugin::Flake8Bandit => vec![CheckCodePrefix::S],
Plugin::Flake8BlindExcept => vec![CheckCodePrefix::BLE],
Plugin::Flake8Bugbear => vec![CheckCodePrefix::B],
Plugin::Flake8Builtins => vec![CheckCodePrefix::A],
Plugin::Flake8Comprehensions => vec![CheckCodePrefix::C4],
Plugin::Flake8Debugger => vec![CheckCodePrefix::T1],
Plugin::Flake8Docstrings => {
// Use the user-provided docstring.
for key in ["docstring-convention", "docstring_convention"] {
@@ -94,10 +85,11 @@ impl Plugin {
// Default to PEP8.
DocstringConvention::PEP8.select()
}
Plugin::Flake8Eradicate => vec![CheckCodePrefix::ERA],
Plugin::Flake8Print => vec![CheckCodePrefix::T2],
Plugin::Flake8Quotes => vec![CheckCodePrefix::Q],
Plugin::Flake8TidyImports => vec![CheckCodePrefix::I25],
Plugin::Flake8Print => vec![CheckCodePrefix::T],
Plugin::Flake8Quotes => vec![CheckCodePrefix::Q],
Plugin::Flake8Annotations => vec![CheckCodePrefix::ANN],
Plugin::Flake8BlindExcept => vec![CheckCodePrefix::BLE],
Plugin::McCabe => vec![CheckCodePrefix::C9],
Plugin::PEP8Naming => vec![CheckCodePrefix::N],
Plugin::Pyupgrade => vec![CheckCodePrefix::U],
@@ -285,6 +277,31 @@ pub fn infer_plugins_from_options(flake8: &HashMap<String, Option<String>>) -> V
let mut plugins = BTreeSet::new();
for key in flake8.keys() {
match key.as_str() {
// flake8-docstrings
"docstring-convention" | "docstring_convention" => {
plugins.insert(Plugin::Flake8Docstrings);
}
// flake8-bugbear
"extend-immutable-calls" | "extend_immutable_calls" => {
plugins.insert(Plugin::Flake8Bugbear);
}
// flake8-builtins
"builtins-ignorelist" | "builtins_ignorelist" => {
plugins.insert(Plugin::Flake8Builtins);
}
// flake8-quotes
"quotes" | "inline-quotes" | "inline_quotes" => {
plugins.insert(Plugin::Flake8Quotes);
}
"multiline-quotes" | "multiline_quotes" => {
plugins.insert(Plugin::Flake8Quotes);
}
"docstring-quotes" | "docstring_quotes" => {
plugins.insert(Plugin::Flake8Quotes);
}
"avoid-escape" | "avoid_escape" => {
plugins.insert(Plugin::Flake8Quotes);
}
// flake8-annotations
"suppress-none-returning" | "suppress_none_returning" => {
plugins.insert(Plugin::Flake8Annotations);
@@ -310,41 +327,6 @@ pub fn infer_plugins_from_options(flake8: &HashMap<String, Option<String>>) -> V
"allow-star-arg-any" | "allow_star_arg_any" => {
plugins.insert(Plugin::Flake8Annotations);
}
// flake8-bugbear
"extend-immutable-calls" | "extend_immutable_calls" => {
plugins.insert(Plugin::Flake8Bugbear);
}
// flake8-builtins
"builtins-ignorelist" | "builtins_ignorelist" => {
plugins.insert(Plugin::Flake8Builtins);
}
// flake8-docstrings
"docstring-convention" | "docstring_convention" => {
plugins.insert(Plugin::Flake8Docstrings);
}
// flake8-eradicate
"eradicate-aggressive" | "eradicate_aggressive" => {
plugins.insert(Plugin::Flake8Eradicate);
}
"eradicate-whitelist" | "eradicate_whitelist" => {
plugins.insert(Plugin::Flake8Eradicate);
}
"eradicate-whitelist-extend" | "eradicate_whitelist_extend" => {
plugins.insert(Plugin::Flake8Eradicate);
}
// flake8-quotes
"quotes" | "inline-quotes" | "inline_quotes" => {
plugins.insert(Plugin::Flake8Quotes);
}
"multiline-quotes" | "multiline_quotes" => {
plugins.insert(Plugin::Flake8Quotes);
}
"docstring-quotes" | "docstring_quotes" => {
plugins.insert(Plugin::Flake8Quotes);
}
"avoid-escape" | "avoid_escape" => {
plugins.insert(Plugin::Flake8Quotes);
}
// flake8-tidy-imports
"ban-relative-imports" | "ban_relative_imports" => {
plugins.insert(Plugin::Flake8TidyImports);
@@ -378,18 +360,16 @@ pub fn infer_plugins_from_options(flake8: &HashMap<String, Option<String>>) -> V
/// `flake8-annotations` is active.
pub fn infer_plugins_from_codes(codes: &BTreeSet<CheckCodePrefix>) -> Vec<Plugin> {
[
Plugin::Flake8Annotations,
Plugin::Flake8Bandit,
Plugin::Flake8BlindExcept,
Plugin::Flake8Bugbear,
Plugin::Flake8Builtins,
Plugin::Flake8Comprehensions,
Plugin::Flake8Debugger,
Plugin::Flake8Docstrings,
Plugin::Flake8Eradicate,
Plugin::Flake8TidyImports,
Plugin::Flake8Print,
Plugin::Flake8Quotes,
Plugin::Flake8TidyImports,
Plugin::Flake8Annotations,
Plugin::Flake8BlindExcept,
Plugin::PEP8Naming,
Plugin::Pyupgrade,
]

View File

@@ -25,11 +25,12 @@ requires-python = ">=3.7"
repository = "https://github.com/charliermarsh/ruff"
[build-system]
requires = ["maturin>=0.14,<0.15"]
requires = ["maturin>=0.13,<0.14"]
build-backend = "maturin"
[tool.maturin]
bindings = "bin"
sdist-include = ["Cargo.lock"]
strip = true
[tool.isort]

View File

@@ -185,23 +185,3 @@ def nested_b008(a=random.randint(0, dt.datetime.now().year)):
# Ignore lambda contents since they are evaluated at call time.
def foo(f=lambda x: print(x)):
f(1)
from collections import abc
from typing import Annotated, Dict, Optional, Sequence, Union, Set
def immutable_annotations(
a: Sequence[int] | None = [],
b: Optional[abc.Mapping[int, int]] = {},
c: Annotated[Union[abc.Set[str], abc.Sized], "annotation"] = set(),
):
pass
def mutable_annotations(
a: list[int] | None = [],
b: Optional[Dict[int, int]] = {},
c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
):
pass

View File

@@ -34,4 +34,3 @@ setattr(foo, "bar", None)
setattr(foo, "_123abc", None)
setattr(foo, "abc123", None)
setattr(foo, r"abc123", None)
setattr(foo.bar, r"baz", None)

View File

@@ -1,82 +0,0 @@
"""
Should emit:
B023 - on lines 12, 13, 16, 28, 29, 30, 31, 40, 42, 50, 51, 52, 53, 61, 68.
"""
functions = []
z = 0
for x in range(3):
y = x + 1
# Subject to late-binding problems
functions.append(lambda: x)
functions.append(lambda: y) # not just the loop var
def f_bad_1():
return x
# Actually OK
functions.append(lambda x: x * 2)
functions.append(lambda x=x: x)
functions.append(lambda: z) # OK because not assigned in the loop
def f_ok_1(x):
return x * 2
def check_inside_functions_too():
ls = [lambda: x for x in range(2)]
st = {lambda: x for x in range(2)}
gn = (lambda: x for x in range(2))
dt = {x: lambda: x for x in range(2)}
async def pointless_async_iterable():
yield 1
async def container_for_problems():
async for x in pointless_async_iterable():
functions.append(lambda: x)
[lambda: x async for x in pointless_async_iterable()]
a = 10
b = 0
while True:
a = a_ = a - 1
b += 1
functions.append(lambda: a)
functions.append(lambda: a_)
functions.append(lambda: b)
functions.append(lambda: c) # not a name error because of late binding!
c: bool = a > 3
if not c:
break
# Nested loops should not duplicate reports
for j in range(2):
for k in range(3):
lambda: j * k
for j, k, l in [(1, 2, 3)]:
def f():
j = None # OK because it's an assignment
[l for k in range(2)] # error for l, not for k
assert a and functions
a.attribute = 1 # modifying an attribute doesn't make it a loop variable
functions[0] = lambda: None # same for an element
for var in range(2):
def explicit_capture(captured=var):
return captured
for i in range(3):
lambda: f"{i}"

View File

@@ -1,55 +0,0 @@
"""
Should emit:
B904 - on lines 10, 11 and 16
"""
try:
raise ValueError
except ValueError:
if "abc":
raise TypeError
raise UserWarning
except AssertionError:
raise # Bare `raise` should not be an error
except Exception as err:
assert err
raise Exception("No cause here...")
except BaseException as base_err:
# Might use this instead of bare raise with the `.with_traceback()` method
raise base_err
finally:
raise Exception("Nothing to chain from, so no warning here")
try:
raise ValueError
except ValueError:
# should not emit, since we are not raising something
def proxy():
raise NameError
try:
from preferred_library import Thing
except ImportError:
try:
from fallback_library import Thing
except ImportError:
class Thing:
def __getattr__(self, name):
# same as the case above, should not emit.
raise AttributeError
try:
from preferred_library import Thing
except ImportError:
try:
from fallback_library import Thing
except ImportError:
def context_switch():
try:
raise ValueError
except ValueError:
raise

View File

@@ -106,33 +106,3 @@ async def foobar(a, b, c):
# Complexity = 1
def annotated_assign():
x: Any = None
# Complexity = 9
class Class:
def handle(self, *args, **options):
if args:
return
class ServiceProvider:
def a(self):
pass
def b(self, data):
if not args:
pass
class Logger:
def c(*args, **kwargs):
pass
def error(self, message):
pass
def info(self, message):
pass
def exception(self):
pass
return ServiceProvider(Logger())

View File

@@ -3,7 +3,6 @@ from functools import wraps
import os
from .expected import Expectation
from typing import overload
from typing_extensions import override
expectation = Expectation()
@@ -43,13 +42,9 @@ class class_:
"D418: Function/ Method decorated with @overload"
" shouldn't contain a docstring")
@override
def overridden_method(a):
return str(a)
@property
def foo(self):
"""The foo of the thing, which isn't in imperative mood."""
"""The foo of the thing, which isn't in imperitive mood."""
return "hello"
@expect('D102: Missing docstring in public method')

View File

@@ -49,9 +49,3 @@ sit amet consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labor
sit amet consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
""", # noqa: E501
}
# OK
# A very long URL: https://loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong.url.com
# OK
# https://loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong.url.com

View File

@@ -1,13 +0,0 @@
#import os
# from foo import junk
#a = 3
a = 4
#foo(1, 2, 3)
def foo(x, y, z):
contentet = 1 # print('hello')
print(x, y, z)
# This is a real comment.
#return True
return False

View File

@@ -1,13 +0,0 @@
dog = {"bob": "bob"}
"%(bob)s" % dog
"%(bob)s" % {"bob": "bob"}
"%(bob)s" % {**{"bob": "bob"}}
"%(bob)s" % ["bob"] # F202
"%(bob)s" % ("bob",) # F202
"%(bob)s" % {"bob"} # F202
"%(bob)s" % [*["bob"]] # F202
"%(bob)s" % {"bob": "bob" for _ in range(1)}
"%(bob)s" % ["bob" for _ in range(1)] # F202
"%(bob)s" % ("bob" for _ in range(1)) # F202
"%(bob)s" % {"bob" for _ in range(1)} # F202

View File

@@ -1,26 +0,0 @@
dog = {"bob": "bob"}
# Single placeholder always fine
"%s" % dog
"%s" % {"bob": "bob"}
"%s" % {**{"bob": "bob"}}
"%s" % ["bob"]
"%s" % ("bob",)
"%s" % {"bob"}
"%s" % [*["bob"]]
"%s" % {"bob": "bob" for _ in range(1)}
"%s" % ["bob" for _ in range(1)]
"%s" % ("bob" for _ in range(1))
"%s" % {"bob" for _ in range(1)}
# Multiple placeholders
"%s %s" % dog
"%s %s" % {"bob": "bob"} # F503
"%s %s" % {**{"bob": "bob"}} # F503
"%s %s" % ["bob"]
"%s %s" % ("bob",)
"%s %s" % {"bob"}
"%s %s" % [*["bob"]]
"%s %s" % {"bob": "bob" for _ in range(1)} # F503
"%s %s" % ["bob" for _ in range(1)]
"%s %s" % ("bob" for _ in range(1))
"%s %s" % {"bob" for _ in range(1)}

View File

@@ -1,6 +0,0 @@
# Ruff has no way of knowing if the following are F505s
a = "wrong"
"%(a)s %(c)s" % {a: "?", "b": "!"} # F504 ("b" not used)
hidden = {"a": "!"}
"%(a)s %(c)s" % {"x": 1, **hidden} # Ok (cannot see through splat)

View File

@@ -1,25 +0,0 @@
'%(foo)' % {'foo': 'bar'} # F501
'%s %(foo)s' % {'foo': 'bar'} # F506
'%(foo)s %s' % {'foo': 'bar'} # F506
'%j' % (1,) # F509
'%s %s' % (1,) # F507
'%s %s' % (1, 2, 3) # F507
'%(bar)s' % {} # F505
'%(bar)s' % {'bar': 1, 'baz': 2} # F504
'%(bar)s' % (1, 2, 3) # F502
'%s %s' % {'k': 'v'} # F503
'%(bar)*s' % {'bar': 'baz'} # F506, F508
# ok: single %s with mapping
'%s' % {'foo': 'bar', 'baz': 'womp'}
# ok: %% should not count towards placeholder count
'%% %s %% %s' % (1, 2)
# ok: * consumes one positional argument
'%.*f' % (2, 1.1234)
'%*.*f' % (5, 2, 3.1234)
# ok *args and **kwargs
a = []
'%s %s' % [*a]
'%s %s' % (*a,)
k = {}
'%(k)s' % {**k}

View File

@@ -1,21 +0,0 @@
"{".format(1)
"}".format(1)
"{foo[}".format(foo=1)
# too much string recursion (placeholder-in-placeholder)
"{:{:{}}}".format(1, 2, 3)
# ruff picks these issues up, but flake8 doesn't
"{foo[]}".format(foo={"": 1})
"{foo..}".format(foo=1)
"{foo..bar}".format(foo=1)
# The following are all "good" uses of .format
"{.__class__}".format("")
"{foo[bar]}".format(foo={"bar": "barv"})
"{[bar]}".format({"bar": "barv"})
"{:{}} {}".format(1, 15, 2)
"{:2}".format(1)
"{foo}-{}".format(1, foo=2)
a = ()
"{}".format(*a)
k = {}
"{foo}".format(**k)

View File

@@ -1,4 +0,0 @@
"{}".format(1, bar=2) # F522
"{bar}{}".format(1, bar=2, spam=3) # F522
"{bar:{spam}}".format(bar=2, spam=3) # No issues
"{bar:{spam}}".format(bar=2, spam=3, eggs=4, ham=5) # F522

View File

@@ -1,12 +0,0 @@
# With indexes
"{0}".format(1, 2) # F523
"{1}".format(1, 2, 3) # F523
"{1:{0}}".format(1, 2) # No issues
"{1:{0}}".format(1, 2, 3) # F523
"{0}{2}".format(1, 2) # F523, # F524
# With no indexes
"{}".format(1, 2) # F523
"{}".format(1, 2, 3) # F523
"{:{}}".format(1, 2) # No issues
"{:{}}".format(1, 2, 3) # F523

View File

@@ -1,6 +0,0 @@
"{} {}".format(1) # F524
"{2}".format() # F524
"{bar}".format() # F524
"{0} {bar}".format(1) # F524
"{0} {bar}".format() # F524
"{bar} {0}".format() # F524

View File

@@ -1,2 +0,0 @@
"{} {1}".format(1, 2) # F525
"{0} {}".format(1, 2) # F523, F525

View File

@@ -12,13 +12,3 @@ x: dict["key", "value"]
# OK
x: dict[str, str]
# OK
def unimportant(name):
pass
def dang(dict, set, list):
unimportant(name=dict["name"])
unimportant(name=set["name"])
unimportant(name=list["name"])

View File

@@ -35,33 +35,3 @@ def f4():
_ = 1
__ = 1
_discarded = 1
a = 1
def f5():
global a
# Used in `f7` via `nonlocal`.
b = 1
def f6():
# F841
b = 1
def f7():
nonlocal b
def f6():
annotations = []
assert len([annotations for annotations in annotations])
def f7():
def connect():
return None, None
with connect() as (connection, cursor):
cursor.execute("SELECT * FROM users")

View File

@@ -38,20 +38,5 @@ def function(
def used(do):
return do
used("a", True)
used(do=True)
# Avoid FBT003 for explicitly allowed methods.
"""
FBT003 Boolean positional value on dict
"""
a = {"a": "b"}
a.get("hello", False)
{}.get("hello", False)
{}.setdefault("hello", True)
{}.pop("hello", False)
{}.pop(True, False)
dict.fromkeys(("world",), True)
{}.deploy(True, False)

View File

@@ -18,14 +18,6 @@ def f() -> None:
# Invalid (and unimplemented)
d = 1 # noqa: F841, W191
# fmt: off
# Invalid - no space before #
d = 1# noqa: E501
# Invalid - many spaces before #
d = 1 # noqa: E501
# fmt: on
# Valid
_ = """Lorem ipsum dolor sit amet.

View File

@@ -30,14 +30,6 @@ class Class:
def __init_subclass__(self, default_name, **kwargs):
...
@classmethod
def class_method_with_positional_only_argument(cls, x, /, other):
...
@classmethod
def bad_class_method_with_positional_only_argument(self, x, /, other):
...
class MetaClass(ABCMeta):
def bad_method(self):

View File

@@ -10,27 +10,6 @@ def good__():
pass
def nested():
def __bad__():
pass
def __good():
pass
def good__():
pass
class Class:
def __good__(self):
pass
# https://peps.python.org/pep-0562/
def __getattr__(name):
pass
# https://peps.python.org/pep-0562/
def __dir__():
pass

View File

@@ -1,5 +0,0 @@
exit(0)
def main():
exit(2)

View File

@@ -1,10 +0,0 @@
import sys
exit(0)
def main():
exit(1)
sys.exit(2)

View File

@@ -1,7 +0,0 @@
import sys as sys2
exit(0)
def main():
exit(1)

View File

@@ -1,7 +0,0 @@
from sys import exit
exit(0)
def main():
exit(1)

View File

@@ -1,7 +0,0 @@
from sys import exit as exit2
exit(0)
def main():
exit(1)

View File

@@ -1,7 +0,0 @@
from sys import *
exit(0)
def main():
exit(1)

View File

@@ -1,12 +0,0 @@
exit(0)
def exit(e):
pass
exit(1)
def main():
exit(2)

View File

@@ -1,14 +0,0 @@
breakpoint()
import pdb
from builtins import breakpoint
from pdb import set_trace as st
from celery.contrib.rdb import set_trace
from celery.contrib import rdb
import celery.contrib.rdb
breakpoint()
st()
set_trace()

View File

@@ -24,7 +24,3 @@ from typing import List as IList
def f(x: IList[str]) -> None:
...
def f(x: "List[str]") -> None:
...

View File

@@ -1,43 +1,40 @@
import typing
from typing import Optional
from typing import Union
def f(x: Optional[str]) -> None:
...
import typing
def f(x: typing.Optional[str]) -> None:
...
from typing import Union
def f(x: Union[str, int, Union[float, bytes]]) -> None:
...
import typing
def f(x: typing.Union[str, int]) -> None:
...
def f(x: typing.Union[(str, int)]) -> None:
...
def f(x: typing.Union[(str, int), float]) -> None:
...
from typing import Union
def f(x: "Union[str, int, Union[float, bytes]]") -> None:
...
import typing
def f(x: "typing.Union[str, int]") -> None:
...
def f(x: Union["str", int]) -> None:
...
def f(x: Union[("str", "int"), float]) -> None:
...

View File

@@ -1,3 +1,3 @@
# coding=utf8
print("Hello world")
print('Hello world')

View File

@@ -1,3 +0,0 @@
# coding=utf8 # noqa: U009
print("Hello world")

View File

@@ -1,5 +1,4 @@
from typing import TypedDict, NotRequired, Literal
import typing
# dict literal
MyType1 = TypedDict("MyType1", {"a": int, "b": str})
@@ -28,6 +27,3 @@ MyType9 = TypedDict("MyType9", {"in": int, "x-y": int})
# using Literal type
MyType10 = TypedDict("MyType10", {"key": Literal["value"]})
# using namespace TypedDict
MyType11 = typing.TypedDict("MyType11", {"key": int})

View File

@@ -1,22 +0,0 @@
from typing import NamedTuple
import typing
# with complex annotations
NT1 = NamedTuple("NT1", [("a", int), ("b", tuple[str, ...])])
# with default values as list
NT2 = NamedTuple(
"NT2",
[("a", int), ("b", str), ("c", list[bool])],
defaults=["foo", [True]],
)
# with namespace
NT3 = typing.NamedTuple("NT3", [("a", int), ("b", str)])
# with too many default values
NT4 = NamedTuple(
"NT4",
[("a", int), ("b", str)],
defaults=[1, "bar", "baz"],
)

View File

@@ -1,80 +0,0 @@
open("foo", "U")
open("foo", "Ur")
open("foo", "Ub")
open("foo", "rUb")
open("foo", "r")
open("foo", "rt")
open("f", "r", encoding="UTF-8")
open("f", "wt")
with open("foo", "U") as f:
pass
with open("foo", "Ur") as f:
pass
with open("foo", "Ub") as f:
pass
with open("foo", "rUb") as f:
pass
with open("foo", "r") as f:
pass
with open("foo", "rt") as f:
pass
with open("foo", "r", encoding="UTF-8") as f:
pass
with open("foo", "wt") as f:
pass
open(f("a", "b", "c"), "U")
open(f("a", "b", "c"), "Ub")
with open(f("a", "b", "c"), "U") as f:
pass
with open(f("a", "b", "c"), "Ub") as f:
pass
with open("foo", "U") as fa, open("bar", "U") as fb:
pass
with open("foo", "Ub") as fa, open("bar", "Ub") as fb:
pass
open("foo", mode="U")
open(name="foo", mode="U")
open(mode="U", name="foo")
with open("foo", mode="U") as f:
pass
with open(name="foo", mode="U") as f:
pass
with open(mode="U", name="foo") as f:
pass
open("foo", mode="Ub")
open(name="foo", mode="Ub")
open(mode="Ub", name="foo")
with open("foo", mode="Ub") as f:
pass
with open(name="foo", mode="Ub") as f:
pass
with open(mode="Ub", name="foo") as f:
pass
open(file="foo", mode='U', buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
open(file="foo", buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None, mode='U')
open(file="foo", buffering=- 1, encoding=None, errors=None, mode='U', newline=None, closefd=True, opener=None)
open(mode='U', file="foo", buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
open(file="foo", mode='Ub', buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
open(file="foo", buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None, mode='Ub')
open(file="foo", buffering=- 1, encoding=None, errors=None, mode='Ub', newline=None, closefd=True, opener=None)
open(mode='Ub', file="foo", buffering=- 1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
open = 1
open("foo", "U")
open("foo", "Ur")
open("foo", "Ub")
open("foo", "rUb")
open("foo", "r")
open("foo", "rt")
open("f", "r", encoding="UTF-8")
open("f", "wt")

View File

@@ -44,7 +44,7 @@ expectation.expected.add((
@expect("D407: Missing dashed underline after section ('Returns')",
arg_count=3)
@expect("D413: Missing blank line after last section ('Raises')", arg_count=3)
def fetch_bigtable_rows(big_table, keys, other_silly_variable=None, **kwargs):
def fetch_bigtable_rows(big_table, keys, other_silly_variable=None):
"""Fetches rows from a Bigtable.
Retrieves rows pertaining to the given keys from the Table instance
@@ -57,7 +57,6 @@ def fetch_bigtable_rows(big_table, keys, other_silly_variable=None, **kwargs):
to fetch.
other_silly_variable: Another optional variable, that has a much
longer name than the other args, and which does nothing.
**kwargs: More keyword arguments.
Returns:
A dict mapping keys to the corresponding table row data

View File

@@ -73,7 +73,7 @@ expectation.expected.add((
"(found 'A')", arg_count=3)
@expect("D413: Missing blank line after last section ('Examples')",
arg_count=3)
def foo(var1, var2, long_var_name='hi', **kwargs):
def foo(var1, var2, long_var_name='hi'):
r"""A one-line summary that does not use variable names.
Several sentences providing an extended description. Refer to
@@ -91,8 +91,6 @@ def foo(var1, var2, long_var_name='hi', **kwargs):
detail, e.g. ``(N,) ndarray`` or ``array_like``.
long_var_name : {'hi', 'ho'}, optional
Choices in brackets, default first when optional.
**kwargs : int
More keyword arguments.
Returns
-------

View File

@@ -1,7 +1,6 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import List, Optional
from models import (
Fruit,
@@ -29,12 +28,3 @@ class Foo:
@classmethod
def d(cls) -> Fruit:
return cls(x=0, y=0)
def f(x: int) -> List[int]:
y = List[int]()
y.append(x)
return y
x: Optional[int] = None

View File

@@ -1,4 +0,0 @@
from .a import a
from ..a import a
from ..b import a
from .b import a

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff_dev"
version = "0.0.145"
version = "0.0.128"
edition = "2021"
[dependencies]
@@ -8,10 +8,10 @@ anyhow = { version = "1.0.66" }
clap = { version = "4.0.1", features = ["derive"] }
codegen = { version = "0.2.0" }
itertools = { version = "0.10.5" }
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a8725d161fe8b3ed73a6758b21e177" }
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "a13ec97dd4eb925bde4d426c6e422582793b260c" }
ruff = { path = ".." }
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "f885db8c61514f069979861f6b3bd83292086231" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "f885db8c61514f069979861f6b3bd83292086231" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "f885db8c61514f069979861f6b3bd83292086231" }
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb" }
strum = { version = "0.24.1", features = ["strum_macros"] }
strum_macros = { version = "0.24.3" }

View File

@@ -24,7 +24,7 @@ pub struct Cli {
pub fn main(cli: &Cli) -> Result<()> {
// Build up a map from prefix to matching CheckCodes.
let mut prefix_to_codes: BTreeMap<String, BTreeSet<CheckCode>> = BTreeMap::default();
let mut prefix_to_codes: BTreeMap<String, BTreeSet<CheckCode>> = Default::default();
for check_code in CheckCode::iter() {
let as_ref: String = check_code.as_ref().to_string();
let prefix_len = as_ref
@@ -77,7 +77,6 @@ pub fn main(cli: &Cli) -> Result<()> {
.arg_ref_self()
.ret(Type::new("Vec<CheckCode>"))
.vis("pub")
.line("#[allow(clippy::match_same_arms)]")
.line("match self {");
for (prefix, codes) in &prefix_to_codes {
gen = gen.line(format!(
@@ -97,7 +96,6 @@ pub fn main(cli: &Cli) -> Result<()> {
.arg_ref_self()
.ret(Type::new("PrefixSpecificity"))
.vis("pub")
.line("#[allow(clippy::match_same_arms)]")
.line("match self {");
for prefix in prefix_to_codes.keys() {
let num_numeric = prefix.chars().filter(|char| char.is_numeric()).count();
@@ -106,7 +104,7 @@ pub fn main(cli: &Cli) -> Result<()> {
2 => "Tens",
1 => "Hundreds",
0 => "Category",
_ => panic!("Invalid prefix: {prefix}"),
_ => panic!("Invalid prefix: {}", prefix),
};
gen = gen.line(format!(
"CheckCodePrefix::{prefix} => PrefixSpecificity::{},",
@@ -117,8 +115,7 @@ pub fn main(cli: &Cli) -> Result<()> {
// Construct the output contents.
let mut output = String::new();
output
.push_str("//! File automatically generated by `examples/generate_check_code_prefix.rs`.");
output.push_str("//! File automatically generated by examples/generate_check_code_prefix.rs.");
output.push('\n');
output.push('\n');
output.push_str("use serde::{{Serialize, Deserialize}};");
@@ -130,28 +127,13 @@ pub fn main(cli: &Cli) -> Result<()> {
output.push('\n');
output.push('\n');
output.push_str(&scope.to_string());
output.push('\n');
output.push('\n');
// Add the list of output categories (not generated).
output.push_str("pub const CATEGORIES: &[CheckCodePrefix] = &[");
output.push('\n');
for prefix in prefix_to_codes.keys() {
if prefix.chars().all(char::is_alphabetic) {
output.push_str(&format!("CheckCodePrefix::{prefix},"));
output.push('\n');
}
}
output.push_str("];");
output.push('\n');
output.push('\n');
// Write the output to `src/checks_gen.rs` (or stdout).
if cli.dry_run {
println!("{output}");
println!("{}", output);
} else {
let mut f = OpenOptions::new().write(true).truncate(true).open(FILE)?;
write!(f, "{output}")?;
write!(f, "{}", output)?;
}
Ok(())

View File

@@ -61,7 +61,7 @@ pub fn main(cli: &Cli) -> Result<()> {
}
if cli.dry_run {
print!("{output}");
print!("{}", output);
} else {
// Read the existing file.
let file = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
@@ -84,9 +84,9 @@ pub fn main(cli: &Cli) -> Result<()> {
// Write the prefix, new contents, and suffix.
let mut f = OpenOptions::new().write(true).truncate(true).open(&file)?;
write!(f, "{prefix}\n\n")?;
write!(f, "{output}")?;
write!(f, "{suffix}")?;
write!(f, "{}\n\n", prefix)?;
write!(f, "{}", output)?;
write!(f, "{}", suffix)?;
}
Ok(())

View File

@@ -19,7 +19,7 @@ pub fn main(cli: &Cli) -> Result<()> {
let contents = fs::read_to_string(&cli.file)?;
let python_ast = parser::parse_program(&contents, &cli.file.to_string_lossy())?;
let mut generator = SourceGenerator::new();
generator.unparse_suite(&python_ast);
generator.unparse_suite(&python_ast)?;
println!("{}", generator.generate()?);
Ok(())
}

View File

@@ -1,16 +1,3 @@
#![allow(
clippy::collapsible_else_if,
clippy::collapsible_if,
clippy::implicit_hasher,
clippy::match_same_arms,
clippy::missing_errors_doc,
clippy::missing_panics_doc,
clippy::module_name_repetitions,
clippy::must_use_candidate,
clippy::similar_names,
clippy::too_many_lines
)]
pub mod generate_check_code_prefix;
pub mod generate_rules_table;
pub mod generate_source_code;

View File

@@ -1,16 +1,3 @@
#![allow(
clippy::collapsible_else_if,
clippy::collapsible_if,
clippy::implicit_hasher,
clippy::match_same_arms,
clippy::missing_errors_doc,
clippy::missing_panics_doc,
clippy::module_name_repetitions,
clippy::must_use_candidate,
clippy::similar_names,
clippy::too_many_lines
)]
use anyhow::Result;
use clap::{Parser, Subcommand};
use ruff_dev::{

View File

@@ -17,6 +17,6 @@ pub struct Cli {
pub fn main(cli: &Cli) -> Result<()> {
let contents = fs::read_to_string(&cli.file)?;
let python_ast = parser::parse_program(&contents, &cli.file.to_string_lossy())?;
println!("{python_ast:#?}");
println!("{:#?}", python_ast);
Ok(())
}

View File

@@ -1,4 +1,4 @@
//! Print the `LibCST` CST for a given Python file.
//! Print the LibCST CST for a given Python file.
use std::fs;
use std::path::PathBuf;
@@ -17,7 +17,7 @@ pub fn main(cli: &Cli) -> Result<()> {
let contents = fs::read_to_string(&cli.file)?;
match libcst_native::parse_module(&contents, None) {
Ok(python_cst) => {
println!("{python_cst:#?}");
println!("{:#?}", python_cst);
Ok(())
}
Err(_) => Err(anyhow::anyhow!("Failed to parse CST")),

View File

@@ -17,7 +17,7 @@ pub struct Cli {
pub fn main(cli: &Cli) -> Result<()> {
let contents = fs::read_to_string(&cli.file)?;
for (_, tok, _) in lexer::make_tokenizer(&contents).flatten() {
println!("{tok:#?}");
println!("{:#?}", tok);
}
Ok(())
}

View File

@@ -1,13 +1,9 @@
use fnv::{FnvHashMap, FnvHashSet};
use once_cell::sync::Lazy;
use regex::Regex;
use rustc_hash::{FxHashMap, FxHashSet};
use rustpython_ast::{
Arguments, Excepthandler, ExcepthandlerKind, Expr, ExprKind, Location, Stmt, StmtKind,
};
use crate::ast::types::Range;
use crate::SourceCodeLocator;
use rustpython_ast::{Excepthandler, ExcepthandlerKind, Expr, ExprKind, Location, StmtKind};
#[inline(always)]
fn collect_call_path_inner<'a>(expr: &'a Expr, parts: &mut Vec<&'a str>) {
match &expr.node {
ExprKind::Call { func, .. } => {
@@ -25,6 +21,7 @@ fn collect_call_path_inner<'a>(expr: &'a Expr, parts: &mut Vec<&'a str>) {
}
/// Convert an `Expr` to its call path (like `List`, or `typing.List`).
#[inline(always)]
pub fn compose_call_path(expr: &Expr) -> Option<String> {
let segments = collect_call_paths(expr);
if segments.is_empty() {
@@ -35,6 +32,7 @@ pub fn compose_call_path(expr: &Expr) -> Option<String> {
}
/// Convert an `Expr` to its call path segments (like ["typing", "List"]).
#[inline(always)]
pub fn collect_call_paths(expr: &Expr) -> Vec<&str> {
let mut segments = vec![];
collect_call_path_inner(expr, &mut segments);
@@ -44,7 +42,7 @@ pub fn collect_call_paths(expr: &Expr) -> Vec<&str> {
/// Rewrite any import aliases on a call path.
pub fn dealias_call_path<'a>(
call_path: Vec<&'a str>,
import_aliases: &FxHashMap<&str, &'a str>,
import_aliases: &FnvHashMap<&str, &'a str>,
) -> Vec<&'a str> {
if let Some(head) = call_path.first() {
if let Some(origin) = import_aliases.get(head) {
@@ -78,8 +76,8 @@ pub fn match_module_member(
expr: &Expr,
module: &str,
member: &str,
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
import_aliases: &FxHashMap<&str, &str>,
from_imports: &FnvHashMap<&str, FnvHashSet<&str>>,
import_aliases: &FnvHashMap<&str, &str>,
) -> bool {
match_call_path(
&dealias_call_path(collect_call_paths(expr), import_aliases),
@@ -96,7 +94,7 @@ pub fn match_call_path(
call_path: &[&str],
module: &str,
member: &str,
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
from_imports: &FnvHashMap<&str, FnvHashSet<&str>>,
) -> bool {
// If we have no segments, we can't ever match.
let num_segments = call_path.len();
@@ -120,9 +118,10 @@ pub fn match_call_path(
// `Match`).
if num_segments == 0 {
module.is_empty()
|| from_imports.get(module).map_or(false, |imports| {
imports.contains(member) || imports.contains("*")
})
|| from_imports
.get(module)
.map(|imports| imports.contains(member) || imports.contains("*"))
.unwrap_or(false)
} else {
let components: Vec<&str> = module.split('.').collect();
@@ -146,7 +145,8 @@ pub fn match_call_path(
let member = components[cut];
if from_imports
.get(&module.as_str())
.map_or(false, |imports| imports.contains(member))
.map(|imports| imports.contains(member))
.unwrap_or(false)
{
return true;
}
@@ -215,27 +215,6 @@ pub fn extract_handler_names(handlers: &[Excepthandler]) -> Vec<Vec<&str>> {
handler_names
}
/// Return the set of all bound argument names.
pub fn collect_arg_names<'a>(arguments: &'a Arguments) -> FxHashSet<&'a str> {
let mut arg_names: FxHashSet<&'a str> = FxHashSet::default();
for arg in &arguments.posonlyargs {
arg_names.insert(arg.node.arg.as_str());
}
for arg in &arguments.args {
arg_names.insert(arg.node.arg.as_str());
}
if let Some(arg) = &arguments.vararg {
arg_names.insert(arg.node.arg.as_str());
}
for arg in &arguments.kwonlyargs {
arg_names.insert(arg.node.arg.as_str());
}
if let Some(arg) = &arguments.kwarg {
arg_names.insert(arg.node.arg.as_str());
}
arg_names
}
/// Returns `true` if a call is an argumented `super` invocation.
pub fn is_super_call_with_arguments(func: &Expr, args: &[Expr]) -> bool {
// Check: is this a `super` call?
@@ -271,7 +250,7 @@ pub fn to_module_and_member(target: &str) -> (&str, &str) {
/// Convert a location within a file (relative to `base`) to an absolute
/// position.
pub fn to_absolute(relative: Location, base: Location) -> Location {
pub fn to_absolute(relative: &Location, base: &Location) -> Location {
if relative.row() == 1 {
Location::new(
relative.row() + base.row() - 1,
@@ -282,38 +261,10 @@ pub fn to_absolute(relative: Location, base: Location) -> Location {
}
}
/// Return `true` if a `Stmt` has leading content.
pub fn match_leading_content(stmt: &Stmt, locator: &SourceCodeLocator) -> bool {
let range = Range {
location: Location::new(stmt.location.row(), 0),
end_location: stmt.location,
};
let prefix = locator.slice_source_code_range(&range);
prefix.chars().any(|char| !char.is_whitespace())
}
/// Return `true` if a `Stmt` has trailing content.
pub fn match_trailing_content(stmt: &Stmt, locator: &SourceCodeLocator) -> bool {
let range = Range {
location: stmt.end_location.unwrap(),
end_location: Location::new(stmt.end_location.unwrap().row() + 1, 0),
};
let suffix = locator.slice_source_code_range(&range);
for char in suffix.chars() {
if char == '#' {
return false;
}
if !char.is_whitespace() {
return true;
}
}
false
}
#[cfg(test)]
mod tests {
use anyhow::Result;
use rustc_hash::{FxHashMap, FxHashSet};
use fnv::{FnvHashMap, FnvHashSet};
use rustpython_parser::parser;
use crate::ast::helpers::match_module_member;
@@ -325,8 +276,8 @@ mod tests {
&expr,
"",
"list",
&FxHashMap::default(),
&FxHashMap::default(),
&FnvHashMap::default(),
&FnvHashMap::default(),
));
Ok(())
}
@@ -338,8 +289,8 @@ mod tests {
&expr,
"typing.re",
"Match",
&FxHashMap::default(),
&FxHashMap::default(),
&FnvHashMap::default(),
&FnvHashMap::default(),
));
Ok(())
}
@@ -351,16 +302,16 @@ mod tests {
&expr,
"typing.re",
"Match",
&FxHashMap::default(),
&FxHashMap::default(),
&FnvHashMap::default(),
&FnvHashMap::default(),
));
let expr = parser::parse_expression("re.Match", "<filename>")?;
assert!(!match_module_member(
&expr,
"typing.re",
"Match",
&FxHashMap::default(),
&FxHashMap::default(),
&FnvHashMap::default(),
&FnvHashMap::default(),
));
Ok(())
}
@@ -372,8 +323,8 @@ mod tests {
&expr,
"typing.re",
"Match",
&FxHashMap::from_iter([("typing.re", FxHashSet::from_iter(["*"]))]),
&FxHashMap::default()
&FnvHashMap::from_iter([("typing.re", FnvHashSet::from_iter(["*"]))]),
&FnvHashMap::default()
));
Ok(())
}
@@ -385,8 +336,8 @@ mod tests {
&expr,
"typing.re",
"Match",
&FxHashMap::from_iter([("typing.re", FxHashSet::from_iter(["Match"]))]),
&FxHashMap::default()
&FnvHashMap::from_iter([("typing.re", FnvHashSet::from_iter(["Match"]))]),
&FnvHashMap::default()
));
Ok(())
}
@@ -398,8 +349,8 @@ mod tests {
&expr,
"typing.re",
"Match",
&FxHashMap::from_iter([("typing", FxHashSet::from_iter(["re"]))]),
&FxHashMap::default()
&FnvHashMap::from_iter([("typing", FnvHashSet::from_iter(["re"]))]),
&FnvHashMap::default()
));
let expr = parser::parse_expression("match.Match", "<filename>")?;
@@ -407,8 +358,8 @@ mod tests {
&expr,
"typing.re.match",
"Match",
&FxHashMap::from_iter([("typing.re", FxHashSet::from_iter(["match"]))]),
&FxHashMap::default()
&FnvHashMap::from_iter([("typing.re", FnvHashSet::from_iter(["match"]))]),
&FnvHashMap::default()
));
let expr = parser::parse_expression("re.match.Match", "<filename>")?;
@@ -416,8 +367,8 @@ mod tests {
&expr,
"typing.re.match",
"Match",
&FxHashMap::from_iter([("typing", FxHashSet::from_iter(["re"]))]),
&FxHashMap::default()
&FnvHashMap::from_iter([("typing", FnvHashSet::from_iter(["re"]))]),
&FnvHashMap::default()
));
Ok(())
}
@@ -429,8 +380,8 @@ mod tests {
&expr,
"typing.re",
"Match",
&FxHashMap::from_iter([("typing.re", FxHashSet::from_iter(["Match"]))]),
&FxHashMap::from_iter([("IMatch", "Match")]),
&FnvHashMap::from_iter([("typing.re", FnvHashSet::from_iter(["Match"]))]),
&FnvHashMap::from_iter([("IMatch", "Match")]),
));
Ok(())
}
@@ -442,8 +393,8 @@ mod tests {
&expr,
"typing.re",
"Match",
&FxHashMap::default(),
&FxHashMap::from_iter([("t", "typing.re")]),
&FnvHashMap::default(),
&FnvHashMap::from_iter([("t", "typing.re")]),
));
Ok(())
}
@@ -455,8 +406,8 @@ mod tests {
&expr,
"typing.re",
"Match",
&FxHashMap::default(),
&FxHashMap::from_iter([("t", "typing")]),
&FnvHashMap::default(),
&FnvHashMap::from_iter([("t", "typing")]),
));
Ok(())
}

View File

@@ -3,4 +3,3 @@ pub mod operations;
pub mod relocate;
pub mod types;
pub mod visitor;
pub mod whitespace;

View File

@@ -4,6 +4,8 @@ use crate::ast::types::{BindingKind, Scope};
/// Extract the names bound to a given __all__ assignment.
pub fn extract_all_names(stmt: &Stmt, scope: &Scope) -> Vec<String> {
let mut names: Vec<String> = vec![];
fn add_to_names(names: &mut Vec<String>, elts: &[Expr]) {
for elt in elts {
if let ExprKind::Constant {
@@ -11,13 +13,11 @@ pub fn extract_all_names(stmt: &Stmt, scope: &Scope) -> Vec<String> {
..
} = &elt.node
{
names.push(value.to_string());
names.push(value.to_string())
}
}
}
let mut names: Vec<String> = vec![];
// Grab the existing bound __all__ values.
if let StmtKind::AugAssign { .. } = &stmt.node {
if let Some(binding) = scope.values.get("__all__") {
@@ -35,7 +35,7 @@ pub fn extract_all_names(stmt: &Stmt, scope: &Scope) -> Vec<String> {
} {
match &value.node {
ExprKind::List { elts, .. } | ExprKind::Tuple { elts, .. } => {
add_to_names(&mut names, elts);
add_to_names(&mut names, elts)
}
ExprKind::BinOp { left, right, .. } => {
let mut current_left = left;

View File

@@ -1,7 +1,7 @@
use std::sync::atomic::{AtomicUsize, Ordering};
use rustc_hash::FxHashMap;
use rustpython_ast::{Expr, Keyword, Stmt};
use fnv::FnvHashMap;
use rustpython_ast::{Expr, Keyword};
use rustpython_parser::ast::{Located, Location};
fn id() -> usize {
@@ -9,12 +9,6 @@ fn id() -> usize {
COUNTER.fetch_add(1, Ordering::Relaxed)
}
#[derive(Clone)]
pub enum Node<'a> {
Stmt(&'a Stmt),
Expr(&'a Expr),
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct Range {
pub location: Location,
@@ -25,7 +19,9 @@ impl Range {
pub fn from_located<T>(located: &Located<T>) -> Self {
Range {
location: located.location,
end_location: located.end_location.unwrap(),
end_location: located
.end_location
.expect("AST nodes should have end_location."),
}
}
}
@@ -58,7 +54,7 @@ pub struct Scope<'a> {
pub id: usize,
pub kind: ScopeKind<'a>,
pub import_starred: bool,
pub values: FxHashMap<&'a str, Binding>,
pub values: FnvHashMap<&'a str, Binding>,
}
impl<'a> Scope<'a> {
@@ -67,7 +63,7 @@ impl<'a> Scope<'a> {
id: id(),
kind,
import_starred: false,
values: FxHashMap::default(),
values: FnvHashMap::default(),
}
}
}
@@ -87,7 +83,6 @@ pub enum BindingKind {
Binding,
LoopVar,
Global,
Nonlocal,
Builtin,
ClassDefinition,
Definition,

View File

@@ -19,8 +19,8 @@ pub trait Visitor<'a> {
fn visit_constant(&mut self, constant: &'a Constant) {
walk_constant(self, constant);
}
fn visit_expr_context(&mut self, expr_context: &'a ExprContext) {
walk_expr_context(self, expr_context);
fn visit_expr_context(&mut self, expr_content: &'a ExprContext) {
walk_expr_context(self, expr_content);
}
fn visit_boolop(&mut self, boolop: &'a Boolop) {
walk_boolop(self, boolop);
@@ -249,7 +249,7 @@ pub fn walk_stmt<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, stmt: &'a Stmt) {
visitor.visit_stmt(stmt);
}
for excepthandler in handlers {
visitor.visit_excepthandler(excepthandler);
visitor.visit_excepthandler(excepthandler)
}
for stmt in orelse {
visitor.visit_stmt(stmt);
@@ -447,7 +447,7 @@ pub fn walk_expr<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, expr: &'a Expr) {
pub fn walk_constant<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, constant: &'a Constant) {
if let Constant::Tuple(constants) = constant {
for constant in constants {
visitor.visit_constant(constant);
visitor.visit_constant(constant)
}
}
}
@@ -456,8 +456,8 @@ pub fn walk_comprehension<'a, V: Visitor<'a> + ?Sized>(
visitor: &mut V,
comprehension: &'a Comprehension,
) {
visitor.visit_expr(&comprehension.iter);
visitor.visit_expr(&comprehension.target);
visitor.visit_expr(&comprehension.iter);
for expr in &comprehension.ifs {
visitor.visit_expr(expr);
}
@@ -577,6 +577,7 @@ pub fn walk_pattern<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, pattern: &'a P
}
#[allow(unused_variables)]
#[inline(always)]
pub fn walk_expr_context<'a, V: Visitor<'a> + ?Sized>(
visitor: &mut V,
expr_context: &'a ExprContext,
@@ -584,16 +585,21 @@ pub fn walk_expr_context<'a, V: Visitor<'a> + ?Sized>(
}
#[allow(unused_variables)]
#[inline(always)]
pub fn walk_boolop<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, boolop: &'a Boolop) {}
#[allow(unused_variables)]
#[inline(always)]
pub fn walk_operator<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, operator: &'a Operator) {}
#[allow(unused_variables)]
#[inline(always)]
pub fn walk_unaryop<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, unaryop: &'a Unaryop) {}
#[allow(unused_variables)]
#[inline(always)]
pub fn walk_cmpop<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, cmpop: &'a Cmpop) {}
#[allow(unused_variables)]
#[inline(always)]
pub fn walk_alias<'a, V: Visitor<'a> + ?Sized>(visitor: &mut V, alias: &'a Alias) {}

View File

@@ -10,6 +10,8 @@ use crate::autofix::{Fix, Patch};
use crate::checks::Check;
use crate::source_code_locator::SourceCodeLocator;
// TODO(charlie): The model here is awkward because `Apply` is only relevant at
// higher levels in the execution flow.
#[derive(Hash)]
pub enum Mode {
Generate,
@@ -17,55 +19,55 @@ pub enum Mode {
None,
}
impl From<bool> for Mode {
fn from(value: bool) -> Self {
if value {
Mode::Apply
} else {
Mode::None
impl Mode {
/// Return `true` if a patch should be generated under the given `Mode`.
pub fn patch(&self) -> bool {
match &self {
Mode::Generate => true,
Mode::Apply => true,
Mode::None => false,
}
}
}
impl From<&Mode> for bool {
fn from(value: &Mode) -> Self {
impl From<bool> for Mode {
fn from(value: bool) -> Self {
match value {
Mode::Generate | Mode::Apply => true,
Mode::None => false,
true => Mode::Apply,
false => Mode::None,
}
}
}
/// Auto-fix errors in a file, and write the fixed source code to disk.
pub fn fix_file<'a>(
checks: &'a [Check],
checks: &'a mut [Check],
locator: &'a SourceCodeLocator<'a>,
) -> Option<(Cow<'a, str>, usize)> {
) -> Option<Cow<'a, str>> {
if checks.iter().all(|check| check.fix.is_none()) {
return None;
}
Some(apply_fixes(
checks.iter().filter_map(|check| check.fix.as_ref()),
checks.iter_mut().filter_map(|check| check.fix.as_mut()),
locator,
))
}
/// Apply a series of fixes.
fn apply_fixes<'a>(
fixes: impl Iterator<Item = &'a Fix>,
fixes: impl Iterator<Item = &'a mut Fix>,
locator: &'a SourceCodeLocator<'a>,
) -> (Cow<'a, str>, usize) {
) -> Cow<'a, str> {
let mut output = RopeBuilder::new();
let mut last_pos: Location = Location::new(1, 0);
let mut applied: BTreeSet<&Patch> = BTreeSet::default();
let mut num_fixed: usize = 0;
for fix in fixes.sorted_by_key(|fix| fix.patch.location) {
// If we already applied an identical fix as part of another correction, skip
// any re-application.
if applied.contains(&fix.patch) {
num_fixed += 1;
fix.applied = true;
continue;
}
@@ -88,18 +90,19 @@ fn apply_fixes<'a>(
// Track that the fix was applied.
last_pos = fix.patch.end_location;
applied.insert(&fix.patch);
num_fixed += 1;
fix.applied = true;
}
// Add the remaining content.
let slice = locator.slice_source_code_at(last_pos);
let slice = locator.slice_source_code_at(&last_pos);
output.append(&slice);
(Cow::from(output.finish()), num_fixed)
Cow::from(output.finish())
}
#[cfg(test)]
mod tests {
use anyhow::Result;
use rustpython_parser::ast::Location;
use crate::autofix::fixer::apply_fixes;
@@ -107,117 +110,115 @@ mod tests {
use crate::SourceCodeLocator;
#[test]
fn empty_file() {
let fixes = vec![];
let locator = SourceCodeLocator::new(r#""#);
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
assert_eq!(contents, "");
assert_eq!(fixed, 0);
fn empty_file() -> Result<()> {
let mut fixes = vec![];
let locator = SourceCodeLocator::new("");
let actual = apply_fixes(fixes.iter_mut(), &locator);
let expected = "";
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn apply_single_replacement() {
let fixes = vec![Fix {
fn apply_single_replacement() -> Result<()> {
let mut fixes = vec![Fix {
patch: Patch {
content: "Bar".to_string(),
location: Location::new(1, 8),
end_location: Location::new(1, 14),
},
applied: false,
}];
let locator = SourceCodeLocator::new(
r#"
class A(object):
...
"#
.trim(),
"class A(object):
...
",
);
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
assert_eq!(
contents,
r#"
class A(Bar):
...
"#
.trim(),
);
assert_eq!(fixed, 1);
let actual = apply_fixes(fixes.iter_mut(), &locator);
let expected = "class A(Bar):
...
";
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn apply_single_removal() {
let fixes = vec![Fix {
fn apply_single_removal() -> Result<()> {
let mut fixes = vec![Fix {
patch: Patch {
content: String::new(),
content: "".to_string(),
location: Location::new(1, 7),
end_location: Location::new(1, 15),
},
applied: false,
}];
let locator = SourceCodeLocator::new(
r#"
class A(object):
...
"#
.trim(),
"class A(object):
...
",
);
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
assert_eq!(
contents,
r#"
class A:
...
"#
.trim()
);
assert_eq!(fixed, 1);
let actual = apply_fixes(fixes.iter_mut(), &locator);
let expected = "class A:
...
";
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn apply_double_removal() {
let fixes = vec![
fn apply_double_removal() -> Result<()> {
let mut fixes = vec![
Fix {
patch: Patch {
content: String::new(),
content: "".to_string(),
location: Location::new(1, 7),
end_location: Location::new(1, 16),
},
applied: false,
},
Fix {
patch: Patch {
content: String::new(),
content: "".to_string(),
location: Location::new(1, 16),
end_location: Location::new(1, 23),
},
applied: false,
},
];
let locator = SourceCodeLocator::new(
r#"
class A(object, object):
...
"#
.trim(),
"class A(object, object):
...
",
);
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
let actual = apply_fixes(fixes.iter_mut(), &locator);
assert_eq!(
contents,
r#"
class A:
...
"#
.trim()
);
assert_eq!(fixed, 2);
let expected = "class A:
...
";
assert_eq!(actual, expected);
Ok(())
}
#[test]
fn ignore_overlapping_fixes() {
let fixes = vec![
fn ignore_overlapping_fixes() -> Result<()> {
let mut fixes = vec![
Fix {
patch: Patch {
content: String::new(),
content: "".to_string(),
location: Location::new(1, 7),
end_location: Location::new(1, 15),
},
applied: false,
},
Fix {
patch: Patch {
@@ -225,24 +226,22 @@ class A:
location: Location::new(1, 9),
end_location: Location::new(1, 11),
},
applied: false,
},
];
let locator = SourceCodeLocator::new(
r#"
class A(object):
"class A(object):
...
"#
.trim(),
",
);
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
assert_eq!(
contents,
r#"
class A:
let actual = apply_fixes(fixes.iter_mut(), &locator);
let expected = "class A:
...
"#
.trim(),
);
assert_eq!(fixed, 1);
";
assert_eq!(actual, expected);
Ok(())
}
}

View File

@@ -14,16 +14,18 @@ pub struct Patch {
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Fix {
pub patch: Patch,
pub applied: bool,
}
impl Fix {
pub fn deletion(start: Location, end: Location) -> Self {
Self {
patch: Patch {
content: String::new(),
content: "".to_string(),
location: start,
end_location: end,
},
applied: false,
}
}
@@ -34,6 +36,7 @@ impl Fix {
location: start,
end_location: end,
},
applied: false,
}
}
@@ -44,16 +47,18 @@ impl Fix {
location: at,
end_location: at,
},
applied: false,
}
}
pub fn dummy(location: Location) -> Self {
Self {
patch: Patch {
content: String::new(),
content: "".to_string(),
location,
end_location: location,
},
applied: false,
}
}
}

View File

@@ -1,11 +1,19 @@
// cacache uses asyncd-std which has no wasm support, so currently no caching
// support on wasm
#![cfg_attr(
target_family = "wasm",
allow(unused_imports, unused_variables, dead_code)
)]
use std::collections::hash_map::DefaultHasher;
use std::fs;
use std::fs::{create_dir_all, File, Metadata};
use std::hash::{Hash, Hasher};
use std::io::Write;
use std::path::Path;
use anyhow::Result;
#[cfg(not(target_family = "wasm"))]
use cacache::Error::EntryNotFound;
use filetime::FileTime;
use log::error;
use path_absolutize::Absolutize;
@@ -63,10 +71,9 @@ impl Mode {
impl From<bool> for Mode {
fn from(value: bool) -> Self {
if value {
Mode::ReadWrite
} else {
Mode::None
match value {
true => Mode::ReadWrite,
false => Mode::None,
}
}
}
@@ -75,59 +82,28 @@ fn cache_dir() -> &'static str {
"./.ruff_cache"
}
fn content_dir() -> &'static str {
"content"
}
fn cache_key(path: &Path, settings: &Settings, autofix: &fixer::Mode) -> u64 {
fn cache_key(path: &Path, settings: &Settings, autofix: &fixer::Mode) -> String {
let mut hasher = DefaultHasher::new();
CARGO_PKG_VERSION.hash(&mut hasher);
path.absolutize().unwrap().hash(&mut hasher);
settings.hash(&mut hasher);
autofix.hash(&mut hasher);
hasher.finish()
format!(
"{}@{}@{}",
path.absolutize().unwrap().to_string_lossy(),
CARGO_PKG_VERSION,
hasher.finish()
)
}
/// Initialize the cache directory.
pub fn init() -> Result<()> {
let path = Path::new(cache_dir());
// Create the cache directories.
create_dir_all(path.join(content_dir()))?;
// Add the CACHEDIR.TAG.
if !cachedir::is_tagged(path)? {
cachedir::add_tag(path)?;
let gitignore_path = Path::new(cache_dir()).join(".gitignore");
if gitignore_path.exists() {
return Ok(());
}
// Add the .gitignore.
let gitignore_path = path.join(".gitignore");
if !gitignore_path.exists() {
let mut file = File::create(gitignore_path)?;
file.write_all(b"*")?;
}
Ok(())
create_dir_all(cache_dir())?;
let mut file = File::create(gitignore_path)?;
file.write_all(b"*").map_err(|e| e.into())
}
fn write_sync(key: u64, value: &[u8]) -> Result<(), std::io::Error> {
fs::write(
Path::new(cache_dir())
.join(content_dir())
.join(format!("{key:x}")),
value,
)
}
fn read_sync(key: u64) -> Result<Vec<u8>, std::io::Error> {
fs::read(
Path::new(cache_dir())
.join(content_dir())
.join(format!("{key:x}")),
)
}
/// Get a value from the cache.
pub fn get(
path: &Path,
metadata: &Metadata,
@@ -139,8 +115,9 @@ pub fn get(
return None;
};
if let Ok(encoded) = read_sync(cache_key(path, settings, autofix)) {
match bincode::deserialize::<CheckResult>(&encoded[..]) {
#[cfg(not(target_family = "wasm"))] // cacache needs async-std which doesn't support wasm
match cacache::read_sync(cache_dir(), cache_key(path, settings, autofix)) {
Ok(encoded) => match bincode::deserialize::<CheckResult>(&encoded[..]) {
Ok(CheckResult {
metadata: CacheMetadata { mtime },
messages,
@@ -150,12 +127,13 @@ pub fn get(
}
}
Err(e) => error!("Failed to deserialize encoded cache entry: {e:?}"),
}
},
Err(EntryNotFound(..)) => {}
Err(e) => error!("Failed to read from cache: {e:?}"),
}
None
}
/// Set a value in the cache.
pub fn set(
path: &Path,
metadata: &Metadata,
@@ -168,16 +146,19 @@ pub fn set(
return;
};
#[cfg(not(target_family = "wasm"))] // modification date not supported on wasm
let check_result = CheckResultRef {
metadata: &CacheMetadata {
mtime: FileTime::from_last_modification_time(metadata).unix_seconds(),
},
messages,
};
if let Err(e) = write_sync(
#[cfg(not(target_family = "wasm"))] // cacache needs async-std which doesn't support wasm
if let Err(e) = cacache::write_sync(
cache_dir(),
cache_key(path, settings, autofix),
&bincode::serialize(&check_result).unwrap(),
bincode::serialize(&check_result).unwrap(),
) {
error!("Failed to write to cache: {e:?}");
error!("Failed to write to cache: {e:?}")
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,7 @@ use nohash_hasher::IntSet;
use rustpython_parser::ast::Suite;
use crate::ast::visitor::Visitor;
use crate::autofix::fixer;
use crate::checks::Check;
use crate::isort;
use crate::isort::track::ImportTracker;
@@ -14,12 +15,12 @@ fn check_import_blocks(
tracker: ImportTracker,
locator: &SourceCodeLocator,
settings: &Settings,
autofix: bool,
autofix: &fixer::Mode,
) -> Vec<Check> {
let mut checks = vec![];
for block in tracker.into_iter() {
if !block.is_empty() {
if let Some(check) = isort::plugins::check_imports(&block, locator, settings, autofix) {
if let Some(check) = isort::plugins::check_imports(block, locator, settings, autofix) {
checks.push(check);
}
}
@@ -32,7 +33,7 @@ pub fn check_imports(
locator: &SourceCodeLocator,
exclusions: &IntSet<usize>,
settings: &Settings,
autofix: bool,
autofix: &fixer::Mode,
) -> Vec<Check> {
let mut tracker = ImportTracker::new(exclusions);
for stmt in python_ast {

View File

@@ -6,7 +6,7 @@ use regex::Regex;
use rustpython_parser::ast::Location;
use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::autofix::{fixer, Fix};
use crate::checks::{Check, CheckCode, CheckKind};
use crate::noqa;
use crate::noqa::Directive;
@@ -14,18 +14,15 @@ use crate::settings::Settings;
// Regex from PEP263
static CODING_COMMENT_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r"^[ \t\f]*#.*?coding[:=][ \t]*utf-?8").unwrap());
static URL_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^https?://\S+$").unwrap());
Lazy::new(|| Regex::new(r"^[ \t\f]*#.*?coding[:=][ \t]*utf-?8").expect("Invalid regex"));
/// Whether the given line is too long and should be reported.
fn should_enforce_line_length(line: &str, length: usize, limit: usize) -> bool {
if length > limit {
let mut chunks = line.split_whitespace();
if let (Some(first), Some(_)) = (chunks.next(), chunks.next()) {
// Do not enforce the line length for commented lines that end with a URL
// or contain only a single word.
!(first == "#" && chunks.last().map_or(true, |c| URL_REGEX.is_match(c)))
// Do not enforce the line length for commented lines with a single word
!(first == "#" && chunks.next().is_none())
} else {
// Single word / no printable chars - no way to make the line shorter
false
@@ -40,12 +37,11 @@ pub fn check_lines(
contents: &str,
noqa_line_for: &IntMap<usize, usize>,
settings: &Settings,
autofix: bool,
ignore_noqa: bool,
autofix: &fixer::Mode,
) {
let enforce_unnecessary_coding_comment = settings.enabled.contains(&CheckCode::U009);
let enforce_line_too_long = settings.enabled.contains(&CheckCode::E501);
let enforce_noqa = settings.enabled.contains(&CheckCode::M001);
let enforce_unnecessary_coding_comment = settings.enabled[CheckCode::U009 as usize];
let enforce_line_too_long = settings.enabled[CheckCode::E501 as usize];
let enforce_noqa = settings.enabled[CheckCode::M001 as usize];
let mut noqa_directives: IntMap<usize, (Directive, Vec<&str>)> = IntMap::default();
let mut line_checks = vec![];
@@ -57,30 +53,6 @@ pub fn check_lines(
assert!(check.location.row() >= 1);
}
macro_rules! add_if {
($check:expr, $noqa:expr) => {{
match $noqa {
(Directive::All(..), matches) => {
matches.push($check.kind.code().as_ref());
if ignore_noqa {
line_checks.push($check);
}
}
(Directive::Codes(.., codes), matches) => {
if codes.contains(&$check.kind.code().as_ref()) {
matches.push($check.kind.code().as_ref());
if ignore_noqa {
line_checks.push($check);
}
} else {
line_checks.push($check);
}
}
(Directive::None, ..) => line_checks.push($check),
}
}};
}
let lines: Vec<&str> = contents.lines().collect();
for (lineno, line) in lines.iter().enumerate() {
// Grab the noqa (logical) line number for the current (physical) line.
@@ -93,24 +65,21 @@ pub fn check_lines(
if lineno < 2 {
// PEP3120 makes utf-8 the default encoding.
if CODING_COMMENT_REGEX.is_match(line) {
let line_length = line.len();
let mut check = Check::new(
CheckKind::PEP3120UnnecessaryCodingComment,
Range {
location: Location::new(lineno + 1, 0),
end_location: Location::new(lineno + 2, 0),
end_location: Location::new(lineno + 1, line_length + 1),
},
);
if autofix && settings.fixable.contains(check.kind.code()) {
if autofix.patch() && settings.fixable.contains(check.kind.code()) {
check.amend(Fix::deletion(
Location::new(lineno + 1, 0),
Location::new(lineno + 2, 0),
Location::new(lineno + 1, line_length + 1),
));
}
let noqa = noqa_directives.entry(noqa_lineno).or_insert_with(|| {
(noqa::extract_noqa_directive(lines[noqa_lineno]), vec![])
});
add_if!(check, noqa);
line_checks.push(check);
}
}
}
@@ -132,15 +101,15 @@ pub fn check_lines(
match noqa {
(Directive::All(..), matches) => {
matches.push(check.kind.code().as_ref());
ignored.push(index);
ignored.push(index)
}
(Directive::Codes(.., codes), matches) => {
(Directive::Codes(_, _, codes), matches) => {
if codes.contains(&check.kind.code().as_ref()) {
matches.push(check.kind.code().as_ref());
ignored.push(index);
}
}
(Directive::None, ..) => {}
(Directive::None, _) => {}
}
}
@@ -148,6 +117,10 @@ pub fn check_lines(
if enforce_line_too_long {
let line_length = line.chars().count();
if should_enforce_line_length(line, line_length, settings.line_length) {
let noqa = noqa_directives
.entry(noqa_lineno)
.or_insert_with(|| (noqa::extract_noqa_directive(lines[noqa_lineno]), vec![]));
let check = Check::new(
CheckKind::LineTooLong(line_length, settings.line_length),
Range {
@@ -156,19 +129,35 @@ pub fn check_lines(
},
);
let noqa = noqa_directives
.entry(noqa_lineno)
.or_insert_with(|| (noqa::extract_noqa_directive(lines[noqa_lineno]), vec![]));
add_if!(check, noqa);
match noqa {
(Directive::All(..), matches) => {
matches.push(check.kind.code().as_ref());
}
(Directive::Codes(_, _, codes), matches) => {
if codes.contains(&check.kind.code().as_ref()) {
matches.push(check.kind.code().as_ref());
} else {
line_checks.push(check);
}
}
(Directive::None, _) => line_checks.push(check),
}
}
}
}
// Enforce newlines at end of files (W292).
if settings.enabled.contains(&CheckCode::W292) && !contents.ends_with('\n') {
// Enforce newlines at end of files.
if settings.enabled[CheckCode::W292 as usize] && !contents.ends_with('\n') {
// Note: if `lines.last()` is `None`, then `contents` is empty (and so we don't
// want to raise W292 anyway).
if let Some(line) = lines.last() {
let lineno = lines.len() - 1;
let noqa_lineno = noqa_line_for.get(&(lineno + 1)).unwrap_or(&(lineno + 1)) - 1;
let noqa = noqa_directives
.entry(noqa_lineno)
.or_insert_with(|| (noqa::extract_noqa_directive(lines[noqa_lineno]), vec![]));
let check = Check::new(
CheckKind::NoNewLineAtEndOfFile,
Range {
@@ -177,20 +166,27 @@ pub fn check_lines(
},
);
let lineno = lines.len() - 1;
let noqa_lineno = noqa_line_for.get(&(lineno + 1)).unwrap_or(&(lineno + 1)) - 1;
let noqa = noqa_directives
.entry(noqa_lineno)
.or_insert_with(|| (noqa::extract_noqa_directive(lines[noqa_lineno]), vec![]));
add_if!(check, noqa);
match noqa {
(Directive::All(..), matches) => {
matches.push(check.kind.code().as_ref());
}
(Directive::Codes(_, _, codes), matches) => {
if codes.contains(&check.kind.code().as_ref()) {
matches.push(check.kind.code().as_ref());
} else {
line_checks.push(check);
}
}
(Directive::None, _) => line_checks.push(check),
}
}
}
// Enforce that the noqa directive was actually used (M001).
// Enforce that the noqa directive was actually used.
if enforce_noqa {
for (row, (directive, matches)) in noqa_directives {
match directive {
Directive::All(spaces, start, end) => {
Directive::All(start, end) => {
if matches.is_empty() {
let mut check = Check::new(
CheckKind::UnusedNOQA(None),
@@ -199,23 +195,23 @@ pub fn check_lines(
end_location: Location::new(row + 1, end),
},
);
if autofix && settings.fixable.contains(check.kind.code()) {
if autofix.patch() && settings.fixable.contains(check.kind.code()) {
check.amend(Fix::deletion(
Location::new(row + 1, start - spaces),
Location::new(row + 1, start),
Location::new(row + 1, lines[row].chars().count()),
));
}
line_checks.push(check);
}
}
Directive::Codes(spaces, start, end, codes) => {
Directive::Codes(start, end, codes) => {
let mut invalid_codes = vec![];
let mut valid_codes = vec![];
for code in codes {
if matches.contains(&code) {
valid_codes.push(code.to_string());
} else {
if !matches.contains(&code) {
invalid_codes.push(code.to_string());
} else {
valid_codes.push(code.to_string());
}
}
@@ -227,15 +223,15 @@ pub fn check_lines(
end_location: Location::new(row + 1, end),
},
);
if autofix && settings.fixable.contains(check.kind.code()) {
if autofix.patch() && settings.fixable.contains(check.kind.code()) {
if valid_codes.is_empty() {
check.amend(Fix::deletion(
Location::new(row + 1, start - spaces),
Location::new(row + 1, start),
Location::new(row + 1, lines[row].chars().count()),
));
} else {
check.amend(Fix::replacement(
format!("# noqa: {}", valid_codes.join(", ")),
format!(" # noqa: {}", valid_codes.join(", ")),
Location::new(row + 1, start),
Location::new(row + 1, lines[row].chars().count()),
));
@@ -249,11 +245,9 @@ pub fn check_lines(
}
}
if !ignore_noqa {
ignored.sort_unstable();
for index in ignored.iter().rev() {
checks.swap_remove(*index);
}
ignored.sort();
for index in ignored.iter().rev() {
checks.swap_remove(*index);
}
checks.extend(line_checks);
}
@@ -263,13 +257,14 @@ mod tests {
use nohash_hasher::IntMap;
use super::check_lines;
use crate::autofix::fixer;
use crate::checks::{Check, CheckCode};
use crate::settings::Settings;
#[test]
fn e501_non_ascii_char() {
let line = "'\u{4e9c}' * 2"; // 7 in UTF-32, 9 in UTF-8.
let noqa_line_for: IntMap<usize, usize> = IntMap::default();
let noqa_line_for: IntMap<usize, usize> = Default::default();
let check_with_max_line_length = |line_length: usize| {
let mut checks: Vec<Check> = vec![];
check_lines(
@@ -280,8 +275,7 @@ mod tests {
line_length,
..Settings::for_rule(CheckCode::E501)
},
true,
false,
&fixer::Mode::Generate,
);
checks
};

View File

@@ -2,32 +2,31 @@
use rustpython_parser::lexer::{LexResult, Tok};
use crate::autofix::fixer;
use crate::checks::{Check, CheckCode};
use crate::lex::docstring_detection::StateMachine;
use crate::rules::checks::Context;
use crate::source_code_locator::SourceCodeLocator;
use crate::{eradicate, flake8_quotes, pycodestyle, rules, Settings};
use crate::{flake8_quotes, pycodestyle, rules, Settings};
pub fn check_tokens(
checks: &mut Vec<Check>,
locator: &SourceCodeLocator,
tokens: &[LexResult],
settings: &Settings,
autofix: bool,
) -> Vec<Check> {
let mut checks: Vec<Check> = vec![];
autofix: &fixer::Mode,
) {
let enforce_ambiguous_unicode_character = settings.enabled[CheckCode::RUF001 as usize]
|| settings.enabled[CheckCode::RUF002 as usize]
|| settings.enabled[CheckCode::RUF003 as usize];
let enforce_quotes = settings.enabled[CheckCode::Q000 as usize]
|| settings.enabled[CheckCode::Q001 as usize]
|| settings.enabled[CheckCode::Q002 as usize]
|| settings.enabled[CheckCode::Q003 as usize];
let enforce_invalid_escape_sequence = settings.enabled[CheckCode::W605 as usize];
let enforce_ambiguous_unicode_character = settings.enabled.contains(&CheckCode::RUF001)
|| settings.enabled.contains(&CheckCode::RUF002)
|| settings.enabled.contains(&CheckCode::RUF003);
let enforce_quotes = settings.enabled.contains(&CheckCode::Q000)
|| settings.enabled.contains(&CheckCode::Q001)
|| settings.enabled.contains(&CheckCode::Q002)
|| settings.enabled.contains(&CheckCode::Q003);
let enforce_commented_out_code = settings.enabled.contains(&CheckCode::ERA001);
let enforce_invalid_escape_sequence = settings.enabled.contains(&CheckCode::W605);
let mut state_machine = StateMachine::default();
for &(start, ref tok, end) in tokens.iter().flatten() {
let mut state_machine: StateMachine = Default::default();
for (start, tok, end) in tokens.iter().flatten() {
let is_docstring = if enforce_ambiguous_unicode_character || enforce_quotes {
state_machine.consume(tok)
} else {
@@ -66,24 +65,13 @@ pub fn check_tokens(
is_docstring,
&settings.flake8_quotes,
) {
if settings.enabled.contains(check.kind.code()) {
if settings.enabled[check.kind.code().clone() as usize] {
checks.push(check);
}
}
}
}
// eradicate
if enforce_commented_out_code {
if matches!(tok, Tok::Comment) {
if let Some(check) =
eradicate::checks::commented_out_code(locator, start, end, settings, autofix)
{
checks.push(check);
}
}
}
// W605
if enforce_invalid_escape_sequence {
if matches!(tok, Tok::String { .. }) {
@@ -93,6 +81,4 @@ pub fn check_tokens(
}
}
}
checks
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
//! File automatically generated by `examples/generate_check_code_prefix.rs`.
//! File automatically generated by examples/generate_check_code_prefix.rs.
use serde::{Deserialize, Serialize};
use strum_macros::EnumString;
@@ -59,14 +59,10 @@ pub enum CheckCodePrefix {
B020,
B021,
B022,
B023,
B024,
B025,
B026,
B027,
B9,
B90,
B904,
BLE,
BLE0,
BLE00,
@@ -176,10 +172,6 @@ pub enum CheckCodePrefix {
E902,
E99,
E999,
ERA,
ERA8,
ERA80,
ERA001,
F,
F4,
F40,
@@ -191,22 +183,6 @@ pub enum CheckCodePrefix {
F406,
F407,
F5,
F50,
F501,
F502,
F503,
F504,
F505,
F506,
F507,
F508,
F509,
F52,
F521,
F522,
F523,
F524,
F525,
F54,
F541,
F6,
@@ -291,9 +267,6 @@ pub enum CheckCodePrefix {
RUF001,
RUF002,
RUF003,
RUF1,
RUF10,
RUF101,
S,
S1,
S10,
@@ -304,9 +277,6 @@ pub enum CheckCodePrefix {
S106,
S107,
T,
T1,
T10,
T100,
T2,
T20,
T201,
@@ -327,8 +297,6 @@ pub enum CheckCodePrefix {
U011,
U012,
U013,
U014,
U015,
W,
W2,
W29,
@@ -365,7 +333,6 @@ pub enum PrefixSpecificity {
impl CheckCodePrefix {
pub fn codes(&self) -> Vec<CheckCode> {
#[allow(clippy::match_same_arms)]
match self {
CheckCodePrefix::A => vec![CheckCode::A001, CheckCode::A002, CheckCode::A003],
CheckCodePrefix::A0 => vec![CheckCode::A001, CheckCode::A002, CheckCode::A003],
@@ -439,12 +406,10 @@ impl CheckCodePrefix {
CheckCode::B020,
CheckCode::B021,
CheckCode::B022,
CheckCode::B023,
CheckCode::B024,
CheckCode::B025,
CheckCode::B026,
CheckCode::B027,
CheckCode::B904,
],
CheckCodePrefix::B0 => vec![
CheckCode::B002,
@@ -468,7 +433,6 @@ impl CheckCodePrefix {
CheckCode::B020,
CheckCode::B021,
CheckCode::B022,
CheckCode::B023,
CheckCode::B024,
CheckCode::B025,
CheckCode::B026,
@@ -518,7 +482,6 @@ impl CheckCodePrefix {
CheckCode::B020,
CheckCode::B021,
CheckCode::B022,
CheckCode::B023,
CheckCode::B024,
CheckCode::B025,
CheckCode::B026,
@@ -527,14 +490,10 @@ impl CheckCodePrefix {
CheckCodePrefix::B020 => vec![CheckCode::B020],
CheckCodePrefix::B021 => vec![CheckCode::B021],
CheckCodePrefix::B022 => vec![CheckCode::B022],
CheckCodePrefix::B023 => vec![CheckCode::B023],
CheckCodePrefix::B024 => vec![CheckCode::B024],
CheckCodePrefix::B025 => vec![CheckCode::B025],
CheckCodePrefix::B026 => vec![CheckCode::B026],
CheckCodePrefix::B027 => vec![CheckCode::B027],
CheckCodePrefix::B9 => vec![CheckCode::B904],
CheckCodePrefix::B90 => vec![CheckCode::B904],
CheckCodePrefix::B904 => vec![CheckCode::B904],
CheckCodePrefix::BLE => vec![CheckCode::BLE001],
CheckCodePrefix::BLE0 => vec![CheckCode::BLE001],
CheckCodePrefix::BLE00 => vec![CheckCode::BLE001],
@@ -867,10 +826,6 @@ impl CheckCodePrefix {
CheckCodePrefix::E902 => vec![CheckCode::E902],
CheckCodePrefix::E99 => vec![CheckCode::E999],
CheckCodePrefix::E999 => vec![CheckCode::E999],
CheckCodePrefix::ERA => vec![CheckCode::ERA001],
CheckCodePrefix::ERA8 => vec![CheckCode::ERA001],
CheckCodePrefix::ERA80 => vec![CheckCode::ERA001],
CheckCodePrefix::ERA001 => vec![CheckCode::ERA001],
CheckCodePrefix::F => vec![
CheckCode::F401,
CheckCode::F402,
@@ -879,20 +834,6 @@ impl CheckCodePrefix {
CheckCode::F405,
CheckCode::F406,
CheckCode::F407,
CheckCode::F501,
CheckCode::F502,
CheckCode::F503,
CheckCode::F504,
CheckCode::F505,
CheckCode::F506,
CheckCode::F507,
CheckCode::F508,
CheckCode::F509,
CheckCode::F521,
CheckCode::F522,
CheckCode::F523,
CheckCode::F524,
CheckCode::F525,
CheckCode::F541,
CheckCode::F601,
CheckCode::F602,
@@ -940,55 +881,7 @@ impl CheckCodePrefix {
CheckCodePrefix::F405 => vec![CheckCode::F405],
CheckCodePrefix::F406 => vec![CheckCode::F406],
CheckCodePrefix::F407 => vec![CheckCode::F407],
CheckCodePrefix::F5 => vec![
CheckCode::F501,
CheckCode::F502,
CheckCode::F503,
CheckCode::F504,
CheckCode::F505,
CheckCode::F506,
CheckCode::F507,
CheckCode::F508,
CheckCode::F509,
CheckCode::F521,
CheckCode::F522,
CheckCode::F523,
CheckCode::F524,
CheckCode::F525,
CheckCode::F541,
],
CheckCodePrefix::F50 => vec![
CheckCode::F501,
CheckCode::F502,
CheckCode::F503,
CheckCode::F504,
CheckCode::F505,
CheckCode::F506,
CheckCode::F507,
CheckCode::F508,
CheckCode::F509,
],
CheckCodePrefix::F501 => vec![CheckCode::F501],
CheckCodePrefix::F502 => vec![CheckCode::F502],
CheckCodePrefix::F503 => vec![CheckCode::F503],
CheckCodePrefix::F504 => vec![CheckCode::F504],
CheckCodePrefix::F505 => vec![CheckCode::F505],
CheckCodePrefix::F506 => vec![CheckCode::F506],
CheckCodePrefix::F507 => vec![CheckCode::F507],
CheckCodePrefix::F508 => vec![CheckCode::F508],
CheckCodePrefix::F509 => vec![CheckCode::F509],
CheckCodePrefix::F52 => vec![
CheckCode::F521,
CheckCode::F522,
CheckCode::F523,
CheckCode::F524,
CheckCode::F525,
],
CheckCodePrefix::F521 => vec![CheckCode::F521],
CheckCodePrefix::F522 => vec![CheckCode::F522],
CheckCodePrefix::F523 => vec![CheckCode::F523],
CheckCodePrefix::F524 => vec![CheckCode::F524],
CheckCodePrefix::F525 => vec![CheckCode::F525],
CheckCodePrefix::F5 => vec![CheckCode::F541],
CheckCodePrefix::F54 => vec![CheckCode::F541],
CheckCodePrefix::F541 => vec![CheckCode::F541],
CheckCodePrefix::F6 => vec![
@@ -1164,20 +1057,12 @@ impl CheckCodePrefix {
CheckCodePrefix::Q001 => vec![CheckCode::Q001],
CheckCodePrefix::Q002 => vec![CheckCode::Q002],
CheckCodePrefix::Q003 => vec![CheckCode::Q003],
CheckCodePrefix::RUF => vec![
CheckCode::RUF001,
CheckCode::RUF002,
CheckCode::RUF003,
CheckCode::RUF101,
],
CheckCodePrefix::RUF => vec![CheckCode::RUF001, CheckCode::RUF002, CheckCode::RUF003],
CheckCodePrefix::RUF0 => vec![CheckCode::RUF001, CheckCode::RUF002, CheckCode::RUF003],
CheckCodePrefix::RUF00 => vec![CheckCode::RUF001, CheckCode::RUF002, CheckCode::RUF003],
CheckCodePrefix::RUF001 => vec![CheckCode::RUF001],
CheckCodePrefix::RUF002 => vec![CheckCode::RUF002],
CheckCodePrefix::RUF003 => vec![CheckCode::RUF003],
CheckCodePrefix::RUF1 => vec![CheckCode::RUF101],
CheckCodePrefix::RUF10 => vec![CheckCode::RUF101],
CheckCodePrefix::RUF101 => vec![CheckCode::RUF101],
CheckCodePrefix::S => vec![
CheckCode::S101,
CheckCode::S102,
@@ -1208,10 +1093,7 @@ impl CheckCodePrefix {
CheckCodePrefix::S105 => vec![CheckCode::S105],
CheckCodePrefix::S106 => vec![CheckCode::S106],
CheckCodePrefix::S107 => vec![CheckCode::S107],
CheckCodePrefix::T => vec![CheckCode::T100, CheckCode::T201, CheckCode::T203],
CheckCodePrefix::T1 => vec![CheckCode::T100],
CheckCodePrefix::T10 => vec![CheckCode::T100],
CheckCodePrefix::T100 => vec![CheckCode::T100],
CheckCodePrefix::T => vec![CheckCode::T201, CheckCode::T203],
CheckCodePrefix::T2 => vec![CheckCode::T201, CheckCode::T203],
CheckCodePrefix::T20 => vec![CheckCode::T201, CheckCode::T203],
CheckCodePrefix::T201 => vec![CheckCode::T201],
@@ -1229,8 +1111,6 @@ impl CheckCodePrefix {
CheckCode::U011,
CheckCode::U012,
CheckCode::U013,
CheckCode::U014,
CheckCode::U015,
],
CheckCodePrefix::U0 => vec![
CheckCode::U001,
@@ -1245,8 +1125,6 @@ impl CheckCodePrefix {
CheckCode::U011,
CheckCode::U012,
CheckCode::U013,
CheckCode::U014,
CheckCode::U015,
],
CheckCodePrefix::U00 => vec![
CheckCode::U001,
@@ -1271,15 +1149,11 @@ impl CheckCodePrefix {
CheckCode::U011,
CheckCode::U012,
CheckCode::U013,
CheckCode::U014,
CheckCode::U015,
],
CheckCodePrefix::U010 => vec![CheckCode::U010],
CheckCodePrefix::U011 => vec![CheckCode::U011],
CheckCodePrefix::U012 => vec![CheckCode::U012],
CheckCodePrefix::U013 => vec![CheckCode::U013],
CheckCodePrefix::U014 => vec![CheckCode::U014],
CheckCodePrefix::U015 => vec![CheckCode::U015],
CheckCodePrefix::W => vec![CheckCode::W292, CheckCode::W605],
CheckCodePrefix::W2 => vec![CheckCode::W292],
CheckCodePrefix::W29 => vec![CheckCode::W292],
@@ -1331,7 +1205,6 @@ impl CheckCodePrefix {
impl CheckCodePrefix {
pub fn specificity(&self) -> PrefixSpecificity {
#[allow(clippy::match_same_arms)]
match self {
CheckCodePrefix::A => PrefixSpecificity::Category,
CheckCodePrefix::A0 => PrefixSpecificity::Hundreds,
@@ -1385,14 +1258,10 @@ impl CheckCodePrefix {
CheckCodePrefix::B020 => PrefixSpecificity::Explicit,
CheckCodePrefix::B021 => PrefixSpecificity::Explicit,
CheckCodePrefix::B022 => PrefixSpecificity::Explicit,
CheckCodePrefix::B023 => PrefixSpecificity::Explicit,
CheckCodePrefix::B024 => PrefixSpecificity::Explicit,
CheckCodePrefix::B025 => PrefixSpecificity::Explicit,
CheckCodePrefix::B026 => PrefixSpecificity::Explicit,
CheckCodePrefix::B027 => PrefixSpecificity::Explicit,
CheckCodePrefix::B9 => PrefixSpecificity::Hundreds,
CheckCodePrefix::B90 => PrefixSpecificity::Tens,
CheckCodePrefix::B904 => PrefixSpecificity::Explicit,
CheckCodePrefix::BLE => PrefixSpecificity::Category,
CheckCodePrefix::BLE0 => PrefixSpecificity::Hundreds,
CheckCodePrefix::BLE00 => PrefixSpecificity::Tens,
@@ -1502,10 +1371,6 @@ impl CheckCodePrefix {
CheckCodePrefix::E902 => PrefixSpecificity::Explicit,
CheckCodePrefix::E99 => PrefixSpecificity::Tens,
CheckCodePrefix::E999 => PrefixSpecificity::Explicit,
CheckCodePrefix::ERA => PrefixSpecificity::Category,
CheckCodePrefix::ERA8 => PrefixSpecificity::Hundreds,
CheckCodePrefix::ERA80 => PrefixSpecificity::Tens,
CheckCodePrefix::ERA001 => PrefixSpecificity::Explicit,
CheckCodePrefix::F => PrefixSpecificity::Category,
CheckCodePrefix::F4 => PrefixSpecificity::Hundreds,
CheckCodePrefix::F40 => PrefixSpecificity::Tens,
@@ -1517,22 +1382,6 @@ impl CheckCodePrefix {
CheckCodePrefix::F406 => PrefixSpecificity::Explicit,
CheckCodePrefix::F407 => PrefixSpecificity::Explicit,
CheckCodePrefix::F5 => PrefixSpecificity::Hundreds,
CheckCodePrefix::F50 => PrefixSpecificity::Tens,
CheckCodePrefix::F501 => PrefixSpecificity::Explicit,
CheckCodePrefix::F502 => PrefixSpecificity::Explicit,
CheckCodePrefix::F503 => PrefixSpecificity::Explicit,
CheckCodePrefix::F504 => PrefixSpecificity::Explicit,
CheckCodePrefix::F505 => PrefixSpecificity::Explicit,
CheckCodePrefix::F506 => PrefixSpecificity::Explicit,
CheckCodePrefix::F507 => PrefixSpecificity::Explicit,
CheckCodePrefix::F508 => PrefixSpecificity::Explicit,
CheckCodePrefix::F509 => PrefixSpecificity::Explicit,
CheckCodePrefix::F52 => PrefixSpecificity::Tens,
CheckCodePrefix::F521 => PrefixSpecificity::Explicit,
CheckCodePrefix::F522 => PrefixSpecificity::Explicit,
CheckCodePrefix::F523 => PrefixSpecificity::Explicit,
CheckCodePrefix::F524 => PrefixSpecificity::Explicit,
CheckCodePrefix::F525 => PrefixSpecificity::Explicit,
CheckCodePrefix::F54 => PrefixSpecificity::Tens,
CheckCodePrefix::F541 => PrefixSpecificity::Explicit,
CheckCodePrefix::F6 => PrefixSpecificity::Hundreds,
@@ -1617,9 +1466,6 @@ impl CheckCodePrefix {
CheckCodePrefix::RUF001 => PrefixSpecificity::Explicit,
CheckCodePrefix::RUF002 => PrefixSpecificity::Explicit,
CheckCodePrefix::RUF003 => PrefixSpecificity::Explicit,
CheckCodePrefix::RUF1 => PrefixSpecificity::Hundreds,
CheckCodePrefix::RUF10 => PrefixSpecificity::Tens,
CheckCodePrefix::RUF101 => PrefixSpecificity::Explicit,
CheckCodePrefix::S => PrefixSpecificity::Category,
CheckCodePrefix::S1 => PrefixSpecificity::Hundreds,
CheckCodePrefix::S10 => PrefixSpecificity::Tens,
@@ -1630,9 +1476,6 @@ impl CheckCodePrefix {
CheckCodePrefix::S106 => PrefixSpecificity::Explicit,
CheckCodePrefix::S107 => PrefixSpecificity::Explicit,
CheckCodePrefix::T => PrefixSpecificity::Category,
CheckCodePrefix::T1 => PrefixSpecificity::Hundreds,
CheckCodePrefix::T10 => PrefixSpecificity::Tens,
CheckCodePrefix::T100 => PrefixSpecificity::Explicit,
CheckCodePrefix::T2 => PrefixSpecificity::Hundreds,
CheckCodePrefix::T20 => PrefixSpecificity::Tens,
CheckCodePrefix::T201 => PrefixSpecificity::Explicit,
@@ -1653,8 +1496,6 @@ impl CheckCodePrefix {
CheckCodePrefix::U011 => PrefixSpecificity::Explicit,
CheckCodePrefix::U012 => PrefixSpecificity::Explicit,
CheckCodePrefix::U013 => PrefixSpecificity::Explicit,
CheckCodePrefix::U014 => PrefixSpecificity::Explicit,
CheckCodePrefix::U015 => PrefixSpecificity::Explicit,
CheckCodePrefix::W => PrefixSpecificity::Category,
CheckCodePrefix::W2 => PrefixSpecificity::Hundreds,
CheckCodePrefix::W29 => PrefixSpecificity::Tens,
@@ -1682,26 +1523,3 @@ impl CheckCodePrefix {
}
}
}
pub const CATEGORIES: &[CheckCodePrefix] = &[
CheckCodePrefix::A,
CheckCodePrefix::ANN,
CheckCodePrefix::B,
CheckCodePrefix::BLE,
CheckCodePrefix::C,
CheckCodePrefix::D,
CheckCodePrefix::E,
CheckCodePrefix::ERA,
CheckCodePrefix::F,
CheckCodePrefix::FBT,
CheckCodePrefix::I,
CheckCodePrefix::M,
CheckCodePrefix::N,
CheckCodePrefix::Q,
CheckCodePrefix::RUF,
CheckCodePrefix::S,
CheckCodePrefix::T,
CheckCodePrefix::U,
CheckCodePrefix::W,
CheckCodePrefix::YTT,
];

View File

@@ -1,21 +1,19 @@
use std::path::PathBuf;
use clap::{command, Parser};
use fnv::FnvHashMap;
use regex::Regex;
use rustc_hash::FxHashMap;
use crate::checks::CheckCode;
use crate::checks_gen::CheckCodePrefix;
use crate::logging::LogLevel;
use crate::printer::SerializationFormat;
use crate::settings::types::{FilePattern, PatternPrefixPair, PerFileIgnore, PythonVersion};
use crate::settings::types::{PatternPrefixPair, PerFileIgnore, PythonVersion};
#[derive(Debug, Parser)]
#[command(author, about = "Ruff: An extremely fast Python linter.")]
#[command(version)]
#[allow(clippy::struct_excessive_bools)]
pub struct Cli {
#[arg(required_unless_present_any = ["explain", "generate_shell_completion"])]
#[arg(required = true)]
pub files: Vec<PathBuf>,
/// Path to the `pyproject.toml` file to use for configuration.
#[arg(long)]
@@ -60,11 +58,11 @@ pub struct Cli {
pub extend_ignore: Vec<CheckCodePrefix>,
/// List of paths, used to exclude files and/or directories from checks.
#[arg(long, value_delimiter = ',')]
pub exclude: Vec<FilePattern>,
pub exclude: Vec<String>,
/// Like --exclude, but adds additional files and directories on top of the
/// excluded ones.
#[arg(long, value_delimiter = ',')]
pub extend_exclude: Vec<FilePattern>,
pub extend_exclude: Vec<String>,
/// List of error codes to treat as eligible for autofix. Only applicable
/// when autofix itself is enabled (e.g., via `--fix`).
#[arg(long, value_delimiter = ',')]
@@ -77,7 +75,7 @@ pub struct Cli {
#[arg(long, value_delimiter = ',')]
pub per_file_ignores: Vec<PatternPrefixPair>,
/// Output serialization format for error messages.
#[arg(long, value_enum, default_value_t = SerializationFormat::Text)]
#[arg(long, value_enum, default_value_t=SerializationFormat::Text)]
pub format: SerializationFormat,
/// Show violations with source code.
#[arg(long)]
@@ -111,12 +109,6 @@ pub struct Cli {
/// The name of the file when passing it through stdin.
#[arg(long)]
pub stdin_filename: Option<String>,
/// Explain a rule.
#[arg(long)]
pub explain: Option<CheckCode>,
/// Generate shell completion
#[arg(long, hide = true, value_name = "SHELL")]
pub generate_shell_completion: Option<clap_complete_command::Shell>,
}
impl Cli {
@@ -151,8 +143,11 @@ pub fn extract_log_level(cli: &Cli) -> LogLevel {
}
/// Convert a list of `PatternPrefixPair` structs to `PerFileIgnore`.
pub fn collect_per_file_ignores(pairs: Vec<PatternPrefixPair>) -> Vec<PerFileIgnore> {
let mut per_file_ignores: FxHashMap<String, Vec<CheckCodePrefix>> = FxHashMap::default();
pub fn collect_per_file_ignores(
pairs: Vec<PatternPrefixPair>,
project_root: Option<&PathBuf>,
) -> Vec<PerFileIgnore> {
let mut per_file_ignores: FnvHashMap<String, Vec<CheckCodePrefix>> = FnvHashMap::default();
for pair in pairs {
per_file_ignores
.entry(pair.pattern)
@@ -160,7 +155,7 @@ pub fn collect_per_file_ignores(pairs: Vec<PatternPrefixPair>) -> Vec<PerFileIgn
.push(pair.prefix);
}
per_file_ignores
.into_iter()
.map(|(pattern, prefixes)| PerFileIgnore::new(pattern, &prefixes))
.iter()
.map(|(pattern, prefixes)| PerFileIgnore::new(pattern, prefixes, project_root))
.collect()
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,66 +0,0 @@
use std::path::PathBuf;
use anyhow::Result;
use serde::Serialize;
use walkdir::DirEntry;
use crate::checks::CheckCode;
use crate::fs::iter_python_files;
use crate::printer::SerializationFormat;
use crate::{Configuration, Settings};
/// Print the user-facing configuration settings.
pub fn show_settings(
configuration: &Configuration,
project_root: Option<&PathBuf>,
pyproject: Option<&PathBuf>,
) {
println!("Resolved configuration: {configuration:#?}");
println!("Found project root at: {project_root:?}");
println!("Found pyproject.toml at: {pyproject:?}");
}
/// Show the list of files to be checked based on current settings.
pub fn show_files(files: &[PathBuf], settings: &Settings) {
let mut entries: Vec<DirEntry> = files
.iter()
.flat_map(|path| iter_python_files(path, &settings.exclude, &settings.extend_exclude))
.flatten()
.collect();
entries.sort_by(|a, b| a.path().cmp(b.path()));
for entry in entries {
println!("{}", entry.path().to_string_lossy());
}
}
#[derive(Serialize)]
struct Explanation<'a> {
code: &'a str,
category: &'a str,
summary: &'a str,
}
/// Explain a `CheckCode` to the user.
pub fn explain(code: &CheckCode, format: SerializationFormat) -> Result<()> {
match format {
SerializationFormat::Text => {
println!(
"{} ({}): {}",
code.as_ref(),
code.category().title(),
code.kind().summary()
);
}
SerializationFormat::Json => {
println!(
"{}",
serde_json::to_string_pretty(&Explanation {
code: code.as_ref(),
category: code.category().title(),
summary: &code.kind().summary(),
})?
);
}
};
Ok(())
}

View File

@@ -11,22 +11,23 @@ use crate::{Settings, SourceCodeLocator};
bitflags! {
pub struct Flags: u32 {
const NOQA = 0b0000_0001;
const ISORT = 0b0000_0010;
const NOQA = 0b00000001;
const ISORT = 0b00000010;
}
}
impl Flags {
pub fn from_settings(settings: &Settings) -> Self {
if settings
.enabled
.iter()
.any(|check_code| matches!(check_code.lint_source(), LintSource::Imports))
{
Flags::NOQA | Flags::ISORT
} else {
Flags::NOQA
}
Flags::NOQA
// if settings
// .enabled
// .iter()
// .any(|check_code| matches!(check_code.lint_source(), LintSource::Imports))
// {
// Flags::NOQA | Flags::ISORT
// } else {
// Flags::NOQA
// }
}
}
@@ -38,18 +39,18 @@ pub struct Directives {
pub fn extract_directives(
lxr: &[LexResult],
locator: &SourceCodeLocator,
flags: Flags,
flags: &Flags,
) -> Directives {
Directives {
noqa_line_for: if flags.contains(Flags::NOQA) {
extract_noqa_line_for(lxr)
} else {
IntMap::default()
Default::default()
},
isort_exclusions: if flags.contains(Flags::ISORT) {
extract_isort_exclusions(lxr, locator)
} else {
IntSet::default()
Default::default()
},
}
}
@@ -75,13 +76,13 @@ pub fn extract_noqa_line_for(lxr: &[LexResult]) -> IntMap<usize, usize> {
/// Extract a set of lines over which to disable isort.
pub fn extract_isort_exclusions(lxr: &[LexResult], locator: &SourceCodeLocator) -> IntSet<usize> {
let mut exclusions: IntSet<usize> = IntSet::default();
let mut off: Option<Location> = None;
for &(start, ref tok, end) in lxr.iter().flatten() {
let mut off: Option<&Location> = None;
for (start, tok, end) in lxr.iter().flatten() {
// TODO(charlie): Modify RustPython to include the comment text in the token.
if matches!(tok, Tok::Comment) {
let comment_text = locator.slice_source_code_range(&Range {
location: start,
end_location: end,
location: *start,
end_location: *end,
});
if off.is_some() {
if comment_text == "# isort: on" {
@@ -113,7 +114,7 @@ pub fn extract_isort_exclusions(lxr: &[LexResult], locator: &SourceCodeLocator)
#[cfg(test)]
mod tests {
use anyhow::Result;
use nohash_hasher::IntMap;
use rustpython_parser::lexer;
use rustpython_parser::lexer::LexResult;
@@ -121,8 +122,8 @@ mod tests {
use crate::directives::extract_noqa_line_for;
#[test]
fn extraction() {
let empty: IntMap<usize, usize> = IntMap::default();
fn extraction() -> Result<()> {
let empty: IntMap<usize, usize> = Default::default();
let lxr: Vec<LexResult> = lexer::make_tokenizer(
"x = 1
@@ -200,5 +201,7 @@ z = x + 1",
extract_noqa_line_for(&lxr),
IntMap::from_iter([(2, 5), (3, 5), (4, 5)])
);
Ok(())
}
}

View File

@@ -1,5 +0,0 @@
pub const TRIPLE_QUOTE_PREFIXES: &[&str] = &[
"ur\"\"\"", "ur'''", "u\"\"\"", "u'''", "r\"\"\"", "r'''", "\"\"\"", "'''",
];
pub const SINGLE_QUOTE_PREFIXES: &[&str] = &["ur\"", "ur'", "u\"", "u'", "r\"", "r'", "\"", "'"];

View File

@@ -1,10 +1,10 @@
//! Abstractions for Google-style docstrings.
use fnv::FnvHashSet;
use once_cell::sync::Lazy;
use rustc_hash::FxHashSet;
pub(crate) static GOOGLE_SECTION_NAMES: Lazy<FxHashSet<&'static str>> = Lazy::new(|| {
FxHashSet::from_iter([
pub(crate) static GOOGLE_SECTION_NAMES: Lazy<FnvHashSet<&'static str>> = Lazy::new(|| {
FnvHashSet::from_iter([
"Args",
"Arguments",
"Attention",
@@ -36,35 +36,36 @@ pub(crate) static GOOGLE_SECTION_NAMES: Lazy<FxHashSet<&'static str>> = Lazy::ne
])
});
pub(crate) static LOWERCASE_GOOGLE_SECTION_NAMES: Lazy<FxHashSet<&'static str>> = Lazy::new(|| {
FxHashSet::from_iter([
"args",
"arguments",
"attention",
"attributes",
"caution",
"danger",
"error",
"example",
"examples",
"hint",
"important",
"keyword args",
"keyword arguments",
"methods",
"note",
"notes",
"return",
"returns",
"raises",
"references",
"see also",
"tip",
"todo",
"warning",
"warnings",
"warns",
"yield",
"yields",
])
});
pub(crate) static LOWERCASE_GOOGLE_SECTION_NAMES: Lazy<FnvHashSet<&'static str>> =
Lazy::new(|| {
FnvHashSet::from_iter([
"args",
"arguments",
"attention",
"attributes",
"caution",
"danger",
"error",
"example",
"examples",
"hint",
"important",
"keyword args",
"keyword arguments",
"methods",
"note",
"notes",
"return",
"returns",
"raises",
"references",
"see also",
"tip",
"todo",
"warning",
"warnings",
"warns",
"yield",
"yields",
])
});

View File

@@ -3,6 +3,12 @@ use rustpython_ast::{Located, Location};
use crate::ast::types::Range;
use crate::check_ast::Checker;
pub const TRIPLE_QUOTE_PREFIXES: &[&str] = &[
"ur\"\"\"", "ur'''", "u\"\"\"", "u'''", "r\"\"\"", "r'''", "\"\"\"", "'''",
];
pub const SINGLE_QUOTE_PREFIXES: &[&str] = &["ur\"", "ur'", "u\"", "u'", "r\"", "r'", "\"", "'"];
/// Extract the leading words from a line of text.
pub fn leading_words(line: &str) -> String {
line.trim()
@@ -19,7 +25,7 @@ pub fn leading_space(line: &str) -> String {
}
/// Extract the leading indentation from a line.
pub fn indentation<T>(checker: &Checker, located: &Located<T>) -> String {
pub fn indentation<'a, T>(checker: &'a Checker, located: &Located<T>) -> String {
let range = Range::from_located(located);
checker
.locator

View File

@@ -1,7 +1,7 @@
pub mod constants;
pub mod definition;
pub mod extraction;
pub mod google;
pub mod helpers;
pub mod numpy;
pub mod sections;
pub mod styles;

View File

@@ -1,10 +1,10 @@
//! Abstractions for NumPy-style docstrings.
use fnv::FnvHashSet;
use once_cell::sync::Lazy;
use rustc_hash::FxHashSet;
pub(crate) static LOWERCASE_NUMPY_SECTION_NAMES: Lazy<FxHashSet<&'static str>> = Lazy::new(|| {
FxHashSet::from_iter([
pub(crate) static LOWERCASE_NUMPY_SECTION_NAMES: Lazy<FnvHashSet<&'static str>> = Lazy::new(|| {
FnvHashSet::from_iter([
"short summary",
"extended summary",
"parameters",
@@ -21,8 +21,8 @@ pub(crate) static LOWERCASE_NUMPY_SECTION_NAMES: Lazy<FxHashSet<&'static str>> =
])
});
pub(crate) static NUMPY_SECTION_NAMES: Lazy<FxHashSet<&'static str>> = Lazy::new(|| {
FxHashSet::from_iter([
pub(crate) static NUMPY_SECTION_NAMES: Lazy<FnvHashSet<&'static str>> = Lazy::new(|| {
FnvHashSet::from_iter([
"Short Summary",
"Extended Summary",
"Parameters",

View File

@@ -1,4 +1,4 @@
use crate::ast::whitespace;
use crate::docstrings::helpers;
use crate::docstrings::styles::SectionStyle;
#[derive(Debug)]
@@ -14,7 +14,7 @@ pub(crate) struct SectionContext<'a> {
fn suspected_as_section(line: &str, style: &SectionStyle) -> bool {
style
.lowercase_section_names()
.contains(&whitespace::leading_words(line).to_lowercase().as_str())
.contains(&helpers::leading_words(line).to_lowercase().as_str())
}
/// Check if the suspected context is really a section header.
@@ -64,7 +64,7 @@ pub(crate) fn section_contexts<'a>(
let mut contexts = vec![];
for lineno in suspected_section_indices {
let context = SectionContext {
section_name: whitespace::leading_words(lines[lineno]),
section_name: helpers::leading_words(lines[lineno]),
previous_line: lines[lineno - 1],
line: lines[lineno],
following_lines: &lines[lineno + 1..],

View File

@@ -1,5 +1,5 @@
use fnv::FnvHashSet;
use once_cell::sync::Lazy;
use rustc_hash::FxHashSet;
use crate::docstrings::google::{GOOGLE_SECTION_NAMES, LOWERCASE_GOOGLE_SECTION_NAMES};
use crate::docstrings::numpy::{LOWERCASE_NUMPY_SECTION_NAMES, NUMPY_SECTION_NAMES};
@@ -10,14 +10,14 @@ pub(crate) enum SectionStyle {
}
impl SectionStyle {
pub(crate) fn section_names(&self) -> &Lazy<FxHashSet<&'static str>> {
pub(crate) fn section_names(&self) -> &Lazy<FnvHashSet<&'static str>> {
match self {
SectionStyle::NumPy => &NUMPY_SECTION_NAMES,
SectionStyle::Google => &GOOGLE_SECTION_NAMES,
}
}
pub(crate) fn lowercase_section_names(&self) -> &Lazy<FxHashSet<&'static str>> {
pub(crate) fn lowercase_section_names(&self) -> &Lazy<FnvHashSet<&'static str>> {
match self {
SectionStyle::NumPy => &LOWERCASE_NUMPY_SECTION_NAMES,
SectionStyle::Google => &LOWERCASE_GOOGLE_SECTION_NAMES,

View File

@@ -1,51 +0,0 @@
use rustpython_ast::Location;
use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checks::{CheckCode, CheckKind};
use crate::eradicate::detection::comment_contains_code;
use crate::{Check, Settings, SourceCodeLocator};
fn is_standalone_comment(line: &str) -> bool {
for char in line.chars() {
if char == '#' {
return true;
} else if !char.is_whitespace() {
return false;
}
}
unreachable!("Comment should contain '#' character")
}
/// ERA001
pub fn commented_out_code(
locator: &SourceCodeLocator,
start: Location,
end: Location,
settings: &Settings,
autofix: bool,
) -> Option<Check> {
let location = Location::new(start.row(), 0);
let end_location = Location::new(end.row() + 1, 0);
let line = locator.slice_source_code_range(&Range {
location,
end_location,
});
// Verify that the comment is on its own line, and that it contains code.
if is_standalone_comment(&line) && comment_contains_code(&line) {
let mut check = Check::new(
CheckKind::CommentedOutCode,
Range {
location: start,
end_location: end,
},
);
if autofix && settings.fixable.contains(&CheckCode::ERA001) {
check.amend(Fix::deletion(location, end_location));
}
Some(check)
} else {
None
}
}

View File

@@ -1,223 +0,0 @@
/// See: [eradicate.py](https://github.com/myint/eradicate/blob/98f199940979c94447a461d50d27862b118b282d/eradicate.py)
use once_cell::sync::Lazy;
use regex::Regex;
static ALLOWLIST_REGEX: Lazy<Regex> = Lazy::new(|| {
Regex::new(
r"(?i)pylint|pyright|noqa|nosec|type:\s*ignore|fmt:\s*(on|off)|isort:\s*(on|off|skip|skip_file|split|dont-add-imports(:\s*\[.*?])?)|TODO|FIXME|XXX"
).unwrap()
});
static BRACKET_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[()\[\]{}\s]+$").unwrap());
static CODE_INDICATORS: &[&str] = &[
"(", ")", "[", "]", "{", "}", ":", "=", "%", "print", "return", "break", "continue", "import",
];
static CODE_KEYWORDS: Lazy<Vec<Regex>> = Lazy::new(|| {
vec![
Regex::new(r"^\s*elif\s+.*\s*:\s*$").unwrap(),
Regex::new(r"^\s*else\s*:\s*$").unwrap(),
Regex::new(r"^\s*try\s*:\s*$").unwrap(),
Regex::new(r"^\s*finally\s*:\s*$").unwrap(),
Regex::new(r"^\s*except\s+.*\s*:\s*$").unwrap(),
]
});
static CODING_COMMENT_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r"^.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)").unwrap());
static HASH_NUMBER: Lazy<Regex> = Lazy::new(|| Regex::new(r"#\d").unwrap());
static MULTILINE_ASSIGNMENT_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r"^\s*\w+\s*=.*[(\[{]$").unwrap());
static PARTIAL_DICTIONARY_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r#"^\s*['"]\w+['"]\s*:.+[,{]\s*$"#).unwrap());
static PRINT_RETURN_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^(print|return)\b\s*").unwrap());
/// Returns `true` if a comment contains Python code.
pub fn comment_contains_code(line: &str) -> bool {
let line = if let Some(line) = line.trim().strip_prefix('#') {
line.trim()
} else {
return false;
};
// Ignore non-comment related hashes (e.g., "# Issue #999").
if HASH_NUMBER.is_match(line) {
return false;
}
// Ignore whitelisted comments.
if ALLOWLIST_REGEX.is_match(line) {
return false;
}
if CODING_COMMENT_REGEX.is_match(line) {
return false;
}
// Check that this is possibly code.
if CODE_INDICATORS.iter().all(|symbol| !line.contains(symbol)) {
return false;
}
if multiline_case(line) {
return true;
}
if CODE_KEYWORDS.iter().any(|symbol| symbol.is_match(line)) {
return true;
}
let line = PRINT_RETURN_REGEX.replace_all(line, "");
if PARTIAL_DICTIONARY_REGEX.is_match(&line) {
return true;
}
// Finally, compile the source code.
rustpython_parser::parser::parse_program(&line, "<filename>").is_ok()
}
/// Returns `true` if a line is probably part of some multiline code.
fn multiline_case(line: &str) -> bool {
if line.ends_with('\\') {
return true;
}
if MULTILINE_ASSIGNMENT_REGEX.is_match(line) {
return true;
}
if BRACKET_REGEX.is_match(line) {
return true;
}
false
}
#[cfg(test)]
mod tests {
use crate::eradicate::detection::comment_contains_code;
#[test]
fn comment_contains_code_basic() {
assert!(comment_contains_code("# x = 1"));
assert!(comment_contains_code("#from foo import eradicate"));
assert!(comment_contains_code("#import eradicate"));
assert!(comment_contains_code(r#"#"key": value,"#));
assert!(comment_contains_code(r#"#"key": "value","#));
assert!(comment_contains_code(r#"#"key": 1 + 1,"#));
assert!(comment_contains_code("#'key': 1 + 1,"));
assert!(comment_contains_code(r#"#"key": {"#));
assert!(comment_contains_code("#}"));
assert!(comment_contains_code("#} )]"));
assert!(!comment_contains_code("#"));
assert!(!comment_contains_code("# This is a (real) comment."));
assert!(!comment_contains_code("# 123"));
assert!(!comment_contains_code("# 123.1"));
assert!(!comment_contains_code("# 1, 2, 3"));
assert!(!comment_contains_code("x = 1 # x = 1"));
assert!(!comment_contains_code(
"# pylint: disable=redefined-outer-name"
));
assert!(!comment_contains_code("# Issue #999: This is not code"));
// TODO(charlie): This should be `true` under aggressive mode.
assert!(!comment_contains_code("#},"));
}
#[test]
fn comment_contains_code_with_print() {
assert!(comment_contains_code("#print"));
assert!(comment_contains_code("#print(1)"));
assert!(comment_contains_code("#print 1"));
assert!(!comment_contains_code("#to print"));
}
#[test]
fn comment_contains_code_with_return() {
assert!(comment_contains_code("#return x"));
assert!(!comment_contains_code("#to print"));
}
#[test]
fn comment_contains_code_with_multiline() {
assert!(comment_contains_code("#else:"));
assert!(comment_contains_code("# else : "));
assert!(comment_contains_code(r#"# "foo %d" % \\"#));
assert!(comment_contains_code("#elif True:"));
assert!(comment_contains_code("#x = foo("));
assert!(comment_contains_code("#except Exception:"));
assert!(!comment_contains_code("# this is = to that :("));
assert!(!comment_contains_code("#else"));
assert!(!comment_contains_code("#or else:"));
assert!(!comment_contains_code("#else True:"));
// TODO(charlie): This should be `true` under aggressive mode.
assert!(!comment_contains_code("#def foo():"));
}
#[test]
fn comment_contains_code_with_sentences() {
assert!(!comment_contains_code("#code is good"));
}
#[test]
fn comment_contains_code_with_encoding() {
assert!(comment_contains_code("# codings=utf-8"));
assert!(!comment_contains_code("# coding=utf-8"));
assert!(!comment_contains_code("#coding= utf-8"));
assert!(!comment_contains_code("# coding: utf-8"));
assert!(!comment_contains_code("# encoding: utf8"));
}
#[test]
fn comment_contains_code_with_default_allowlist() {
assert!(!comment_contains_code("# pylint: disable=A0123"));
assert!(!comment_contains_code("# pylint:disable=A0123"));
assert!(!comment_contains_code("# pylint: disable = A0123"));
assert!(!comment_contains_code("# pylint:disable = A0123"));
assert!(!comment_contains_code("# pyright: reportErrorName=true"));
assert!(!comment_contains_code("# noqa"));
assert!(!comment_contains_code("# NOQA"));
assert!(!comment_contains_code("# noqa: A123"));
assert!(!comment_contains_code("# noqa:A123"));
assert!(!comment_contains_code("# nosec"));
assert!(!comment_contains_code("# fmt: on"));
assert!(!comment_contains_code("# fmt: off"));
assert!(!comment_contains_code("# fmt:on"));
assert!(!comment_contains_code("# fmt:off"));
assert!(!comment_contains_code("# isort: on"));
assert!(!comment_contains_code("# isort:on"));
assert!(!comment_contains_code("# isort: off"));
assert!(!comment_contains_code("# isort:off"));
assert!(!comment_contains_code("# isort: skip"));
assert!(!comment_contains_code("# isort:skip"));
assert!(!comment_contains_code("# isort: skip_file"));
assert!(!comment_contains_code("# isort:skip_file"));
assert!(!comment_contains_code("# isort: split"));
assert!(!comment_contains_code("# isort:split"));
assert!(!comment_contains_code("# isort: dont-add-imports"));
assert!(!comment_contains_code("# isort:dont-add-imports"));
assert!(!comment_contains_code(
"# isort: dont-add-imports: [\"import os\"]"
));
assert!(!comment_contains_code(
"# isort:dont-add-imports: [\"import os\"]"
));
assert!(!comment_contains_code(
"# isort: dont-add-imports:[\"import os\"]"
));
assert!(!comment_contains_code(
"# isort:dont-add-imports:[\"import os\"]"
));
assert!(!comment_contains_code("# type: ignore"));
assert!(!comment_contains_code("# type:ignore"));
assert!(!comment_contains_code("# type: ignore[import]"));
assert!(!comment_contains_code("# type:ignore[import]"));
assert!(!comment_contains_code("# TODO: Do that"));
assert!(!comment_contains_code("# FIXME: Fix that"));
assert!(!comment_contains_code("# XXX: What ever"));
}
}

View File

@@ -1,2 +0,0 @@
pub mod checks;
pub mod detection;

Some files were not shown because too many files have changed in this diff Show More