Compare commits

...

28 Commits

Author SHA1 Message Date
Charlie Marsh
0b9188011b Bump version to 0.0.133 2022-11-21 13:39:37 -05:00
Anders Kaseorg
b657d912d9 Propagate errors from glob::Pattern::new (#858) 2022-11-21 13:39:19 -05:00
Charlie Marsh
1559671093 Target isort code in README.md 2022-11-21 13:38:01 -05:00
Charlie Marsh
70a53bf12b Add unit tests for complexity check (#859) 2022-11-21 13:31:17 -05:00
Charlie Marsh
cd1e07f37c Avoid incrementing McCabe complexity for class methods (#857) 2022-11-21 13:30:36 -05:00
Charlie Marsh
7bd6db62d9 Sort relative imports by parent level descending (#856) 2022-11-21 13:30:24 -05:00
Charlie Marsh
f8b49f308d Upgrade RustPython (#855) 2022-11-21 13:20:03 -05:00
Charlie Marsh
b1f9c7b6bd Update default complexity in README.md 2022-11-21 13:19:55 -05:00
Keming
0867d2ded9 Make it visible under light theme (#854) 2022-11-21 10:18:21 -05:00
messense
3f597a3b30 Upgrade maturin to 0.14 (#846) 2022-11-21 10:00:14 -05:00
Charlie Marsh
6733aad216 Avoid attempting to fix PEP 604 violations with deferred annotations (#845) 2022-11-20 21:41:54 -05:00
Harutaka Kawamura
89980ad651 Implement autofix for B013 (#824) 2022-11-20 18:49:07 -05:00
Charlie Marsh
38f896502a Bump version to 0.0.132 2022-11-20 18:10:13 -05:00
Jonathan Plasse
7cab541343 Add convert exit() to sys.exit() rule (#816) 2022-11-20 18:09:40 -05:00
Charlie Marsh
1a3d2ead41 Support PEP 562 (#841) 2022-11-20 17:55:57 -05:00
Jonathan Plasse
f96c64b40d Fix N804 class method with positional only args (#836) 2022-11-20 15:48:09 -05:00
Charlie Marsh
0791869451 Add RUF to list of fixable defaults (#838) 2022-11-20 15:40:14 -05:00
Charlie Marsh
965918744b Replace FNV with rustc-hash (#837) 2022-11-20 15:38:31 -05:00
Charlie Marsh
6b4aedb366 Bump version to 0.0.131 2022-11-20 13:40:58 -05:00
Charlie Marsh
8123e3e94e Remove extraneous Python file 2022-11-20 13:39:55 -05:00
Charlie Marsh
9f9a545c51 Improve cache performance by removing cacache dependency (#833) 2022-11-20 13:36:33 -05:00
Charlie Marsh
5bf8219db3 Make main.rs robust to cache initialization failures (#831) 2022-11-20 11:05:17 -05:00
Charlie Marsh
529513bf02 Add CACHEDIR.TAG to .ruff_cache (#830) 2022-11-20 10:53:31 -05:00
Charlie Marsh
124782771f Bump version to 0.0.130 2022-11-20 10:37:19 -05:00
Charlie Marsh
98cab5cdba Add class names to NamedTuple and TypedDict rules (#829) 2022-11-20 10:29:47 -05:00
Martin Lehoux
40f38c94a5 Implement U014: Convert NamedTuple function to class (#819) 2022-11-20 10:26:15 -05:00
Harutaka Kawamura
7839204bf7 Implement autofix for B010 (#823) 2022-11-20 10:14:29 -05:00
Jonathan Plasse
e63ea704f0 Adjust U011 start location (#828) 2022-11-20 10:13:29 -05:00
95 changed files with 1746 additions and 1102 deletions

View File

@@ -34,7 +34,6 @@ 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
@@ -61,7 +60,6 @@ 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
@@ -93,7 +91,6 @@ 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: |
@@ -121,7 +118,6 @@ 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: |
@@ -148,7 +144,6 @@ 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
@@ -187,7 +182,6 @@ 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
@@ -223,7 +217,6 @@ 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:
@@ -261,7 +254,6 @@ 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,7 +36,6 @@ 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
@@ -63,7 +62,6 @@ 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
@@ -95,7 +93,6 @@ jobs:
with:
target: ${{ matrix.target }}
args: --release --out dist
maturin-version: "v0.13.0"
- name: Install built wheel
shell: bash
run: |
@@ -123,7 +120,6 @@ 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: |
@@ -150,7 +146,6 @@ 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
@@ -189,7 +184,6 @@ 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
@@ -225,7 +219,6 @@ 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:
@@ -263,7 +256,6 @@ 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.129
rev: v0.0.133
hooks:
- id: ruff

652
Cargo.lock generated
View File

@@ -94,133 +94,6 @@ 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"
@@ -238,15 +111,6 @@ 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"
@@ -283,50 +147,6 @@ 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"
@@ -357,46 +177,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
[[package]]
name = "byte-tools"
version = "0.3.1"
name = "cachedir"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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"
checksum = "e236bf5873ea57ec2877445297f4da008916bfae51567131acfc54a073d694f3"
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"
@@ -582,15 +370,6 @@ 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"
@@ -616,15 +395,6 @@ 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"
@@ -719,16 +489,6 @@ 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"
@@ -785,24 +545,6 @@ 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"
@@ -890,18 +632,6 @@ 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"
@@ -940,15 +670,15 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8-to-ruff"
version = "0.0.129-dev.0"
version = "0.0.133-dev.0"
dependencies = [
"anyhow",
"clap 4.0.22",
"configparser",
"fnv",
"once_cell",
"regex",
"ruff",
"rustc-hash",
"serde",
"serde_json",
"toml",
@@ -1014,129 +744,6 @@ 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"
@@ -1167,18 +774,6 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "gloo-timers"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9"
dependencies = [
"futures-channel",
"futures-core",
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "half"
version = "1.8.2"
@@ -1209,18 +804,6 @@ 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"
@@ -1363,15 +946,6 @@ 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"
@@ -1508,7 +1082,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if 1.0.0",
"value-bag",
]
[[package]]
@@ -1532,15 +1105,6 @@ 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"
@@ -1727,30 +1291,12 @@ 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"
@@ -1923,18 +1469,6 @@ 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"
@@ -1963,20 +1497,6 @@ 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"
@@ -2248,7 +1768,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.129"
version = "0.0.133"
dependencies = [
"annotate-snippets 0.9.1",
"anyhow",
@@ -2256,7 +1776,7 @@ dependencies = [
"atty",
"bincode",
"bitflags",
"cacache",
"cachedir",
"chrono",
"clap 4.0.22",
"clearscreen",
@@ -2266,7 +1786,6 @@ dependencies = [
"dirs 4.0.0",
"fern",
"filetime",
"fnv",
"getrandom 0.2.8",
"glob",
"insta",
@@ -2281,6 +1800,7 @@ dependencies = [
"rayon",
"regex",
"ropey",
"rustc-hash",
"rustpython-ast",
"rustpython-common",
"rustpython-parser",
@@ -2298,7 +1818,7 @@ dependencies = [
[[package]]
name = "ruff_dev"
version = "0.0.129"
version = "0.0.133"
dependencies = [
"anyhow",
"clap 4.0.22",
@@ -2313,6 +1833,12 @@ 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"
@@ -2328,7 +1854,7 @@ dependencies = [
[[package]]
name = "rustpython-ast"
version = "0.1.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
dependencies = [
"num-bigint",
"rustpython-common",
@@ -2338,7 +1864,7 @@ dependencies = [
[[package]]
name = "rustpython-common"
version = "0.0.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
dependencies = [
"ascii",
"cfg-if 1.0.0",
@@ -2361,7 +1887,7 @@ dependencies = [
[[package]]
name = "rustpython-compiler-core"
version = "0.1.2"
source = "git+https://github.com/RustPython/RustPython.git?rev=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
dependencies = [
"bincode",
"bitflags",
@@ -2378,7 +1904,7 @@ dependencies = [
[[package]]
name = "rustpython-parser"
version = "0.1.2"
source = "git+https://github.com/RustPython/RustPython.git?rev=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
dependencies = [
"ahash",
"anyhow",
@@ -2390,6 +1916,7 @@ dependencies = [
"num-traits",
"phf 0.10.1",
"phf_codegen 0.10.0",
"rustc-hash",
"rustpython-ast",
"rustpython-compiler-core",
"thiserror",
@@ -2479,75 +2006,6 @@ 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"
@@ -2581,37 +2039,12 @@ 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"
@@ -2856,12 +2289,6 @@ 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"
@@ -3000,7 +2427,7 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97acb4c28a254fd7a4aeec976c46a7fa404eac4d7c134b30c75144846d7cb8f"
dependencies = [
"base64 0.13.1",
"base64",
"chunked_transfer",
"flate2",
"log",
@@ -3024,16 +2451,6 @@ 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"
@@ -3055,12 +2472,6 @@ 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"
@@ -3109,18 +2520,6 @@ 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"
@@ -3179,15 +2578,6 @@ 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,7 +6,7 @@ members = [
[package]
name = "ruff"
version = "0.0.129"
version = "0.0.133"
edition = "2021"
[lib]
@@ -18,6 +18,7 @@ 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"] }
colored = { version = "2.0.0" }
@@ -25,7 +26,6 @@ common-path = { version = "1.0.0" }
dirs = { version = "4.0.0" }
fern = { version = "0.6.1" }
filetime = { version = "0.2.17" }
fnv = { version = "1.0.7" }
glob = { version = "0.3.0" }
itertools = { version = "0.10.5" }
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "a13ec97dd4eb925bde4d426c6e422582793b260c" }
@@ -38,9 +38,10 @@ 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 }
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" }
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" }
serde = { version = "1.0.147", features = ["derive"] }
serde_json = { version = "1.0.87" }
strum = { version = "0.24.1", features = ["strum_macros"] }
@@ -52,7 +53,6 @@ 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

View File

@@ -155,6 +155,10 @@ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
# Assume Python 3.10.
target-version = "py310"
[tool.ruff.mccabe]
# Unlike Flake8, default to a complexity level of 10.
max-complexity = 10
```
As an example, the following would configure Ruff to: (1) avoid checking for line-length
@@ -465,7 +469,8 @@ For more, see [pyupgrade](https://pypi.org/project/pyupgrade/3.2.0/) on PyPI.
| U010 | UnnecessaryFutureImport | Unnecessary `__future__` import `...` for target Python version | 🛠 |
| U011 | UnnecessaryLRUCacheParams | Unnecessary parameters to `functools.lru_cache` | 🛠 |
| U012 | UnnecessaryEncodeUTF8 | Unnecessary call to `encode` as UTF-8 | 🛠 |
| U013 | ConvertTypedDictFunctionalToClass | Convert `TypedDict` functional syntax to class syntax | 🛠 |
| U013 | ConvertTypedDictFunctionalToClass | Convert `...` from `TypedDict` functional to class syntax | 🛠 |
| U014 | ConvertNamedTupleFunctionalToClass | Convert `...` from `NamedTuple` functional to class syntax | 🛠 |
### pep8-naming
@@ -549,10 +554,10 @@ For more, see [flake8-bugbear](https://pypi.org/project/flake8-bugbear/22.10.27/
| B007 | UnusedLoopControlVariable | Loop control variable `i` not used within the loop body | 🛠 |
| B008 | FunctionCallArgumentDefault | Do not perform function call in argument defaults | |
| B009 | GetAttrWithConstant | Do not call `getattr` with a constant attribute value. It is not any safer than normal property access. | 🛠 |
| B010 | SetAttrWithConstant | Do not call `setattr` with a constant attribute value. It is not any safer than normal property access. | |
| B010 | SetAttrWithConstant | Do not call `setattr` with a constant attribute value. It is not any safer than normal property access. | 🛠 |
| B011 | DoNotAssertFalse | Do not `assert False` (`python -O` removes these calls), raise `AssertionError()` | 🛠 |
| B012 | JumpStatementInFinally | `return/continue/break` inside finally blocks cause exceptions to be silenced | |
| B013 | RedundantTupleInExceptionHandler | A length-one tuple literal is redundant. Write `except ValueError` instead of `except (ValueError,)`. | |
| B013 | RedundantTupleInExceptionHandler | A length-one tuple literal is redundant. Write `except ValueError` instead of `except (ValueError,)`. | 🛠 |
| B014 | DuplicateHandlerException | Exception handler with duplicate exception: `ValueError` | 🛠 |
| B015 | UselessComparison | Pointless comparison. This comparison does nothing but waste CPU instructions. Either prepend `assert` or remove it. | |
| B016 | CannotRaiseLiteral | Cannot raise a literal. Did you intend to return it or raise an Exception? | |
@@ -663,6 +668,7 @@ For more, see [mccabe](https://pypi.org/project/mccabe/0.7.0/) on PyPI.
| RUF001 | AmbiguousUnicodeCharacterString | String contains ambiguous unicode character '𝐁' (did you mean 'B'?) | 🛠 |
| RUF002 | AmbiguousUnicodeCharacterDocstring | Docstring contains ambiguous unicode character '𝐁' (did you mean 'B'?) | 🛠 |
| RUF003 | AmbiguousUnicodeCharacterComment | Comment contains ambiguous unicode character '𝐁' (did you mean 'B'?) | |
| RUF101 | ConvertExitToSysExit | `exit()` is only available in the interpreter, use `sys.exit()` instead | 🛠 |
### Meta rules
@@ -820,7 +826,7 @@ including:
- [`flake8-boolean-trap`](https://pypi.org/project/flake8-boolean-trap/)
- [`mccabe`](https://pypi.org/project/mccabe/)
- [`isort`](https://pypi.org/project/isort/)
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (14/33)
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (15/33)
- [`autoflake`](https://pypi.org/project/autoflake/) (1/7)
Beyond rule-set parity, Ruff suffers from the following limitations vis-à-vis Flake8:
@@ -852,7 +858,7 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
- [`mccabe`](https://pypi.org/project/mccabe/)
Ruff can also replace [`isort`](https://pypi.org/project/isort/), [`yesqa`](https://github.com/asottile/yesqa),
and a subset of the rules implemented in [`pyupgrade`](https://pypi.org/project/pyupgrade/) (14/33).
and a subset of the rules implemented in [`pyupgrade`](https://pypi.org/project/pyupgrade/) (15/33).
If you're looking to use Ruff, but rely on an unsupported Flake8 plugin, free to file an Issue.
@@ -891,7 +897,7 @@ select = [
"E",
"W",
# isort
"I"
"I001"
]
src = ["src", "tests"]

View File

@@ -771,7 +771,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8_to_ruff"
version = "0.0.129"
version = "0.0.133"
dependencies = [
"anyhow",
"clap",
@@ -1975,7 +1975,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.129"
version = "0.0.133"
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=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
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=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
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=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
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=27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb#27bf82a2251d7e6ac6cd75e6ad51be12a53d84bb"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
dependencies = [
"ahash",
"anyhow",

View File

@@ -1,6 +1,6 @@
[package]
name = "flake8-to-ruff"
version = "0.0.129-dev.0"
version = "0.0.133-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.13,<0.14"]
requires = ["maturin>=0.14,<0.15"]
build-backend = "maturin"
[tool.maturin]

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());
@@ -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>,
) -> FnvHashMap<String, Vec<CheckCodePrefix>> {
let mut per_file_ignores: FnvHashMap<String, Vec<CheckCodePrefix>> = FnvHashMap::default();
) -> FxHashMap<String, Vec<CheckCodePrefix>> {
let mut per_file_ignores: FxHashMap<String, Vec<CheckCodePrefix>> = FxHashMap::default();
for pair in pairs {
per_file_ignores
.entry(pair.pattern)

View File

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

View File

@@ -34,3 +34,4 @@ 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

@@ -106,3 +106,33 @@ 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

@@ -30,6 +30,14 @@ 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,6 +10,27 @@ 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

5
resources/test/fixtures/RUF101_0.py vendored Normal file
View File

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

10
resources/test/fixtures/RUF101_1.py vendored Normal file
View File

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

7
resources/test/fixtures/RUF101_2.py vendored Normal file
View File

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

7
resources/test/fixtures/RUF101_3.py vendored Normal file
View File

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

7
resources/test/fixtures/RUF101_4.py vendored Normal file
View File

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

7
resources/test/fixtures/RUF101_5.py vendored Normal file
View File

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

12
resources/test/fixtures/RUF101_6.py vendored Normal file
View File

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

View File

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

View File

@@ -1,40 +1,43 @@
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:
...
from typing import Union
def f(x: typing.Union[(str, int)]) -> None:
...
def f(x: typing.Union[(str, int), float]) -> None:
...
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:
...

22
resources/test/fixtures/U014.py vendored Normal file
View File

@@ -0,0 +1,22 @@
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

@@ -0,0 +1,4 @@
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.129"
version = "0.0.133"
edition = "2021"
[dependencies]
@@ -10,8 +10,8 @@ codegen = { version = "0.2.0" }
itertools = { version = "0.10.5" }
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "a13ec97dd4eb925bde4d426c6e422582793b260c" }
ruff = { path = ".." }
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" }
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" }
strum = { version = "0.24.1", features = ["strum_macros"] }
strum_macros = { version = "0.24.3" }

View File

@@ -1,6 +1,6 @@
use fnv::{FnvHashMap, FnvHashSet};
use once_cell::sync::Lazy;
use regex::Regex;
use rustc_hash::{FxHashMap, FxHashSet};
use rustpython_ast::{Excepthandler, ExcepthandlerKind, Expr, ExprKind, Location, Stmt, StmtKind};
use crate::ast::types::Range;
@@ -45,7 +45,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: &FnvHashMap<&str, &'a str>,
import_aliases: &FxHashMap<&str, &'a str>,
) -> Vec<&'a str> {
if let Some(head) = call_path.first() {
if let Some(origin) = import_aliases.get(head) {
@@ -79,8 +79,8 @@ pub fn match_module_member(
expr: &Expr,
module: &str,
member: &str,
from_imports: &FnvHashMap<&str, FnvHashSet<&str>>,
import_aliases: &FnvHashMap<&str, &str>,
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
import_aliases: &FxHashMap<&str, &str>,
) -> bool {
match_call_path(
&dealias_call_path(collect_call_paths(expr), import_aliases),
@@ -97,7 +97,7 @@ pub fn match_call_path(
call_path: &[&str],
module: &str,
member: &str,
from_imports: &FnvHashMap<&str, FnvHashSet<&str>>,
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
) -> bool {
// If we have no segments, we can't ever match.
let num_segments = call_path.len();
@@ -295,7 +295,7 @@ pub fn match_trailing_content(stmt: &Stmt, locator: &SourceCodeLocator) -> bool
#[cfg(test)]
mod tests {
use anyhow::Result;
use fnv::{FnvHashMap, FnvHashSet};
use rustc_hash::{FxHashMap, FxHashSet};
use rustpython_parser::parser;
use crate::ast::helpers::match_module_member;
@@ -307,8 +307,8 @@ mod tests {
&expr,
"",
"list",
&FnvHashMap::default(),
&FnvHashMap::default(),
&FxHashMap::default(),
&FxHashMap::default(),
));
Ok(())
}
@@ -320,8 +320,8 @@ mod tests {
&expr,
"typing.re",
"Match",
&FnvHashMap::default(),
&FnvHashMap::default(),
&FxHashMap::default(),
&FxHashMap::default(),
));
Ok(())
}
@@ -333,16 +333,16 @@ mod tests {
&expr,
"typing.re",
"Match",
&FnvHashMap::default(),
&FnvHashMap::default(),
&FxHashMap::default(),
&FxHashMap::default(),
));
let expr = parser::parse_expression("re.Match", "<filename>")?;
assert!(!match_module_member(
&expr,
"typing.re",
"Match",
&FnvHashMap::default(),
&FnvHashMap::default(),
&FxHashMap::default(),
&FxHashMap::default(),
));
Ok(())
}
@@ -354,8 +354,8 @@ mod tests {
&expr,
"typing.re",
"Match",
&FnvHashMap::from_iter([("typing.re", FnvHashSet::from_iter(["*"]))]),
&FnvHashMap::default()
&FxHashMap::from_iter([("typing.re", FxHashSet::from_iter(["*"]))]),
&FxHashMap::default()
));
Ok(())
}
@@ -367,8 +367,8 @@ mod tests {
&expr,
"typing.re",
"Match",
&FnvHashMap::from_iter([("typing.re", FnvHashSet::from_iter(["Match"]))]),
&FnvHashMap::default()
&FxHashMap::from_iter([("typing.re", FxHashSet::from_iter(["Match"]))]),
&FxHashMap::default()
));
Ok(())
}
@@ -380,8 +380,8 @@ mod tests {
&expr,
"typing.re",
"Match",
&FnvHashMap::from_iter([("typing", FnvHashSet::from_iter(["re"]))]),
&FnvHashMap::default()
&FxHashMap::from_iter([("typing", FxHashSet::from_iter(["re"]))]),
&FxHashMap::default()
));
let expr = parser::parse_expression("match.Match", "<filename>")?;
@@ -389,8 +389,8 @@ mod tests {
&expr,
"typing.re.match",
"Match",
&FnvHashMap::from_iter([("typing.re", FnvHashSet::from_iter(["match"]))]),
&FnvHashMap::default()
&FxHashMap::from_iter([("typing.re", FxHashSet::from_iter(["match"]))]),
&FxHashMap::default()
));
let expr = parser::parse_expression("re.match.Match", "<filename>")?;
@@ -398,8 +398,8 @@ mod tests {
&expr,
"typing.re.match",
"Match",
&FnvHashMap::from_iter([("typing", FnvHashSet::from_iter(["re"]))]),
&FnvHashMap::default()
&FxHashMap::from_iter([("typing", FxHashSet::from_iter(["re"]))]),
&FxHashMap::default()
));
Ok(())
}
@@ -411,8 +411,8 @@ mod tests {
&expr,
"typing.re",
"Match",
&FnvHashMap::from_iter([("typing.re", FnvHashSet::from_iter(["Match"]))]),
&FnvHashMap::from_iter([("IMatch", "Match")]),
&FxHashMap::from_iter([("typing.re", FxHashSet::from_iter(["Match"]))]),
&FxHashMap::from_iter([("IMatch", "Match")]),
));
Ok(())
}
@@ -424,8 +424,8 @@ mod tests {
&expr,
"typing.re",
"Match",
&FnvHashMap::default(),
&FnvHashMap::from_iter([("t", "typing.re")]),
&FxHashMap::default(),
&FxHashMap::from_iter([("t", "typing.re")]),
));
Ok(())
}
@@ -437,8 +437,8 @@ mod tests {
&expr,
"typing.re",
"Match",
&FnvHashMap::default(),
&FnvHashMap::from_iter([("t", "typing")]),
&FxHashMap::default(),
&FxHashMap::from_iter([("t", "typing")]),
));
Ok(())
}

View File

@@ -1,6 +1,6 @@
use std::sync::atomic::{AtomicUsize, Ordering};
use fnv::FnvHashMap;
use rustc_hash::FxHashMap;
use rustpython_ast::{Expr, Keyword};
use rustpython_parser::ast::{Located, Location};
@@ -19,9 +19,7 @@ impl Range {
pub fn from_located<T>(located: &Located<T>) -> Self {
Range {
location: located.location,
end_location: located
.end_location
.expect("AST nodes should have end_location."),
end_location: located.end_location.unwrap(),
}
}
}
@@ -54,7 +52,7 @@ pub struct Scope<'a> {
pub id: usize,
pub kind: ScopeKind<'a>,
pub import_starred: bool,
pub values: FnvHashMap<&'a str, Binding>,
pub values: FxHashMap<&'a str, Binding>,
}
impl<'a> Scope<'a> {
@@ -63,7 +61,7 @@ impl<'a> Scope<'a> {
id: id(),
kind,
import_starred: false,
values: FnvHashMap::default(),
values: FxHashMap::default(),
}
}
}

View File

@@ -1,19 +1,11 @@
// 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;
@@ -82,28 +74,59 @@ fn cache_dir() -> &'static str {
"./.ruff_cache"
}
fn cache_key(path: &Path, settings: &Settings, autofix: &fixer::Mode) -> String {
fn content_dir() -> &'static str {
"content"
}
fn cache_key(path: &Path, settings: &Settings, autofix: &fixer::Mode) -> u64 {
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);
format!(
"{}@{}@{}",
path.absolutize().unwrap().to_string_lossy(),
CARGO_PKG_VERSION,
hasher.finish()
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)?;
}
// 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(())
}
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,
)
}
pub fn init() -> Result<()> {
let gitignore_path = Path::new(cache_dir()).join(".gitignore");
if gitignore_path.exists() {
return Ok(());
}
create_dir_all(cache_dir())?;
let mut file = File::create(gitignore_path)?;
file.write_all(b"*").map_err(|e| e.into())
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,
@@ -115,9 +138,8 @@ pub fn get(
return None;
};
#[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[..]) {
if let Ok(encoded) = read_sync(&cache_key(path, settings, autofix)) {
match bincode::deserialize::<CheckResult>(&encoded[..]) {
Ok(CheckResult {
metadata: CacheMetadata { mtime },
messages,
@@ -127,13 +149,12 @@ 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,
@@ -146,18 +167,15 @@ 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,
};
#[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(),
if let Err(e) = write_sync(
&cache_key(path, settings, autofix),
&bincode::serialize(&check_result).unwrap(),
) {
error!("Failed to write to cache: {e:?}")
}

View File

@@ -4,9 +4,9 @@ use std::collections::BTreeMap;
use std::ops::Deref;
use std::path::Path;
use fnv::{FnvHashMap, FnvHashSet};
use itertools::Itertools;
use log::error;
use rustc_hash::{FxHashMap, FxHashSet};
use rustpython_parser::ast::{
Arg, Arguments, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind,
KeywordData, Operator, Stmt, StmtKind, Suite,
@@ -37,7 +37,7 @@ use crate::visibility::{module_visibility, transition_scope, Modifier, Visibilit
use crate::{
docstrings, flake8_2020, flake8_annotations, flake8_bandit, flake8_blind_except,
flake8_boolean_trap, flake8_bugbear, flake8_builtins, flake8_comprehensions, flake8_print,
flake8_tidy_imports, mccabe, pep8_naming, pycodestyle, pydocstyle, pyflakes, pyupgrade,
flake8_tidy_imports, mccabe, pep8_naming, pycodestyle, pydocstyle, pyflakes, pyupgrade, rules,
};
const GLOBAL_SCOPE_INDEX: usize = 0;
@@ -54,10 +54,10 @@ pub struct Checker<'a> {
definitions: Vec<(Definition<'a>, Visibility)>,
// Edit tracking.
// TODO(charlie): Instead of exposing deletions, wrap in a public API.
pub(crate) deletions: FnvHashSet<usize>,
pub(crate) deletions: FxHashSet<usize>,
// Import tracking.
pub(crate) from_imports: FnvHashMap<&'a str, FnvHashSet<&'a str>>,
pub(crate) import_aliases: FnvHashMap<&'a str, &'a str>,
pub(crate) from_imports: FxHashMap<&'a str, FxHashSet<&'a str>>,
pub(crate) import_aliases: FxHashMap<&'a str, &'a str>,
// Retain all scopes and parent nodes, along with a stack of indexes to track which are active
// at various points in time.
pub(crate) parents: Vec<&'a Stmt>,
@@ -74,6 +74,7 @@ pub struct Checker<'a> {
visible_scope: VisibleScope,
in_f_string: Option<Range>,
in_annotation: bool,
in_deferred_string_annotation: bool,
in_literal: bool,
in_subscript: bool,
seen_import_boundary: bool,
@@ -116,6 +117,7 @@ impl<'a> Checker<'a> {
},
in_f_string: Default::default(),
in_annotation: Default::default(),
in_deferred_string_annotation: Default::default(),
in_literal: Default::default(),
in_subscript: Default::default(),
seen_import_boundary: Default::default(),
@@ -471,7 +473,7 @@ where
}
StmtKind::Return { .. } => {
if self.settings.enabled.contains(&CheckCode::F706) {
if let Some(index) = self.scope_stack.last().cloned() {
if let Some(&index) = self.scope_stack.last() {
if matches!(
self.scopes[index].kind,
ScopeKind::Class(_) | ScopeKind::Module
@@ -687,7 +689,7 @@ where
if let Some(module) = module {
self.from_imports
.entry(module)
.or_insert_with(FnvHashSet::default)
.or_insert_with(FxHashSet::default)
.extend(
names
.iter()
@@ -1002,6 +1004,11 @@ where
self, stmt, targets, value,
);
}
if self.settings.enabled.contains(&CheckCode::U014) {
pyupgrade::plugins::convert_named_tuple_functional_to_class(
self, stmt, targets, value,
);
}
}
StmtKind::AnnAssign { target, value, .. } => {
if self.settings.enabled.contains(&CheckCode::E731) {
@@ -1145,7 +1152,8 @@ where
match &expr.node {
ExprKind::Subscript { value, slice, .. } => {
// Ex) typing.List[...]
if self.settings.enabled.contains(&CheckCode::U007)
if !self.in_deferred_string_annotation
&& self.settings.enabled.contains(&CheckCode::U007)
&& self.settings.target_version >= PythonVersion::Py310
{
pyupgrade::plugins::use_pep604_annotation(self, expr, value, slice);
@@ -1183,7 +1191,8 @@ where
match ctx {
ExprContext::Load => {
// Ex) List[...]
if self.settings.enabled.contains(&CheckCode::U006)
if !self.in_deferred_string_annotation
&& self.settings.enabled.contains(&CheckCode::U006)
&& self.settings.target_version >= PythonVersion::Py39
&& typing::is_pep585_builtin(
expr,
@@ -1522,6 +1531,11 @@ where
}
}
}
// Ruff
if self.settings.enabled.contains(&CheckCode::RUF101) {
rules::plugins::convert_exit_to_sys_exit(self, func);
}
}
ExprKind::Dict { keys, .. } => {
let check_repeated_literals = self.settings.enabled.contains(&CheckCode::F601);
@@ -2164,6 +2178,10 @@ impl<'a> Checker<'a> {
&self.scopes[*(self.scope_stack.last().expect("No current scope found."))]
}
pub fn current_scopes(&self) -> impl Iterator<Item = &Scope> {
self.scope_stack.iter().rev().map(|s| &self.scopes[*s])
}
pub fn current_parent(&self) -> &'a Stmt {
self.parents[*(self.parent_stack.last().expect("No parent found."))]
}
@@ -2183,7 +2201,7 @@ impl<'a> Checker<'a> {
'b: 'a,
{
if self.settings.enabled.contains(&CheckCode::F402) {
let scope = &self.scopes[*(self.scope_stack.last().expect("No current scope found."))];
let scope = self.current_scope();
if let Some(existing) = scope.values.get(&name) {
if matches!(binding.kind, BindingKind::LoopVar)
&& matches!(
@@ -2208,7 +2226,7 @@ impl<'a> Checker<'a> {
// TODO(charlie): Don't treat annotations as assignments if there is an existing
// value.
let scope = &self.scopes[*(self.scope_stack.last().expect("No current scope found."))];
let scope = self.current_scope();
let binding = match scope.values.get(&name) {
None => binding,
Some(existing) => Binding {
@@ -2224,8 +2242,7 @@ impl<'a> Checker<'a> {
fn handle_node_load(&mut self, expr: &Expr) {
if let ExprKind::Name { id, .. } = &expr.node {
let scope_id =
self.scopes[*(self.scope_stack.last().expect("No current scope found."))].id;
let scope_id = self.current_scope().id;
let mut first_iter = true;
let mut in_generator = false;
@@ -2380,7 +2397,7 @@ impl<'a> Checker<'a> {
return;
}
let current = &self.scopes[*(self.scope_stack.last().expect("No current scope found."))];
let current = self.current_scope();
if id == "__all__"
&& matches!(current.kind, ScopeKind::Module)
&& matches!(
@@ -2489,9 +2506,11 @@ impl<'a> Checker<'a> {
}
}
for (expr, (scopes, parents)) in allocator.iter().zip(stacks) {
self.in_deferred_string_annotation = true;
self.scope_stack = scopes;
self.parent_stack = parents;
self.visit_expr(expr);
self.in_deferred_string_annotation = false;
}
}

View File

@@ -170,6 +170,7 @@ pub enum CheckCode {
U011,
U012,
U013,
U014,
// pydocstyle
D100,
D101,
@@ -248,6 +249,7 @@ pub enum CheckCode {
RUF001,
RUF002,
RUF003,
RUF101,
// Meta
M001,
}
@@ -500,7 +502,8 @@ pub enum CheckKind {
UnnecessaryFutureImport(Vec<String>),
UnnecessaryLRUCacheParams,
UnnecessaryEncodeUTF8,
ConvertTypedDictFunctionalToClass,
ConvertTypedDictFunctionalToClass(String),
ConvertNamedTupleFunctionalToClass(String),
// pydocstyle
BlankLineAfterLastSection(String),
BlankLineAfterSection(String),
@@ -581,6 +584,7 @@ pub enum CheckKind {
AmbiguousUnicodeCharacterString(char, char),
AmbiguousUnicodeCharacterDocstring(char, char),
AmbiguousUnicodeCharacterComment(char, char),
ConvertExitToSysExit,
// Meta
UnusedNOQA(Option<Vec<String>>),
}
@@ -773,7 +777,8 @@ impl CheckCode {
CheckCode::U010 => CheckKind::UnnecessaryFutureImport(vec!["...".to_string()]),
CheckCode::U011 => CheckKind::UnnecessaryLRUCacheParams,
CheckCode::U012 => CheckKind::UnnecessaryEncodeUTF8,
CheckCode::U013 => CheckKind::ConvertTypedDictFunctionalToClass,
CheckCode::U013 => CheckKind::ConvertTypedDictFunctionalToClass("...".to_string()),
CheckCode::U014 => CheckKind::ConvertNamedTupleFunctionalToClass("...".to_string()),
// pydocstyle
CheckCode::D100 => CheckKind::PublicModule,
CheckCode::D101 => CheckKind::PublicClass,
@@ -869,6 +874,7 @@ impl CheckCode {
CheckCode::RUF001 => CheckKind::AmbiguousUnicodeCharacterString('𝐁', 'B'),
CheckCode::RUF002 => CheckKind::AmbiguousUnicodeCharacterDocstring('𝐁', 'B'),
CheckCode::RUF003 => CheckKind::AmbiguousUnicodeCharacterComment('𝐁', 'B'),
CheckCode::RUF101 => CheckKind::ConvertExitToSysExit,
// Meta
CheckCode::M001 => CheckKind::UnusedNOQA(None),
}
@@ -1005,6 +1011,7 @@ impl CheckCode {
CheckCode::U011 => CheckCategory::Pyupgrade,
CheckCode::U012 => CheckCategory::Pyupgrade,
CheckCode::U013 => CheckCategory::Pyupgrade,
CheckCode::U014 => CheckCategory::Pyupgrade,
CheckCode::D100 => CheckCategory::Pydocstyle,
CheckCode::D101 => CheckCategory::Pydocstyle,
CheckCode::D102 => CheckCategory::Pydocstyle,
@@ -1078,6 +1085,7 @@ impl CheckCode {
CheckCode::RUF001 => CheckCategory::Ruff,
CheckCode::RUF002 => CheckCategory::Ruff,
CheckCode::RUF003 => CheckCategory::Ruff,
CheckCode::RUF101 => CheckCategory::Ruff,
CheckCode::M001 => CheckCategory::Meta,
}
}
@@ -1227,7 +1235,8 @@ impl CheckKind {
CheckKind::UnnecessaryFutureImport(_) => &CheckCode::U010,
CheckKind::UnnecessaryLRUCacheParams => &CheckCode::U011,
CheckKind::UnnecessaryEncodeUTF8 => &CheckCode::U012,
CheckKind::ConvertTypedDictFunctionalToClass => &CheckCode::U013,
CheckKind::ConvertTypedDictFunctionalToClass(_) => &CheckCode::U013,
CheckKind::ConvertNamedTupleFunctionalToClass(_) => &CheckCode::U014,
// pydocstyle
CheckKind::BlankLineAfterLastSection(_) => &CheckCode::D413,
CheckKind::BlankLineAfterSection(_) => &CheckCode::D410,
@@ -1308,6 +1317,7 @@ impl CheckKind {
CheckKind::AmbiguousUnicodeCharacterString(..) => &CheckCode::RUF001,
CheckKind::AmbiguousUnicodeCharacterDocstring(..) => &CheckCode::RUF002,
CheckKind::AmbiguousUnicodeCharacterComment(..) => &CheckCode::RUF003,
CheckKind::ConvertExitToSysExit => &CheckCode::RUF101,
// Meta
CheckKind::UnusedNOQA(_) => &CheckCode::M001,
}
@@ -1776,8 +1786,11 @@ impl CheckKind {
"Unnecessary parameters to `functools.lru_cache`".to_string()
}
CheckKind::UnnecessaryEncodeUTF8 => "Unnecessary call to `encode` as UTF-8".to_string(),
CheckKind::ConvertTypedDictFunctionalToClass => {
"Convert `TypedDict` functional syntax to class syntax".to_string()
CheckKind::ConvertTypedDictFunctionalToClass(name) => {
format!("Convert `{name}` from `TypedDict` functional to class syntax")
}
CheckKind::ConvertNamedTupleFunctionalToClass(name) => {
format!("Convert `{name}` from `NamedTuple` functional to class syntax")
}
// pydocstyle
CheckKind::FitsOnOneLine => "One-line docstring should fit on one line".to_string(),
@@ -1994,6 +2007,9 @@ impl CheckKind {
'{representant}'?)"
)
}
CheckKind::ConvertExitToSysExit => "`exit()` is only available in the interpreter, \
use `sys.exit()` instead"
.to_string(),
// Meta
CheckKind::UnusedNOQA(codes) => match codes {
None => "Unused `noqa` directive".to_string(),
@@ -2045,7 +2061,9 @@ impl CheckKind {
| CheckKind::BlankLineAfterSummary
| CheckKind::BlankLineBeforeSection(..)
| CheckKind::CapitalizeSectionName(..)
| CheckKind::ConvertTypedDictFunctionalToClass
| CheckKind::ConvertExitToSysExit
| CheckKind::ConvertNamedTupleFunctionalToClass(..)
| CheckKind::ConvertTypedDictFunctionalToClass(..)
| CheckKind::DashedUnderlineAfterSection(..)
| CheckKind::DeprecatedUnittestAlias(..)
| CheckKind::DoNotAssertFalse
@@ -2071,11 +2089,13 @@ impl CheckKind {
| CheckKind::PPrintFound
| CheckKind::PrintFound
| CheckKind::RaiseNotImplemented
| CheckKind::RedundantTupleInExceptionHandler(..)
| CheckKind::SectionNameEndsInColon(..)
| CheckKind::SectionNotOverIndented(..)
| CheckKind::SectionUnderlineAfterName(..)
| CheckKind::SectionUnderlineMatchesSectionLength(..)
| CheckKind::SectionUnderlineNotOverIndented(..)
| CheckKind::SetAttrWithConstant
| CheckKind::SuperCallWithParameters
| CheckKind::TrueFalseComparison(..)
| CheckKind::TypeOfPrimitive(..)

View File

@@ -267,6 +267,9 @@ pub enum CheckCodePrefix {
RUF001,
RUF002,
RUF003,
RUF1,
RUF10,
RUF101,
S,
S1,
S10,
@@ -297,6 +300,7 @@ pub enum CheckCodePrefix {
U011,
U012,
U013,
U014,
W,
W2,
W29,
@@ -1057,12 +1061,20 @@ 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],
CheckCodePrefix::RUF => vec![
CheckCode::RUF001,
CheckCode::RUF002,
CheckCode::RUF003,
CheckCode::RUF101,
],
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,
@@ -1111,6 +1123,7 @@ impl CheckCodePrefix {
CheckCode::U011,
CheckCode::U012,
CheckCode::U013,
CheckCode::U014,
],
CheckCodePrefix::U0 => vec![
CheckCode::U001,
@@ -1125,6 +1138,7 @@ impl CheckCodePrefix {
CheckCode::U011,
CheckCode::U012,
CheckCode::U013,
CheckCode::U014,
],
CheckCodePrefix::U00 => vec![
CheckCode::U001,
@@ -1149,11 +1163,13 @@ impl CheckCodePrefix {
CheckCode::U011,
CheckCode::U012,
CheckCode::U013,
CheckCode::U014,
],
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::W => vec![CheckCode::W292, CheckCode::W605],
CheckCodePrefix::W2 => vec![CheckCode::W292],
CheckCodePrefix::W29 => vec![CheckCode::W292],
@@ -1466,6 +1482,9 @@ 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,
@@ -1496,6 +1515,7 @@ impl CheckCodePrefix {
CheckCodePrefix::U011 => PrefixSpecificity::Explicit,
CheckCodePrefix::U012 => PrefixSpecificity::Explicit,
CheckCodePrefix::U013 => PrefixSpecificity::Explicit,
CheckCodePrefix::U014 => PrefixSpecificity::Explicit,
CheckCodePrefix::W => PrefixSpecificity::Category,
CheckCodePrefix::W2 => PrefixSpecificity::Hundreds,
CheckCodePrefix::W29 => PrefixSpecificity::Tens,

View File

@@ -1,8 +1,9 @@
use std::path::PathBuf;
use anyhow::Result;
use clap::{command, Parser};
use fnv::FnvHashMap;
use regex::Regex;
use rustc_hash::FxHashMap;
use crate::checks_gen::CheckCodePrefix;
use crate::logging::LogLevel;
@@ -146,8 +147,8 @@ pub fn extract_log_level(cli: &Cli) -> LogLevel {
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();
) -> Result<Vec<PerFileIgnore>> {
let mut per_file_ignores: FxHashMap<String, Vec<CheckCodePrefix>> = FxHashMap::default();
for pair in pairs {
per_file_ignores
.entry(pair.pattern)

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<FnvHashSet<&'static str>> = Lazy::new(|| {
FnvHashSet::from_iter([
pub(crate) static GOOGLE_SECTION_NAMES: Lazy<FxHashSet<&'static str>> = Lazy::new(|| {
FxHashSet::from_iter([
"Args",
"Arguments",
"Attention",
@@ -36,36 +36,35 @@ pub(crate) static GOOGLE_SECTION_NAMES: Lazy<FnvHashSet<&'static str>> = Lazy::n
])
});
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",
])
});
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",
])
});

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<FnvHashSet<&'static str>> = Lazy::new(|| {
FnvHashSet::from_iter([
pub(crate) static LOWERCASE_NUMPY_SECTION_NAMES: Lazy<FxHashSet<&'static str>> = Lazy::new(|| {
FxHashSet::from_iter([
"short summary",
"extended summary",
"parameters",
@@ -21,8 +21,8 @@ pub(crate) static LOWERCASE_NUMPY_SECTION_NAMES: Lazy<FnvHashSet<&'static str>>
])
});
pub(crate) static NUMPY_SECTION_NAMES: Lazy<FnvHashSet<&'static str>> = Lazy::new(|| {
FnvHashSet::from_iter([
pub(crate) static NUMPY_SECTION_NAMES: Lazy<FxHashSet<&'static str>> = Lazy::new(|| {
FxHashSet::from_iter([
"Short Summary",
"Extended Summary",
"Parameters",

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<FnvHashSet<&'static str>> {
pub(crate) fn section_names(&self) -> &Lazy<FxHashSet<&'static str>> {
match self {
SectionStyle::NumPy => &NUMPY_SECTION_NAMES,
SectionStyle::Google => &GOOGLE_SECTION_NAMES,
}
}
pub(crate) fn lowercase_section_names(&self) -> &Lazy<FnvHashSet<&'static str>> {
pub(crate) fn lowercase_section_names(&self) -> &Lazy<FxHashSet<&'static str>> {
match self {
SectionStyle::NumPy => &LOWERCASE_NUMPY_SECTION_NAMES,
SectionStyle::Google => &LOWERCASE_GOOGLE_SECTION_NAMES,

View File

@@ -1,4 +1,4 @@
use fnv::{FnvHashMap, FnvHashSet};
use rustc_hash::{FxHashMap, FxHashSet};
use rustpython_ast::{Constant, Expr, ExprKind, Keyword, Stmt, StmtKind};
use crate::ast::helpers::match_module_member;
@@ -9,8 +9,8 @@ use crate::checks::{Check, CheckCode, CheckKind};
fn is_abc_class(
bases: &[Expr],
keywords: &[Keyword],
from_imports: &FnvHashMap<&str, FnvHashSet<&str>>,
import_aliases: &FnvHashMap<&str, &str>,
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
import_aliases: &FxHashMap<&str, &str>,
) -> bool {
keywords.iter().any(|keyword| {
keyword
@@ -46,16 +46,16 @@ fn is_empty_body(body: &[Stmt]) -> bool {
fn is_abstractmethod(
expr: &Expr,
from_imports: &FnvHashMap<&str, FnvHashSet<&str>>,
import_aliases: &FnvHashMap<&str, &str>,
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
import_aliases: &FxHashMap<&str, &str>,
) -> bool {
match_module_member(expr, "abc", "abstractmethod", from_imports, import_aliases)
}
fn is_overload(
expr: &Expr,
from_imports: &FnvHashMap<&str, FnvHashSet<&str>>,
import_aliases: &FnvHashMap<&str, &str>,
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
import_aliases: &FxHashMap<&str, &str>,
) -> bool {
match_module_member(expr, "typing", "overload", from_imports, import_aliases)
}

View File

@@ -1,4 +1,4 @@
use fnv::{FnvHashMap, FnvHashSet};
use rustc_hash::{FxHashMap, FxHashSet};
use rustpython_ast::{Arguments, Constant, Expr, ExprKind};
use crate::ast::helpers::{
@@ -24,8 +24,8 @@ const IMMUTABLE_FUNCS: [(&str, &str); 7] = [
fn is_immutable_func(
expr: &Expr,
extend_immutable_calls: &[(&str, &str)],
from_imports: &FnvHashMap<&str, FnvHashSet<&str>>,
import_aliases: &FnvHashMap<&str, &str>,
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
import_aliases: &FxHashMap<&str, &str>,
) -> bool {
let call_path = dealias_call_path(collect_call_paths(expr), import_aliases);
IMMUTABLE_FUNCS
@@ -37,8 +37,8 @@ fn is_immutable_func(
struct ArgumentDefaultVisitor<'a> {
checks: Vec<(CheckKind, Range)>,
extend_immutable_calls: &'a [(&'a str, &'a str)],
from_imports: &'a FnvHashMap<&'a str, FnvHashSet<&'a str>>,
import_aliases: &'a FnvHashMap<&'a str, &'a str>,
from_imports: &'a FxHashMap<&'a str, FxHashSet<&'a str>>,
import_aliases: &'a FxHashMap<&'a str, &'a str>,
}
impl<'a, 'b> Visitor<'b> for ArgumentDefaultVisitor<'b>

View File

@@ -1,4 +1,4 @@
use fnv::FnvHashMap;
use rustc_hash::FxHashMap;
use rustpython_ast::{Expr, ExprKind};
use crate::ast::types::Range;
@@ -9,7 +9,7 @@ use crate::checks::{Check, CheckKind};
#[derive(Default)]
struct NameFinder<'a> {
names: FnvHashMap<&'a str, &'a Expr>,
names: FxHashMap<&'a str, &'a Expr>,
}
impl<'a, 'b> Visitor<'b> for NameFinder<'a>

View File

@@ -1,4 +1,4 @@
use fnv::{FnvHashMap, FnvHashSet};
use rustc_hash::{FxHashMap, FxHashSet};
use rustpython_ast::{Arguments, Constant, Expr, ExprKind, Operator};
use crate::ast::helpers::{collect_call_paths, dealias_call_path, match_call_path};
@@ -59,8 +59,8 @@ const IMMUTABLE_GENERIC_TYPES: &[(&str, &str)] = &[
pub fn is_mutable_func(
expr: &Expr,
from_imports: &FnvHashMap<&str, FnvHashSet<&str>>,
import_aliases: &FnvHashMap<&str, &str>,
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
import_aliases: &FxHashMap<&str, &str>,
) -> bool {
let call_path = dealias_call_path(collect_call_paths(expr), import_aliases);
MUTABLE_FUNCS
@@ -70,8 +70,8 @@ pub fn is_mutable_func(
fn is_mutable_expr(
expr: &Expr,
from_imports: &FnvHashMap<&str, FnvHashSet<&str>>,
import_aliases: &FnvHashMap<&str, &str>,
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
import_aliases: &FxHashMap<&str, &str>,
) -> bool {
match &expr.node {
ExprKind::List { .. }
@@ -87,8 +87,8 @@ fn is_mutable_expr(
fn is_immutable_annotation(
expr: &Expr,
from_imports: &FnvHashMap<&str, FnvHashSet<&str>>,
import_aliases: &FnvHashMap<&str, &str>,
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
import_aliases: &FxHashMap<&str, &str>,
) -> bool {
match &expr.node {
ExprKind::Name { .. } | ExprKind::Attribute { .. } => {

View File

@@ -1,8 +1,55 @@
use rustpython_ast::{Excepthandler, ExcepthandlerKind, ExprKind};
use anyhow::Result;
use log::error;
use rustpython_ast::{Excepthandler, ExcepthandlerKind, ExprKind, Located};
use rustpython_parser::lexer;
use rustpython_parser::lexer::Tok;
use crate::ast::helpers;
use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::check_ast::Checker;
use crate::checks::{Check, CheckKind};
use crate::code_gen::SourceGenerator;
use crate::SourceCodeLocator;
/// Given a statement like `except (ValueError,)`, find the range of the
/// parenthesized expression.
fn match_tuple_range<T>(located: &Located<T>, locator: &SourceCodeLocator) -> Result<Range> {
// Extract contents from the source code.
let range = Range::from_located(located);
let contents = locator.slice_source_code_range(&range);
// Find the left (opening) and right (closing) parentheses.
let mut location = None;
let mut end_location = None;
let mut count: usize = 0;
for (start, tok, end) in lexer::make_tokenizer(&contents).flatten() {
if matches!(tok, Tok::Lpar) {
if count == 0 {
location = Some(helpers::to_absolute(&start, &range.location));
}
count += 1;
}
if matches!(tok, Tok::Rpar) {
count -= 1;
if count == 0 {
end_location = Some(helpers::to_absolute(&end, &range.location));
break;
}
}
}
if let (Some(location), Some(end_location)) = (location, end_location) {
Ok(Range {
location,
end_location,
})
} else {
Err(anyhow::anyhow!(
"Unable to find left and right parentheses."
))
}
}
/// B013
pub fn redundant_tuple_in_exception_handler(checker: &mut Checker, handlers: &[Excepthandler]) {
@@ -10,11 +57,29 @@ pub fn redundant_tuple_in_exception_handler(checker: &mut Checker, handlers: &[E
let ExcepthandlerKind::ExceptHandler { type_, .. } = &handler.node;
if let Some(type_) = type_ {
if let ExprKind::Tuple { elts, .. } = &type_.node {
if elts.len() == 1 {
checker.add_check(Check::new(
CheckKind::RedundantTupleInExceptionHandler(elts[0].to_string()),
if let [elt] = &elts[..] {
let mut check = Check::new(
CheckKind::RedundantTupleInExceptionHandler(elt.to_string()),
Range::from_located(type_),
));
);
if checker.patch(check.kind.code()) {
let mut generator = SourceGenerator::new();
if let Ok(()) = generator.unparse_expr(elt, 0) {
if let Ok(content) = generator.generate() {
match match_tuple_range(handler, checker.locator) {
Ok(range) => {
check.amend(Fix::replacement(
content,
range.location,
range.end_location,
));
}
Err(e) => error!("Failed to locate parentheses: {}", e),
}
}
}
}
checker.add_check(check)
}
}
}

View File

@@ -1,26 +1,61 @@
use rustpython_ast::{Constant, Expr, ExprKind};
use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Stmt, StmtKind};
use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::check_ast::Checker;
use crate::checks::{Check, CheckKind};
use crate::code_gen::SourceGenerator;
use crate::python::identifiers::IDENTIFIER_REGEX;
use crate::python::keyword::KWLIST;
fn assignment(obj: &Expr, name: &str, value: &Expr) -> Option<String> {
let stmt = Stmt::new(
Default::default(),
Default::default(),
StmtKind::Assign {
targets: vec![Expr::new(
Default::default(),
Default::default(),
ExprKind::Attribute {
value: Box::new(obj.clone()),
attr: name.to_string(),
ctx: ExprContext::Store,
},
)],
value: Box::new(value.clone()),
type_comment: None,
},
);
let mut generator = SourceGenerator::new();
match generator.unparse_stmt(&stmt) {
Ok(()) => generator.generate().ok(),
Err(_) => None,
}
}
/// B010
pub fn setattr_with_constant(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) {
if let ExprKind::Name { id, .. } = &func.node {
if id == "setattr" {
if let [_, arg, _] = args {
if let [obj, name, value] = args {
if let ExprKind::Constant {
value: Constant::Str(value),
value: Constant::Str(name),
..
} = &arg.node
} = &name.node
{
if IDENTIFIER_REGEX.is_match(value) && !KWLIST.contains(&value.as_str()) {
checker.add_check(Check::new(
CheckKind::SetAttrWithConstant,
Range::from_located(expr),
));
if IDENTIFIER_REGEX.is_match(name) && !KWLIST.contains(&name.as_str()) {
let mut check =
Check::new(CheckKind::SetAttrWithConstant, Range::from_located(expr));
if checker.patch(check.kind.code()) {
if let Some(content) = assignment(obj, name, value) {
check.amend(Fix::replacement(
content,
expr.location,
expr.end_location.unwrap(),
));
}
}
checker.add_check(check);
}
}
}

View File

@@ -1,4 +1,4 @@
use fnv::FnvHashMap;
use rustc_hash::FxHashMap;
use rustpython_ast::{Expr, ExprKind, Stmt};
use crate::ast::types::Range;
@@ -11,7 +11,7 @@ use crate::checks::{Check, CheckKind};
/// Identify all `ExprKind::Name` nodes in an AST.
struct NameFinder<'a> {
/// A map from identifier to defining expression.
names: FnvHashMap<&'a str, &'a Expr>,
names: FxHashMap<&'a str, &'a Expr>,
}
impl NameFinder<'_> {

View File

@@ -5,7 +5,6 @@ use crate::checks::{Check, CheckKind};
/// Check whether a function call is a `print` or `pprint` invocation
pub fn print_call(
expr: &Expr,
func: &Expr,
check_print: bool,
check_pprint: bool,
@@ -13,7 +12,7 @@ pub fn print_call(
) -> Option<Check> {
if let ExprKind::Name { id, .. } = &func.node {
if check_print && id == "print" {
return Some(Check::new(CheckKind::PrintFound, Range::from_located(expr)));
return Some(Check::new(CheckKind::PrintFound, location));
} else if check_pprint && id == "pprint" {
return Some(Check::new(CheckKind::PPrintFound, location));
}

View File

@@ -10,7 +10,6 @@ use crate::flake8_print::checks;
/// T201, T203
pub fn print_call(checker: &mut Checker, expr: &Expr, func: &Expr) {
if let Some(mut check) = checks::print_call(
expr,
func,
checker.settings.enabled.contains(&CheckCode::T201),
checker.settings.enabled.contains(&CheckCode::T203),

View File

@@ -5,9 +5,9 @@ use std::ops::Deref;
use std::path::{Path, PathBuf};
use anyhow::{anyhow, Result};
use fnv::FnvHashSet;
use log::debug;
use path_absolutize::{path_dedot, Absolutize};
use rustc_hash::FxHashSet;
use walkdir::{DirEntry, WalkDir};
use crate::checks::CheckCode;
@@ -121,7 +121,7 @@ pub fn iter_python_files<'a>(
pub(crate) fn ignores_from_path<'a>(
path: &Path,
pattern_code_pairs: &'a [PerFileIgnore],
) -> Result<FnvHashSet<&'a CheckCode>> {
) -> Result<FxHashSet<&'a CheckCode>> {
let (file_path, file_basename) = extract_path_names(path)?;
Ok(pattern_code_pairs
.iter()
@@ -203,7 +203,7 @@ mod tests {
let exclude = vec![FilePattern::from_user(
"foo",
Some(&project_root.to_path_buf()),
)];
)?];
let (file_path, file_basename) = extract_path_names(&path)?;
assert!(is_excluded(file_path, file_basename, exclude.iter()));
@@ -211,7 +211,7 @@ mod tests {
let exclude = vec![FilePattern::from_user(
"bar",
Some(&project_root.to_path_buf()),
)];
)?];
let (file_path, file_basename) = extract_path_names(&path)?;
assert!(is_excluded(file_path, file_basename, exclude.iter()));
@@ -221,7 +221,7 @@ mod tests {
let exclude = vec![FilePattern::from_user(
"baz.py",
Some(&project_root.to_path_buf()),
)];
)?];
let (file_path, file_basename) = extract_path_names(&path)?;
assert!(is_excluded(file_path, file_basename, exclude.iter()));
@@ -229,7 +229,7 @@ mod tests {
let exclude = vec![FilePattern::from_user(
"foo/bar",
Some(&project_root.to_path_buf()),
)];
)?];
let (file_path, file_basename) = extract_path_names(&path)?;
assert!(is_excluded(file_path, file_basename, exclude.iter()));
@@ -239,7 +239,7 @@ mod tests {
let exclude = vec![FilePattern::from_user(
"foo/bar/baz.py",
Some(&project_root.to_path_buf()),
)];
)?];
let (file_path, file_basename) = extract_path_names(&path)?;
assert!(is_excluded(file_path, file_basename, exclude.iter()));
@@ -249,7 +249,7 @@ mod tests {
let exclude = vec![FilePattern::from_user(
"foo/bar/*.py",
Some(&project_root.to_path_buf()),
)];
)?];
let (file_path, file_basename) = extract_path_names(&path)?;
assert!(is_excluded(file_path, file_basename, exclude.iter()));
@@ -259,7 +259,7 @@ mod tests {
let exclude = vec![FilePattern::from_user(
"baz",
Some(&project_root.to_path_buf()),
)];
)?];
let (file_path, file_basename) = extract_path_names(&path)?;
assert!(!is_excluded(file_path, file_basename, exclude.iter()));

View File

@@ -1,9 +1,10 @@
use std::cmp::Reverse;
use std::collections::{BTreeMap, BTreeSet};
use std::path::PathBuf;
use fnv::FnvHashMap;
use itertools::Itertools;
use ropey::RopeBuilder;
use rustc_hash::FxHashMap;
use rustpython_ast::{Stmt, StmtKind};
use crate::isort::categorize::{categorize, ImportType};
@@ -355,7 +356,7 @@ fn sort_imports(block: ImportBlock) -> OrderedImportBlock {
atop: comments.atop,
inline: Default::default(),
},
FnvHashMap::from_iter([(
FxHashMap::from_iter([(
alias,
CommentSet {
atop: Default::default(),
@@ -381,6 +382,7 @@ fn sort_imports(block: ImportBlock) -> OrderedImportBlock {
// Sort each `StmtKind::ImportFrom` by module key, breaking ties based on
// members.
(
Reverse(import_from.level),
import_from
.module
.as_ref()
@@ -477,6 +479,7 @@ mod tests {
#[test_case(Path::new("leading_prefix.py"))]
#[test_case(Path::new("no_reorder_within_section.py"))]
#[test_case(Path::new("order_by_type.py"))]
#[test_case(Path::new("order_relative_imports_by_level.py"))]
#[test_case(Path::new("preserve_comment_order.py"))]
#[test_case(Path::new("preserve_indentation.py"))]
#[test_case(Path::new("reorder_within_section.py"))]

View File

@@ -0,0 +1,22 @@
---
source: src/isort/mod.rs
expression: checks
---
- kind: UnsortedImports
location:
row: 1
column: 0
end_location:
row: 5
column: 0
fix:
patch:
content: "from ..a import a\nfrom ..b import a\nfrom .a import a\nfrom .b import a\n"
location:
row: 1
column: 0
end_location:
row: 5
column: 0
applied: false

View File

@@ -1,6 +1,6 @@
use std::borrow::Cow;
use fnv::FnvHashMap;
use rustc_hash::FxHashMap;
use crate::ast;
@@ -51,14 +51,14 @@ impl Importable for ImportFromData<'_> {
pub struct ImportBlock<'a> {
// Set of (name, asname), used to track regular imports.
// Ex) `import module`
pub import: FnvHashMap<AliasData<'a>, CommentSet<'a>>,
pub import: FxHashMap<AliasData<'a>, CommentSet<'a>>,
// Map from (module, level) to `AliasData`, used to track 'from' imports.
// Ex) `from module import member`
pub import_from:
FnvHashMap<ImportFromData<'a>, (CommentSet<'a>, FnvHashMap<AliasData<'a>, CommentSet<'a>>)>,
FxHashMap<ImportFromData<'a>, (CommentSet<'a>, FxHashMap<AliasData<'a>, CommentSet<'a>>)>,
// Set of (module, level, name, asname), used to track re-exported 'from' imports.
// Ex) `from module import member as member`
pub import_from_as: FnvHashMap<(ImportFromData<'a>, AliasData<'a>), CommentSet<'a>>,
pub import_from_as: FxHashMap<(ImportFromData<'a>, AliasData<'a>), CommentSet<'a>>,
}
type AliasDataWithComments<'a> = (AliasData<'a>, CommentSet<'a>);

View File

@@ -188,7 +188,6 @@ pub fn lint_stdin(
.collect())
}
#[cfg_attr(target_family = "wasm", allow(unused_variables))]
pub fn lint_path(
path: &Path,
settings: &Settings,
@@ -198,7 +197,6 @@ pub fn lint_path(
let metadata = path.metadata()?;
// Check the cache.
#[cfg(not(target_family = "wasm"))]
if let Some(messages) = cache::get(path, &metadata, settings, autofix, mode) {
debug!("Cache hit for: {}", path.to_string_lossy());
return Ok(messages);
@@ -251,7 +249,7 @@ pub fn lint_path(
Message::from_check(check, filename, source)
})
.collect();
#[cfg(not(target_family = "wasm"))]
cache::set(path, &metadata, settings, autofix, &messages, mode);
Ok(messages)
@@ -519,6 +517,7 @@ mod tests {
#[test_case(CheckCode::U011, Path::new("U011_1.py"); "U011_1")]
#[test_case(CheckCode::U012, Path::new("U012.py"); "U012")]
#[test_case(CheckCode::U013, Path::new("U013.py"); "U013")]
#[test_case(CheckCode::U014, Path::new("U014.py"); "U014")]
#[test_case(CheckCode::W292, Path::new("W292_0.py"); "W292_0")]
#[test_case(CheckCode::W292, Path::new("W292_1.py"); "W292_1")]
#[test_case(CheckCode::W292, Path::new("W292_2.py"); "W292_2")]
@@ -527,6 +526,13 @@ mod tests {
#[test_case(CheckCode::RUF001, Path::new("RUF001.py"); "RUF001")]
#[test_case(CheckCode::RUF002, Path::new("RUF002.py"); "RUF002")]
#[test_case(CheckCode::RUF003, Path::new("RUF003.py"); "RUF003")]
#[test_case(CheckCode::RUF101, Path::new("RUF101_0.py"); "RUF101_0")]
#[test_case(CheckCode::RUF101, Path::new("RUF101_1.py"); "RUF101_1")]
#[test_case(CheckCode::RUF101, Path::new("RUF101_2.py"); "RUF101_2")]
#[test_case(CheckCode::RUF101, Path::new("RUF101_3.py"); "RUF101_3")]
#[test_case(CheckCode::RUF101, Path::new("RUF101_4.py"); "RUF101_4")]
#[test_case(CheckCode::RUF101, Path::new("RUF101_5.py"); "RUF101_5")]
#[test_case(CheckCode::RUF101, Path::new("RUF101_6.py"); "RUF101_6")]
#[test_case(CheckCode::YTT101, Path::new("YTT101.py"); "YTT101")]
#[test_case(CheckCode::YTT102, Path::new("YTT102.py"); "YTT102")]
#[test_case(CheckCode::YTT103, Path::new("YTT103.py"); "YTT103")]

View File

@@ -4,7 +4,6 @@ use std::process::ExitCode;
use std::sync::mpsc::channel;
use std::time::Instant;
#[cfg(not(target_family = "wasm"))]
use ::ruff::cache;
use ::ruff::checks::{CheckCode, CheckKind};
use ::ruff::cli::{collect_per_file_ignores, extract_log_level, Cli};
@@ -220,12 +219,12 @@ fn inner_main() -> Result<ExitCode> {
.exclude
.iter()
.map(|path| FilePattern::from_user(path, project_root.as_ref()))
.collect();
.collect::<Result<_>>()?;
let extend_exclude: Vec<FilePattern> = cli
.extend_exclude
.iter()
.map(|path| FilePattern::from_user(path, project_root.as_ref()))
.collect();
.collect::<Result<_>>()?;
let mut configuration =
Configuration::from_pyproject(pyproject.as_ref(), project_root.as_ref())?;
@@ -237,7 +236,7 @@ fn inner_main() -> Result<ExitCode> {
}
if !cli.per_file_ignores.is_empty() {
configuration.per_file_ignores =
collect_per_file_ignores(cli.per_file_ignores, project_root.as_ref());
collect_per_file_ignores(cli.per_file_ignores, project_root.as_ref())?;
}
if !cli.select.is_empty() {
configuration.select = cli.select;
@@ -286,7 +285,7 @@ fn inner_main() -> Result<ExitCode> {
}
// Extract settings for internal use.
let autofix = configuration.fix;
let fix_enabled: bool = configuration.fix;
let settings = Settings::from_configuration(configuration);
if cli.show_files {
@@ -294,12 +293,16 @@ fn inner_main() -> Result<ExitCode> {
return Ok(ExitCode::SUCCESS);
}
#[cfg(not(target_family = "wasm"))]
cache::init()?;
// Initialize the cache.
let mut cache_enabled: bool = !cli.no_cache;
if cache_enabled && cache::init().is_err() {
eprintln!("Unable to initialize cache; disabling...");
cache_enabled = false;
}
let printer = Printer::new(&cli.format, &log_level);
if cli.watch {
if autofix {
if fix_enabled {
eprintln!("Warning: --fix is not enabled in watch mode.");
}
@@ -319,7 +322,7 @@ fn inner_main() -> Result<ExitCode> {
printer.clear_screen()?;
printer.write_to_user("Starting linter in watch mode...\n");
let messages = run_once(&cli.files, &settings, !cli.no_cache, false)?;
let messages = run_once(&cli.files, &settings, cache_enabled, false)?;
printer.write_continuously(&messages)?;
// Configure the file watcher.
@@ -337,7 +340,7 @@ fn inner_main() -> Result<ExitCode> {
printer.clear_screen()?;
printer.write_to_user("File change detected...\n");
let messages = run_once(&cli.files, &settings, !cli.no_cache, false)?;
let messages = run_once(&cli.files, &settings, cache_enabled, false)?;
printer.write_continuously(&messages)?;
}
}
@@ -362,15 +365,15 @@ fn inner_main() -> Result<ExitCode> {
let messages = if is_stdin {
let filename = cli.stdin_filename.unwrap_or_else(|| "-".to_string());
let path = Path::new(&filename);
run_once_stdin(&settings, path, autofix)?
run_once_stdin(&settings, path, fix_enabled)?
} else {
run_once(&cli.files, &settings, !cli.no_cache, autofix)?
run_once(&cli.files, &settings, cache_enabled, fix_enabled)?
};
// Always try to print violations (the printer itself may suppress output),
// unless we're writing fixes via stdin (in which case, the transformed
// source code goes to stdout).
if !(is_stdin && autofix) {
if !(is_stdin && fix_enabled) {
printer.write_once(&messages)?;
}

View File

@@ -46,7 +46,6 @@ fn get_complexity_number(stmts: &[Stmt]) -> usize {
complexity += get_complexity_number(body);
}
StmtKind::ClassDef { body, .. } => {
complexity += 1;
complexity += get_complexity_number(body);
}
_ => {}
@@ -71,3 +70,235 @@ pub fn function_is_too_complex(
None
}
}
#[cfg(test)]
mod tests {
use anyhow::Result;
use rustpython_parser::parser;
use crate::mccabe::checks::get_complexity_number;
#[test]
fn trivial() -> Result<()> {
let source = r#"
def trivial():
pass
"#;
let stmts = parser::parse_program(source, "<filename>")?;
assert_eq!(get_complexity_number(&stmts), 1);
Ok(())
}
#[test]
fn expr_as_statement() -> Result<()> {
let source = r#"
def expr_as_statement():
0xF00D
"#;
let stmts = parser::parse_program(source, "<filename>")?;
assert_eq!(get_complexity_number(&stmts), 1);
Ok(())
}
#[test]
fn sequential() -> Result<()> {
let source = r#"
def sequential(n):
k = n + 4
s = k + n
return s
"#;
let stmts = parser::parse_program(source, "<filename>")?;
assert_eq!(get_complexity_number(&stmts), 1);
Ok(())
}
#[test]
fn if_elif_else_dead_path() -> Result<()> {
let source = r#"
def if_elif_else_dead_path(n):
if n > 3:
return "bigger than three"
elif n > 4:
return "is never executed"
else:
return "smaller than or equal to three"
"#;
let stmts = parser::parse_program(source, "<filename>")?;
assert_eq!(get_complexity_number(&stmts), 3);
Ok(())
}
#[test]
fn nested_ifs() -> Result<()> {
let source = r#"
def nested_ifs():
if n > 3:
if n > 4:
return "bigger than four"
else:
return "bigger than three"
else:
return "smaller than or equal to three"
"#;
let stmts = parser::parse_program(source, "<filename>")?;
assert_eq!(get_complexity_number(&stmts), 3);
Ok(())
}
#[test]
fn for_loop() -> Result<()> {
let source = r#"
def for_loop():
for i in range(10):
print(i)
"#;
let stmts = parser::parse_program(source, "<filename>")?;
assert_eq!(get_complexity_number(&stmts), 2);
Ok(())
}
#[test]
fn for_else() -> Result<()> {
let source = r#"
def for_else(mylist):
for i in mylist:
print(i)
else:
print(None)
"#;
let stmts = parser::parse_program(source, "<filename>")?;
assert_eq!(get_complexity_number(&stmts), 2);
Ok(())
}
#[test]
fn recursive() -> Result<()> {
let source = r#"
def recursive(n):
if n > 4:
return f(n - 1)
else:
return n
"#;
let stmts = parser::parse_program(source, "<filename>")?;
assert_eq!(get_complexity_number(&stmts), 2);
Ok(())
}
#[test]
fn nested_functions() -> Result<()> {
let source = r#"
def nested_functions():
def a():
def b():
pass
b()
a()
"#;
let stmts = parser::parse_program(source, "<filename>")?;
assert_eq!(get_complexity_number(&stmts), 3);
Ok(())
}
#[test]
fn try_else() -> Result<()> {
let source = r#"
def try_else():
try:
print(1)
except TypeA:
print(2)
except TypeB:
print(3)
else:
print(4)
"#;
let stmts = parser::parse_program(source, "<filename>")?;
assert_eq!(get_complexity_number(&stmts), 4);
Ok(())
}
#[test]
fn nested_try_finally() -> Result<()> {
let source = r#"
def nested_try_finally():
try:
try:
print(1)
finally:
print(2)
finally:
print(3)
"#;
let stmts = parser::parse_program(source, "<filename>")?;
assert_eq!(get_complexity_number(&stmts), 3);
Ok(())
}
#[test]
fn foobar() -> Result<()> {
let source = r#"
async def foobar(a, b, c):
await whatever(a, b, c)
if await b:
pass
async with c:
pass
async for x in a:
pass
"#;
let stmts = parser::parse_program(source, "<filename>")?;
assert_eq!(get_complexity_number(&stmts), 3);
Ok(())
}
#[test]
fn annotated_assign() -> Result<()> {
let source = r#"
def annotated_assign():
x: Any = None
"#;
let stmts = parser::parse_program(source, "<filename>")?;
assert_eq!(get_complexity_number(&stmts), 1);
Ok(())
}
#[test]
fn class() -> Result<()> {
let source = r#"
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())
"#;
let stmts = parser::parse_program(source, "<filename>")?;
assert_eq!(get_complexity_number(&stmts), 9);
Ok(())
}
}

View File

@@ -164,7 +164,84 @@ expression: checks
row: 107
column: 0
end_location:
row: 109
row: 112
column: 0
fix: ~
- kind:
FunctionIsTooComplex:
- handle
- 9
location:
row: 113
column: 4
end_location:
row: 139
column: 0
fix: ~
- kind:
FunctionIsTooComplex:
- a
- 1
location:
row: 118
column: 12
end_location:
row: 121
column: 12
fix: ~
- kind:
FunctionIsTooComplex:
- b
- 2
location:
row: 121
column: 12
end_location:
row: 125
column: 8
fix: ~
- kind:
FunctionIsTooComplex:
- c
- 1
location:
row: 126
column: 12
end_location:
row: 129
column: 12
fix: ~
- kind:
FunctionIsTooComplex:
- error
- 1
location:
row: 129
column: 12
end_location:
row: 132
column: 12
fix: ~
- kind:
FunctionIsTooComplex:
- info
- 1
location:
row: 132
column: 12
end_location:
row: 135
column: 12
fix: ~
- kind:
FunctionIsTooComplex:
- exception
- 1
location:
row: 135
column: 12
end_location:
row: 138
column: 8
fix: ~

View File

@@ -13,4 +13,15 @@ expression: checks
row: 85
column: 0
fix: ~
- kind:
FunctionIsTooComplex:
- handle
- 9
location:
row: 113
column: 4
end_location:
row: 139
column: 0
fix: ~

View File

@@ -56,7 +56,7 @@ impl fmt::Display for Message {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let label = format!(
"{}{}{}{}{}{} {} {}",
relativize_path(Path::new(&self.filename)).white().bold(),
relativize_path(Path::new(&self.filename)).bold(),
":".cyan(),
self.location.row(),
":".cyan(),

View File

@@ -1,4 +1,4 @@
use fnv::{FnvHashMap, FnvHashSet};
use rustc_hash::{FxHashMap, FxHashSet};
use rustpython_ast::{Arguments, Expr, ExprKind, Stmt};
use crate::ast::types::{Range, Scope, ScopeKind};
@@ -59,8 +59,8 @@ pub fn invalid_first_argument_name_for_class_method(
name: &str,
decorator_list: &[Expr],
args: &Arguments,
from_imports: &FnvHashMap<&str, FnvHashSet<&str>>,
import_aliases: &FnvHashMap<&str, &str>,
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
import_aliases: &FxHashMap<&str, &str>,
settings: &Settings,
) -> Option<Check> {
if matches!(
@@ -74,7 +74,14 @@ pub fn invalid_first_argument_name_for_class_method(
),
FunctionType::ClassMethod
) {
if let Some(arg) = args.args.first() {
if let Some(arg) = args.posonlyargs.first() {
if arg.node.arg != "cls" {
return Some(Check::new(
CheckKind::InvalidFirstArgumentNameForClassMethod,
Range::from_located(arg),
));
}
} else if let Some(arg) = args.args.first() {
if arg.node.arg != "cls" {
return Some(Check::new(
CheckKind::InvalidFirstArgumentNameForClassMethod,
@@ -92,8 +99,8 @@ pub fn invalid_first_argument_name_for_method(
name: &str,
decorator_list: &[Expr],
args: &Arguments,
from_imports: &FnvHashMap<&str, FnvHashSet<&str>>,
import_aliases: &FnvHashMap<&str, &str>,
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
import_aliases: &FxHashMap<&str, &str>,
settings: &Settings,
) -> Option<Check> {
if matches!(
@@ -125,6 +132,11 @@ pub fn dunder_function_name(scope: &Scope, stmt: &Stmt, name: &str) -> Option<Ch
return None;
}
if name.starts_with("__") && name.ends_with("__") {
// Allowed under PEP 562 (https://peps.python.org/pep-0562/).
if matches!(scope.kind, ScopeKind::Module) && (name == "__getattr__" || name == "__dir__") {
return None;
}
return Some(Check::new(
CheckKind::DunderFunctionName,
Range::from_located(stmt),

View File

@@ -1,5 +1,5 @@
use fnv::{FnvHashMap, FnvHashSet};
use itertools::Itertools;
use rustc_hash::{FxHashMap, FxHashSet};
use rustpython_ast::{Expr, Stmt, StmtKind};
use crate::ast::helpers::{
@@ -24,8 +24,8 @@ pub fn function_type(
scope: &Scope,
name: &str,
decorator_list: &[Expr],
from_imports: &FnvHashMap<&str, FnvHashSet<&str>>,
import_aliases: &FnvHashMap<&str, &str>,
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
import_aliases: &FxHashMap<&str, &str>,
settings: &Settings,
) -> FunctionType {
if let ScopeKind::Class(scope) = &scope.kind {
@@ -87,7 +87,7 @@ pub fn is_acronym(name: &str, asname: &str) -> bool {
pub fn is_namedtuple_assignment(
stmt: &Stmt,
from_imports: &FnvHashMap<&str, FnvHashSet<&str>>,
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
) -> bool {
if let StmtKind::Assign { value, .. } = &stmt.node {
match_call_path(

View File

@@ -1,7 +1,7 @@
use anyhow::Result;
use fnv::FnvHashMap;
use itertools::izip;
use log::error;
use rustc_hash::FxHashMap;
use rustpython_ast::{Arguments, Location, StmtKind};
use rustpython_parser::ast::{Cmpop, Constant, Expr, ExprKind, Stmt, Unaryop};
@@ -46,7 +46,7 @@ pub fn literal_comparisons(
// through the list of operators, we apply "dummy" fixes for each error,
// then replace the entire expression at the end with one "real" fix, to
// avoid conflicts.
let mut bad_ops: FnvHashMap<usize, Cmpop> = FnvHashMap::default();
let mut bad_ops: FxHashMap<usize, Cmpop> = FxHashMap::default();
let mut checks: Vec<Check> = vec![];
let op = ops.first().unwrap();

View File

@@ -1,9 +1,9 @@
use std::collections::BTreeSet;
use fnv::FnvHashSet;
use itertools::Itertools;
use once_cell::sync::Lazy;
use regex::Regex;
use rustc_hash::FxHashSet;
use rustpython_ast::{Arg, Constant, ExprKind, Location, StmtKind};
use crate::ast::types::Range;
@@ -1292,11 +1292,7 @@ fn common_section(
blanks_and_section_underline(checker, definition, context);
}
fn missing_args(
checker: &mut Checker,
definition: &Definition,
docstrings_args: &FnvHashSet<&str>,
) {
fn missing_args(checker: &mut Checker, definition: &Definition, docstrings_args: &FxHashSet<&str>) {
if let DefinitionKind::Function(parent)
| DefinitionKind::NestedFunction(parent)
| DefinitionKind::Method(parent) = definition.kind
@@ -1389,7 +1385,7 @@ fn args_section(checker: &mut Checker, definition: &Definition, context: &Sectio
checker,
definition,
// Collect the list of arguments documented in the docstring.
&FnvHashSet::from_iter(args_sections.iter().filter_map(|section| {
&FxHashSet::from_iter(args_sections.iter().filter_map(|section| {
match GOOGLE_ARGS_REGEX.captures(section.as_str()) {
Some(caps) => caps.get(1).map(|arg_name| arg_name.as_str()),
None => None,
@@ -1400,7 +1396,7 @@ fn args_section(checker: &mut Checker, definition: &Definition, context: &Sectio
fn parameters_section(checker: &mut Checker, definition: &Definition, context: &SectionContext) {
// Collect the list of arguments documented in the docstring.
let mut docstring_args: FnvHashSet<&str> = FnvHashSet::default();
let mut docstring_args: FxHashSet<&str> = FxHashSet::default();
let section_level_indent = whitespace::leading_space(context.line);
for i in 1..context.following_lines.len() {
let current_line = context.following_lines[i - 1];

View File

@@ -1,5 +1,5 @@
use fnv::FnvHashSet;
use regex::Regex;
use rustc_hash::FxHashSet;
use rustpython_parser::ast::{
Arg, Arguments, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprKind, Stmt, StmtKind,
};
@@ -115,7 +115,7 @@ pub fn duplicate_arguments(arguments: &Arguments) -> Vec<Check> {
}
// Search for duplicates.
let mut idents: FnvHashSet<&str> = FnvHashSet::default();
let mut idents: FxHashSet<&str> = FxHashSet::default();
for arg in all_arguments {
let ident = &arg.node.arg;
if idents.contains(ident.as_str()) {

View File

@@ -1,9 +1,9 @@
use fnv::FnvHashSet;
use once_cell::sync::Lazy;
use rustc_hash::FxHashSet;
// See: https://pycqa.github.io/isort/docs/configuration/options.html#known-standard-library
pub static KNOWN_STANDARD_LIBRARY: Lazy<FnvHashSet<&'static str>> = Lazy::new(|| {
FnvHashSet::from_iter([
pub static KNOWN_STANDARD_LIBRARY: Lazy<FxHashSet<&'static str>> = Lazy::new(|| {
FxHashSet::from_iter([
"_ast",
"_dummy_thread",
"_thread",

View File

@@ -1,12 +1,12 @@
use fnv::{FnvHashMap, FnvHashSet};
use once_cell::sync::Lazy;
use rustc_hash::{FxHashMap, FxHashSet};
use rustpython_ast::Expr;
use crate::ast::helpers::{collect_call_paths, dealias_call_path, match_call_path};
// See: https://pypi.org/project/typing-extensions/
static TYPING_EXTENSIONS: Lazy<FnvHashSet<&'static str>> = Lazy::new(|| {
FnvHashSet::from_iter([
static TYPING_EXTENSIONS: Lazy<FxHashSet<&'static str>> = Lazy::new(|| {
FxHashSet::from_iter([
"Annotated",
"Any",
"AsyncContextManager",
@@ -211,8 +211,8 @@ pub enum SubscriptKind {
pub fn match_annotated_subscript(
expr: &Expr,
from_imports: &FnvHashMap<&str, FnvHashSet<&str>>,
import_aliases: &FnvHashMap<&str, &str>,
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
import_aliases: &FxHashMap<&str, &str>,
) -> Option<SubscriptKind> {
let call_path = dealias_call_path(collect_call_paths(expr), import_aliases);
if !call_path.is_empty() {
@@ -234,8 +234,8 @@ pub fn match_annotated_subscript(
/// PEP 585 built-in.
pub fn is_pep585_builtin(
expr: &Expr,
from_imports: &FnvHashMap<&str, FnvHashSet<&str>>,
import_aliases: &FnvHashMap<&str, &str>,
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
import_aliases: &FxHashMap<&str, &str>,
) -> bool {
let call_path = dealias_call_path(collect_call_paths(expr), import_aliases);
if !call_path.is_empty() {

View File

@@ -1,4 +1,4 @@
use fnv::{FnvHashMap, FnvHashSet};
use rustc_hash::{FxHashMap, FxHashSet};
use rustpython_ast::{Constant, KeywordData};
use rustpython_parser::ast::{ArgData, Expr, ExprKind, Stmt, StmtKind};
@@ -143,8 +143,8 @@ pub fn type_of_primitive(func: &Expr, args: &[Expr], location: Range) -> Option<
pub fn unnecessary_lru_cache_params(
decorator_list: &[Expr],
target_version: PythonVersion,
from_imports: &FnvHashMap<&str, FnvHashSet<&str>>,
import_aliases: &FnvHashMap<&str, &str>,
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
import_aliases: &FxHashMap<&str, &str>,
) -> Option<Check> {
for expr in decorator_list.iter() {
if let ExprKind::Call {
@@ -162,12 +162,13 @@ pub fn unnecessary_lru_cache_params(
import_aliases,
)
{
let range = Range {
location: func.end_location.unwrap(),
end_location: expr.end_location.unwrap(),
};
// Ex) `functools.lru_cache()`
if keywords.is_empty() {
return Some(Check::new(
CheckKind::UnnecessaryLRUCacheParams,
Range::from_located(expr),
));
return Some(Check::new(CheckKind::UnnecessaryLRUCacheParams, range));
}
// Ex) `functools.lru_cache(maxsize=None)`
if target_version >= PythonVersion::Py39 && keywords.len() == 1 {
@@ -181,10 +182,7 @@ pub fn unnecessary_lru_cache_params(
}
)
{
return Some(Check::new(
CheckKind::UnnecessaryLRUCacheParams,
Range::from_located(expr),
));
return Some(Check::new(CheckKind::UnnecessaryLRUCacheParams, range));
}
}
}

View File

@@ -191,34 +191,3 @@ pub fn remove_unnecessary_future_import(
))
}
}
/// U011
pub fn remove_unnecessary_lru_cache_params(
locator: &SourceCodeLocator,
decor_at: &Location,
) -> Option<Fix> {
let contents = locator.slice_source_code_at(decor_at);
let mut fix_start = None;
let mut fix_end = None;
let mut count: usize = 0;
for (start, tok, end) in lexer::make_tokenizer(&contents).flatten() {
if matches!(tok, Tok::Lpar) {
if count == 0 {
fix_start = Some(helpers::to_absolute(&start, decor_at));
}
count += 1;
}
if matches!(tok, Tok::Rpar) {
count -= 1;
if count == 0 {
fix_end = Some(helpers::to_absolute(&end, decor_at));
break;
}
}
}
match (fix_start, fix_end) {
(Some(start), Some(end)) => Some(Fix::deletion(start, end)),
_ => None,
}
}

View File

@@ -0,0 +1,208 @@
use anyhow::{bail, Result};
use log::error;
use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Keyword, Stmt, StmtKind};
use crate::ast::helpers::match_module_member;
use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::check_ast::Checker;
use crate::checks::{Check, CheckKind};
use crate::code_gen::SourceGenerator;
use crate::python::identifiers::IDENTIFIER_REGEX;
use crate::python::keyword::KWLIST;
/// Return the typename, args, keywords and mother class
fn match_named_tuple_assign<'a>(
checker: &Checker,
targets: &'a [Expr],
value: &'a Expr,
) -> Option<(&'a str, &'a [Expr], &'a [Keyword], &'a ExprKind)> {
if let Some(target) = targets.get(0) {
if let ExprKind::Name { id: typename, .. } = &target.node {
if let ExprKind::Call {
func,
args,
keywords,
} = &value.node
{
if match_module_member(
func,
"typing",
"NamedTuple",
&checker.from_imports,
&checker.import_aliases,
) {
return Some((typename, args, keywords, &func.node));
}
}
}
}
None
}
/// Generate a `StmtKind::AnnAssign` representing the provided property
/// definition.
fn create_property_assignment_stmt(
property: &str,
annotation: &ExprKind,
value: Option<&ExprKind>,
) -> Stmt {
Stmt::new(
Default::default(),
Default::default(),
StmtKind::AnnAssign {
target: Box::new(Expr::new(
Default::default(),
Default::default(),
ExprKind::Name {
id: property.to_string(),
ctx: ExprContext::Load,
},
)),
annotation: Box::new(Expr::new(
Default::default(),
Default::default(),
annotation.clone(),
)),
value: value
.map(|v| Box::new(Expr::new(Default::default(), Default::default(), v.clone()))),
simple: 1,
},
)
}
/// Match the `defaults` keyword in a `NamedTuple(...)` call.
fn match_defaults(keywords: &[Keyword]) -> Result<&[Expr]> {
match keywords.iter().find(|keyword| {
if let Some(arg) = &keyword.node.arg {
arg.as_str() == "defaults"
} else {
false
}
}) {
Some(defaults) => match &defaults.node.value.node {
ExprKind::List { elts, .. } => Ok(elts),
ExprKind::Tuple { elts, .. } => Ok(elts),
_ => bail!("Expected defaults to be `ExprKind::List` | `ExprKind::Tuple`"),
},
None => Ok(&[]),
}
}
/// Create a list of property assignments from the `NamedTuple` arguments.
fn create_properties_from_args(args: &[Expr], defaults: &[Expr]) -> Result<Vec<Stmt>> {
if let Some(fields) = args.get(1) {
if let ExprKind::List { elts, .. } = &fields.node {
let padded_defaults = if elts.len() >= defaults.len() {
std::iter::repeat(None)
.take(elts.len() - defaults.len())
.chain(defaults.iter().map(Some))
} else {
bail!("Defaults must be `None` or an iterable of at least the number of fields")
};
elts.iter()
.zip(padded_defaults)
.map(|(field, default)| {
if let ExprKind::Tuple { elts, .. } = &field.node {
if let [field_name, annotation] = elts.as_slice() {
if let ExprKind::Constant {
value: Constant::Str(property),
..
} = &field_name.node
{
if IDENTIFIER_REGEX.is_match(property)
&& !KWLIST.contains(&property.as_str())
{
Ok(create_property_assignment_stmt(
property,
&annotation.node,
default.map(|d| &d.node),
))
} else {
bail!("Invalid property name: {}", property)
}
} else {
bail!("Expected `field_name` to be `Constant::Str`")
}
} else {
bail!("Expected `elts` to have exactly two elements")
}
} else {
bail!("Expected `field` to be `ExprKind::Tuple`")
}
})
.collect()
} else {
bail!("Expected argument to be `ExprKind::List`")
}
} else {
Ok(vec![])
}
}
/// Generate a `StmtKind:ClassDef` statement based on the provided body and
/// keywords.
fn create_class_def_stmt(typename: &str, body: Vec<Stmt>, base_class: &ExprKind) -> Stmt {
Stmt::new(
Default::default(),
Default::default(),
StmtKind::ClassDef {
name: typename.to_string(),
bases: vec![Expr::new(
Default::default(),
Default::default(),
base_class.clone(),
)],
keywords: vec![],
body,
decorator_list: vec![],
},
)
}
fn convert_to_class(
stmt: &Stmt,
typename: &str,
body: Vec<Stmt>,
base_class: &ExprKind,
) -> Result<Fix> {
let mut generator = SourceGenerator::new();
generator.unparse_stmt(&create_class_def_stmt(typename, body, base_class))?;
let content = generator.generate()?;
Ok(Fix::replacement(
content,
stmt.location,
stmt.end_location.unwrap(),
))
}
/// U014
pub fn convert_named_tuple_functional_to_class(
checker: &mut Checker,
stmt: &Stmt,
targets: &[Expr],
value: &Expr,
) {
if let Some((typename, args, keywords, base_class)) =
match_named_tuple_assign(checker, targets, value)
{
match match_defaults(keywords) {
Ok(defaults) => {
if let Ok(properties) = create_properties_from_args(args, defaults) {
let mut check = Check::new(
CheckKind::ConvertNamedTupleFunctionalToClass(typename.to_string()),
Range::from_located(stmt),
);
if checker.patch(check.kind.code()) {
match convert_to_class(stmt, typename, properties, base_class) {
Ok(fix) => check.amend(fix),
Err(err) => error!("Failed to convert `NamedTuple`: {}", err),
}
}
checker.add_check(check);
}
}
Err(err) => error!("Failed to parse defaults: {}", err),
}
}
}

View File

@@ -230,7 +230,7 @@ pub fn convert_typed_dict_functional_to_class(
Err(err) => error!("Failed to parse TypedDict: {}", err),
Ok((body, total_keyword)) => {
let mut check = Check::new(
CheckKind::ConvertTypedDictFunctionalToClass,
CheckKind::ConvertTypedDictFunctionalToClass(class_name.to_string()),
Range::from_located(stmt),
);
if checker.patch(check.kind.code()) {

View File

@@ -1,5 +1,5 @@
use fnv::FnvHashMap;
use once_cell::sync::Lazy;
use rustc_hash::FxHashMap;
use rustpython_ast::{Expr, ExprKind};
use crate::ast::types::Range;
@@ -7,8 +7,8 @@ use crate::autofix::Fix;
use crate::check_ast::Checker;
use crate::checks::{Check, CheckKind};
static DEPRECATED_ALIASES: Lazy<FnvHashMap<&'static str, &'static str>> = Lazy::new(|| {
FnvHashMap::from_iter([
static DEPRECATED_ALIASES: Lazy<FxHashMap<&'static str, &'static str>> = Lazy::new(|| {
FxHashMap::from_iter([
("failUnlessEqual", "assertEqual"),
("assertEquals", "assertEqual"),
("failIfEqual", "assertNotEqual"),

View File

@@ -1,3 +1,4 @@
pub use convert_named_tuple_functional_to_class::convert_named_tuple_functional_to_class;
pub use convert_typed_dict_functional_to_class::convert_typed_dict_functional_to_class;
pub use deprecated_unittest_alias::deprecated_unittest_alias;
pub use super_call_with_parameters::super_call_with_parameters;
@@ -10,6 +11,7 @@ pub use use_pep604_annotation::use_pep604_annotation;
pub use useless_metaclass_type::useless_metaclass_type;
pub use useless_object_inheritance::useless_object_inheritance;
mod convert_named_tuple_functional_to_class;
mod convert_typed_dict_functional_to_class;
mod deprecated_unittest_alias;
mod super_call_with_parameters;

View File

@@ -1,7 +1,8 @@
use rustpython_parser::ast::Expr;
use crate::autofix::Fix;
use crate::check_ast::Checker;
use crate::pyupgrade::{checks, fixes};
use crate::pyupgrade::checks;
/// U011
pub fn unnecessary_lru_cache_params(checker: &mut Checker, decorator_list: &[Expr]) {
@@ -12,11 +13,7 @@ pub fn unnecessary_lru_cache_params(checker: &mut Checker, decorator_list: &[Exp
&checker.import_aliases,
) {
if checker.patch(check.kind.code()) {
if let Some(fix) =
fixes::remove_unnecessary_lru_cache_params(checker.locator, &check.location)
{
check.amend(fix);
}
check.amend(Fix::deletion(check.location, check.end_location));
}
checker.add_check(check);
}

View File

@@ -42,8 +42,25 @@ fn union(elts: &[Expr]) -> Expr {
}
}
/// Returns `true` if any argument in the slice is a string.
fn any_arg_is_str(slice: &Expr) -> bool {
match &slice.node {
ExprKind::Constant {
value: Constant::Str(_),
..
} => true,
ExprKind::Tuple { elts, .. } => elts.iter().any(any_arg_is_str),
_ => false,
}
}
/// U007
pub fn use_pep604_annotation(checker: &mut Checker, expr: &Expr, value: &Expr, slice: &Expr) {
// Avoid rewriting forward annotations.
if any_arg_is_str(slice) {
return;
}
let call_path = dealias_call_path(collect_call_paths(value), &checker.import_aliases);
if checker.match_typing_call_path(&call_path, "Optional") {
let mut check = Check::new(CheckKind::UsePEP604Annotation, Range::from_located(expr));

View File

@@ -1,5 +1,5 @@
use fnv::FnvHashMap;
use once_cell::sync::Lazy;
use rustc_hash::FxHashMap;
use rustpython_ast::Location;
use crate::ast::types::Range;
@@ -9,8 +9,8 @@ use crate::source_code_locator::SourceCodeLocator;
use crate::{Check, Settings};
/// See: https://github.com/microsoft/vscode/blob/095ddabc52b82498ee7f718a34f9dd11d59099a8/src/vs/base/common/strings.ts#L1094
static CONFUSABLES: Lazy<FnvHashMap<u32, u32>> = Lazy::new(|| {
FnvHashMap::from_iter([
static CONFUSABLES: Lazy<FxHashMap<u32, u32>> = Lazy::new(|| {
FxHashMap::from_iter([
(8232, 32),
(8233, 32),
(5760, 32),

View File

@@ -1,3 +1,4 @@
//! Module for Ruff-specific rules.
pub mod checks;
pub mod plugins;

View File

@@ -0,0 +1,92 @@
use rustpython_ast::{Expr, ExprKind};
use crate::ast::types::{BindingKind, Range};
use crate::autofix::Fix;
use crate::check_ast::Checker;
use crate::checks::{Check, CheckKind};
/// Return `true` if the `module` was imported using a star import (e.g., `from
/// sys import *`).
fn is_module_star_imported(checker: &Checker, module: &str) -> bool {
checker.current_scopes().any(|scope| {
scope.values.values().any(|binding| {
if let BindingKind::StarImportation(_, name) = &binding.kind {
name.as_ref().map(|name| name == module).unwrap_or_default()
} else {
false
}
})
})
}
/// Return `true` if `exit` is (still) bound as a built-in in the current scope.
fn has_builtin_exit_in_scope(checker: &Checker) -> bool {
!is_module_star_imported(checker, "sys")
&& checker
.current_scopes()
.find_map(|scope| scope.values.get("exit"))
.map(|binding| matches!(binding.kind, BindingKind::Builtin))
.unwrap_or_default()
}
/// Return the appropriate `sys.exit` reference based on the current set of
/// imports, or `None` is `sys.exit` hasn't been imported.
fn get_member_import_name_alias(checker: &Checker, module: &str, member: &str) -> Option<String> {
checker.current_scopes().find_map(|scope| {
scope
.values
.values()
.find_map(|binding| match &binding.kind {
// e.g. module=sys object=exit
// `import sys` -> `sys.exit`
// `import sys as sys2` -> `sys2.exit`
BindingKind::Importation(name, full_name, _) if full_name == module => {
Some(format!("{}.{}", name, member))
}
// e.g. module=os.path object=join
// `from os.path import join` -> `join`
// `from os.path import join as join2` -> `join2`
BindingKind::FromImportation(name, full_name, _)
if full_name == &format!("{}.{}", module, member) =>
{
Some(name.to_string())
}
// e.g. module=os.path object=join
// `from os.path import *` -> `join`
BindingKind::StarImportation(_, name)
if name.as_ref().map(|name| name == module).unwrap_or_default() =>
{
Some(member.to_string())
}
// e.g. module=os.path object=join
// `import os.path ` -> `os.path.join`
BindingKind::SubmoduleImportation(_, full_name, _) if full_name == module => {
Some(format!("{}.{}", full_name, member))
}
// Non-imports.
_ => None,
})
})
}
/// RUF101
pub fn convert_exit_to_sys_exit(checker: &mut Checker, func: &Expr) {
if let ExprKind::Name { id, .. } = &func.node {
if id == "exit" {
if has_builtin_exit_in_scope(checker) {
let mut check =
Check::new(CheckKind::ConvertExitToSysExit, Range::from_located(func));
if checker.patch(check.kind.code()) {
if let Some(content) = get_member_import_name_alias(checker, "sys", "exit") {
check.amend(Fix::replacement(
content,
func.location,
func.end_location.unwrap(),
))
}
}
checker.add_check(check);
}
}
}
}

3
src/rules/plugins/mod.rs Normal file
View File

@@ -0,0 +1,3 @@
pub use convert_exit_to_sys_exit::convert_exit_to_sys_exit;
mod convert_exit_to_sys_exit;

View File

@@ -111,13 +111,14 @@ impl Configuration {
.map(|path| FilePattern::from_user(path, project_root))
.collect()
})
.transpose()?
.unwrap_or_else(|| DEFAULT_EXCLUDE.clone()),
extend_exclude: options
.extend_exclude
.unwrap_or_default()
.iter()
.map(|path| FilePattern::from_user(path, project_root))
.collect(),
.collect::<Result<_>>()?,
extend_ignore: options.extend_ignore.unwrap_or_default(),
select: options
.select
@@ -138,6 +139,7 @@ impl Configuration {
CheckCodePrefix::M,
CheckCodePrefix::N,
CheckCodePrefix::Q,
CheckCodePrefix::RUF,
CheckCodePrefix::S,
CheckCodePrefix::T,
CheckCodePrefix::U,
@@ -158,6 +160,7 @@ impl Configuration {
})
.collect()
})
.transpose()?
.unwrap_or_default(),
show_source: options.show_source.unwrap_or_default(),
// Plugins

View File

@@ -5,9 +5,9 @@
use std::hash::{Hash, Hasher};
use std::path::PathBuf;
use fnv::FnvHashSet;
use path_absolutize::path_dedot;
use regex::Regex;
use rustc_hash::FxHashSet;
use crate::checks::CheckCode;
use crate::checks_gen::{CheckCodePrefix, PrefixSpecificity};
@@ -27,10 +27,10 @@ pub mod user;
#[derive(Debug)]
pub struct Settings {
pub dummy_variable_rgx: Regex,
pub enabled: FnvHashSet<CheckCode>,
pub enabled: FxHashSet<CheckCode>,
pub exclude: Vec<FilePattern>,
pub extend_exclude: Vec<FilePattern>,
pub fixable: FnvHashSet<CheckCode>,
pub fixable: FxHashSet<CheckCode>,
pub line_length: usize,
pub per_file_ignores: Vec<PerFileIgnore>,
pub show_source: bool,
@@ -83,8 +83,8 @@ impl Settings {
pub fn for_rule(check_code: CheckCode) -> Self {
Self {
dummy_variable_rgx: Regex::new("^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$").unwrap(),
enabled: FnvHashSet::from_iter([check_code.clone()]),
fixable: FnvHashSet::from_iter([check_code]),
enabled: FxHashSet::from_iter([check_code.clone()]),
fixable: FxHashSet::from_iter([check_code]),
exclude: Default::default(),
extend_exclude: Default::default(),
line_length: 88,
@@ -105,8 +105,8 @@ impl Settings {
pub fn for_rules(check_codes: Vec<CheckCode>) -> Self {
Self {
dummy_variable_rgx: Regex::new("^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$").unwrap(),
enabled: FnvHashSet::from_iter(check_codes.clone()),
fixable: FnvHashSet::from_iter(check_codes),
enabled: FxHashSet::from_iter(check_codes.clone()),
fixable: FxHashSet::from_iter(check_codes),
exclude: Default::default(),
extend_exclude: Default::default(),
line_length: 88,
@@ -154,8 +154,8 @@ impl Hash for Settings {
/// Given a set of selected and ignored prefixes, resolve the set of enabled
/// error codes.
fn resolve_codes(select: &[CheckCodePrefix], ignore: &[CheckCodePrefix]) -> FnvHashSet<CheckCode> {
let mut codes: FnvHashSet<CheckCode> = FnvHashSet::default();
fn resolve_codes(select: &[CheckCodePrefix], ignore: &[CheckCodePrefix]) -> FxHashSet<CheckCode> {
let mut codes: FxHashSet<CheckCode> = FxHashSet::default();
for specificity in [
PrefixSpecificity::Category,
PrefixSpecificity::Hundreds,
@@ -180,7 +180,7 @@ fn resolve_codes(select: &[CheckCodePrefix], ignore: &[CheckCodePrefix]) -> FnvH
#[cfg(test)]
mod tests {
use fnv::FnvHashSet;
use rustc_hash::FxHashSet;
use crate::checks::CheckCode;
use crate::checks_gen::CheckCodePrefix;
@@ -189,19 +189,19 @@ mod tests {
#[test]
fn resolver() {
let actual = resolve_codes(&[CheckCodePrefix::W], &[]);
let expected = FnvHashSet::from_iter([CheckCode::W292, CheckCode::W605]);
let expected = FxHashSet::from_iter([CheckCode::W292, CheckCode::W605]);
assert_eq!(actual, expected);
let actual = resolve_codes(&[CheckCodePrefix::W6], &[]);
let expected = FnvHashSet::from_iter([CheckCode::W605]);
let expected = FxHashSet::from_iter([CheckCode::W605]);
assert_eq!(actual, expected);
let actual = resolve_codes(&[CheckCodePrefix::W], &[CheckCodePrefix::W292]);
let expected = FnvHashSet::from_iter([CheckCode::W605]);
let expected = FxHashSet::from_iter([CheckCode::W605]);
assert_eq!(actual, expected);
let actual = resolve_codes(&[CheckCodePrefix::W605], &[CheckCodePrefix::W605]);
let expected = FnvHashSet::from_iter([]);
let expected = FxHashSet::from_iter([]);
assert_eq!(actual, expected);
}
}

View File

@@ -1,6 +1,6 @@
//! Options that the user can provide via pyproject.toml.
use fnv::FnvHashMap;
use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};
use crate::checks_gen::CheckCodePrefix;
@@ -36,5 +36,5 @@ pub struct Options {
pub mccabe: Option<mccabe::settings::Options>,
pub pep8_naming: Option<pep8_naming::settings::Options>,
// Tables are required to go last.
pub per_file_ignores: Option<FnvHashMap<String, Vec<CheckCodePrefix>>>,
pub per_file_ignores: Option<FxHashMap<String, Vec<CheckCodePrefix>>>,
}

View File

@@ -101,7 +101,7 @@ mod tests {
use std::str::FromStr;
use anyhow::Result;
use fnv::FnvHashMap;
use rustc_hash::FxHashMap;
use crate::checks_gen::CheckCodePrefix;
use crate::flake8_quotes::settings::Quote;
@@ -374,7 +374,7 @@ other-attribute = 1
extend_ignore: None,
fixable: None,
unfixable: None,
per_file_ignores: Some(FnvHashMap::from_iter([(
per_file_ignores: Some(FxHashMap::from_iter([(
"__init__.py".to_string(),
vec![CheckCodePrefix::F401]
),])),

View File

@@ -51,21 +51,21 @@ pub enum FilePattern {
}
impl FilePattern {
pub fn from_user(pattern: &str, project_root: Option<&PathBuf>) -> Self {
pub fn from_user(pattern: &str, project_root: Option<&PathBuf>) -> Result<Self> {
let path = Path::new(pattern);
let absolute_path = match project_root {
Some(project_root) => fs::normalize_path_to(path, project_root),
None => fs::normalize_path(path),
};
let absolute = Pattern::new(&absolute_path.to_string_lossy()).expect("Invalid pattern.");
let absolute = Pattern::new(&absolute_path.to_string_lossy())?;
let basename = if !pattern.contains(std::path::MAIN_SEPARATOR) {
Some(Pattern::new(pattern).expect("Invalid pattern."))
Some(Pattern::new(pattern)?)
} else {
None
};
FilePattern::Complex(absolute, basename)
Ok(FilePattern::Complex(absolute, basename))
}
}
@@ -80,10 +80,10 @@ impl PerFileIgnore {
pattern: &str,
prefixes: &[CheckCodePrefix],
project_root: Option<&PathBuf>,
) -> Self {
let pattern = FilePattern::from_user(pattern, project_root);
) -> Result<Self> {
let pattern = FilePattern::from_user(pattern, project_root)?;
let codes = BTreeSet::from_iter(prefixes.iter().flat_map(|prefix| prefix.codes()));
Self { pattern, codes }
Ok(Self { pattern, codes })
}
}

View File

@@ -9,7 +9,16 @@ expression: checks
end_location:
row: 33
column: 25
fix: ~
fix:
patch:
content: foo.bar = None
location:
row: 33
column: 0
end_location:
row: 33
column: 25
applied: false
- kind: SetAttrWithConstant
location:
row: 34
@@ -17,7 +26,16 @@ expression: checks
end_location:
row: 34
column: 29
fix: ~
fix:
patch:
content: foo._123abc = None
location:
row: 34
column: 0
end_location:
row: 34
column: 29
applied: false
- kind: SetAttrWithConstant
location:
row: 35
@@ -25,7 +43,16 @@ expression: checks
end_location:
row: 35
column: 28
fix: ~
fix:
patch:
content: foo.abc123 = None
location:
row: 35
column: 0
end_location:
row: 35
column: 28
applied: false
- kind: SetAttrWithConstant
location:
row: 36
@@ -33,5 +60,31 @@ expression: checks
end_location:
row: 36
column: 29
fix: ~
fix:
patch:
content: foo.abc123 = None
location:
row: 36
column: 0
end_location:
row: 36
column: 29
applied: false
- kind: SetAttrWithConstant
location:
row: 37
column: 0
end_location:
row: 37
column: 30
fix:
patch:
content: foo.bar.baz = None
location:
row: 37
column: 0
end_location:
row: 37
column: 30
applied: false

View File

@@ -10,5 +10,14 @@ expression: checks
end_location:
row: 3
column: 19
fix: ~
fix:
patch:
content: ValueError
location:
row: 3
column: 7
end_location:
row: 3
column: 20
applied: false

View File

@@ -12,10 +12,18 @@ expression: checks
fix: ~
- kind: InvalidFirstArgumentNameForClassMethod
location:
row: 35
row: 38
column: 55
end_location:
row: 38
column: 59
fix: ~
- kind: InvalidFirstArgumentNameForClassMethod
location:
row: 43
column: 19
end_location:
row: 35
row: 43
column: 23
fix: ~

View File

@@ -10,4 +10,12 @@ expression: checks
row: 5
column: 0
fix: ~
- kind: DunderFunctionName
location:
row: 14
column: 4
end_location:
row: 17
column: 4
fix: ~

View File

@@ -0,0 +1,21 @@
---
source: src/linter.rs
expression: checks
---
- kind: ConvertExitToSysExit
location:
row: 1
column: 0
end_location:
row: 1
column: 4
fix: ~
- kind: ConvertExitToSysExit
location:
row: 5
column: 4
end_location:
row: 5
column: 8
fix: ~

View File

@@ -0,0 +1,39 @@
---
source: src/linter.rs
expression: checks
---
- kind: ConvertExitToSysExit
location:
row: 3
column: 0
end_location:
row: 3
column: 4
fix:
patch:
content: sys.exit
location:
row: 3
column: 0
end_location:
row: 3
column: 4
applied: false
- kind: ConvertExitToSysExit
location:
row: 7
column: 4
end_location:
row: 7
column: 8
fix:
patch:
content: sys.exit
location:
row: 7
column: 4
end_location:
row: 7
column: 8
applied: false

View File

@@ -0,0 +1,39 @@
---
source: src/linter.rs
expression: checks
---
- kind: ConvertExitToSysExit
location:
row: 3
column: 0
end_location:
row: 3
column: 4
fix:
patch:
content: sys2.exit
location:
row: 3
column: 0
end_location:
row: 3
column: 4
applied: false
- kind: ConvertExitToSysExit
location:
row: 7
column: 4
end_location:
row: 7
column: 8
fix:
patch:
content: sys2.exit
location:
row: 7
column: 4
end_location:
row: 7
column: 8
applied: false

View File

@@ -0,0 +1,6 @@
---
source: src/linter.rs
expression: checks
---
[]

View File

@@ -0,0 +1,39 @@
---
source: src/linter.rs
expression: checks
---
- kind: ConvertExitToSysExit
location:
row: 3
column: 0
end_location:
row: 3
column: 4
fix:
patch:
content: exit2
location:
row: 3
column: 0
end_location:
row: 3
column: 4
applied: false
- kind: ConvertExitToSysExit
location:
row: 7
column: 4
end_location:
row: 7
column: 8
fix:
patch:
content: exit2
location:
row: 7
column: 4
end_location:
row: 7
column: 8
applied: false

View File

@@ -0,0 +1,6 @@
---
source: src/linter.rs
expression: checks
---
[]

View File

@@ -0,0 +1,13 @@
---
source: src/linter.rs
expression: checks
---
- kind: ConvertExitToSysExit
location:
row: 1
column: 0
end_location:
row: 1
column: 4
fix: ~

View File

@@ -4,138 +4,121 @@ expression: checks
---
- kind: UsePEP604Annotation
location:
row: 4
row: 6
column: 9
end_location:
row: 4
row: 6
column: 22
fix:
patch:
content: str | None
location:
row: 4
row: 6
column: 9
end_location:
row: 4
row: 6
column: 22
applied: false
- kind: UsePEP604Annotation
location:
row: 11
row: 10
column: 9
end_location:
row: 11
row: 10
column: 29
fix:
patch:
content: str | None
location:
row: 11
row: 10
column: 9
end_location:
row: 11
row: 10
column: 29
applied: false
- kind: UsePEP604Annotation
location:
row: 18
row: 14
column: 9
end_location:
row: 18
row: 14
column: 45
fix:
patch:
content: "str | int | Union[float, bytes]"
location:
row: 18
row: 14
column: 9
end_location:
row: 18
row: 14
column: 45
applied: false
- kind: UsePEP604Annotation
location:
row: 18
row: 14
column: 25
end_location:
row: 18
row: 14
column: 44
fix:
patch:
content: float | bytes
location:
row: 18
row: 14
column: 25
end_location:
row: 18
row: 14
column: 44
applied: false
- kind: UsePEP604Annotation
location:
row: 25
row: 18
column: 9
end_location:
row: 25
row: 18
column: 31
fix:
patch:
content: str | int
location:
row: 25
row: 18
column: 9
end_location:
row: 25
row: 18
column: 31
applied: false
- kind: UsePEP604Annotation
location:
row: 32
row: 22
column: 9
end_location:
row: 32
column: 47
fix:
patch:
content: "str | int | Union[float, bytes]"
location:
row: 32
column: 9
end_location:
row: 32
column: 47
applied: false
- kind: UsePEP604Annotation
location:
row: 32
column: 9
end_location:
row: 32
column: 47
fix:
patch:
content: float | bytes
location:
row: 32
column: 9
end_location:
row: 32
column: 47
applied: false
- kind: UsePEP604Annotation
location:
row: 39
column: 9
end_location:
row: 39
row: 22
column: 33
fix:
patch:
content: str | int
location:
row: 39
row: 22
column: 9
end_location:
row: 39
row: 22
column: 33
applied: false
- kind: UsePEP604Annotation
location:
row: 26
column: 9
end_location:
row: 26
column: 40
fix:
patch:
content: "(str, int) | float"
location:
row: 26
column: 9
end_location:
row: 26
column: 40
applied: false

View File

@@ -5,7 +5,7 @@ expression: checks
- kind: UnnecessaryLRUCacheParams
location:
row: 5
column: 1
column: 10
end_location:
row: 5
column: 12
@@ -22,7 +22,7 @@ expression: checks
- kind: UnnecessaryLRUCacheParams
location:
row: 11
column: 1
column: 20
end_location:
row: 11
column: 22
@@ -39,7 +39,7 @@ expression: checks
- kind: UnnecessaryLRUCacheParams
location:
row: 16
column: 1
column: 10
end_location:
row: 16
column: 24
@@ -56,7 +56,7 @@ expression: checks
- kind: UnnecessaryLRUCacheParams
location:
row: 21
column: 1
column: 20
end_location:
row: 21
column: 34
@@ -73,7 +73,7 @@ expression: checks
- kind: UnnecessaryLRUCacheParams
location:
row: 27
column: 1
column: 10
end_location:
row: 28
column: 1
@@ -90,7 +90,7 @@ expression: checks
- kind: UnnecessaryLRUCacheParams
location:
row: 33
column: 1
column: 10
end_location:
row: 35
column: 1
@@ -107,7 +107,7 @@ expression: checks
- kind: UnnecessaryLRUCacheParams
location:
row: 40
column: 1
column: 20
end_location:
row: 42
column: 19
@@ -124,7 +124,7 @@ expression: checks
- kind: UnnecessaryLRUCacheParams
location:
row: 47
column: 1
column: 20
end_location:
row: 51
column: 1
@@ -141,7 +141,7 @@ expression: checks
- kind: UnnecessaryLRUCacheParams
location:
row: 56
column: 1
column: 20
end_location:
row: 62
column: 1
@@ -158,7 +158,7 @@ expression: checks
- kind: UnnecessaryLRUCacheParams
location:
row: 67
column: 1
column: 20
end_location:
row: 72
column: 1

View File

@@ -2,7 +2,8 @@
source: src/linter.rs
expression: checks
---
- kind: ConvertTypedDictFunctionalToClass
- kind:
ConvertTypedDictFunctionalToClass: MyType1
location:
row: 5
column: 0
@@ -19,7 +20,8 @@ expression: checks
row: 5
column: 52
applied: false
- kind: ConvertTypedDictFunctionalToClass
- kind:
ConvertTypedDictFunctionalToClass: MyType2
location:
row: 8
column: 0
@@ -36,7 +38,8 @@ expression: checks
row: 8
column: 50
applied: false
- kind: ConvertTypedDictFunctionalToClass
- kind:
ConvertTypedDictFunctionalToClass: MyType3
location:
row: 11
column: 0
@@ -53,7 +56,8 @@ expression: checks
row: 11
column: 44
applied: false
- kind: ConvertTypedDictFunctionalToClass
- kind:
ConvertTypedDictFunctionalToClass: MyType4
location:
row: 14
column: 0
@@ -70,7 +74,8 @@ expression: checks
row: 14
column: 30
applied: false
- kind: ConvertTypedDictFunctionalToClass
- kind:
ConvertTypedDictFunctionalToClass: MyType5
location:
row: 17
column: 0
@@ -87,7 +92,8 @@ expression: checks
row: 17
column: 46
applied: false
- kind: ConvertTypedDictFunctionalToClass
- kind:
ConvertTypedDictFunctionalToClass: MyType6
location:
row: 18
column: 0
@@ -104,7 +110,8 @@ expression: checks
row: 18
column: 41
applied: false
- kind: ConvertTypedDictFunctionalToClass
- kind:
ConvertTypedDictFunctionalToClass: MyType7
location:
row: 21
column: 0
@@ -121,7 +128,8 @@ expression: checks
row: 21
column: 56
applied: false
- kind: ConvertTypedDictFunctionalToClass
- kind:
ConvertTypedDictFunctionalToClass: MyType8
location:
row: 24
column: 0
@@ -138,7 +146,8 @@ expression: checks
row: 24
column: 65
applied: false
- kind: ConvertTypedDictFunctionalToClass
- kind:
ConvertTypedDictFunctionalToClass: MyType10
location:
row: 30
column: 0
@@ -155,7 +164,8 @@ expression: checks
row: 30
column: 59
applied: false
- kind: ConvertTypedDictFunctionalToClass
- kind:
ConvertTypedDictFunctionalToClass: MyType11
location:
row: 33
column: 0

View File

@@ -0,0 +1,59 @@
---
source: src/linter.rs
expression: checks
---
- kind:
ConvertNamedTupleFunctionalToClass: NT1
location:
row: 5
column: 0
end_location:
row: 5
column: 61
fix:
patch:
content: "class NT1(NamedTuple):\n a: int\n b: tuple[str, ...]"
location:
row: 5
column: 0
end_location:
row: 5
column: 61
applied: false
- kind:
ConvertNamedTupleFunctionalToClass: NT2
location:
row: 8
column: 0
end_location:
row: 12
column: 1
fix:
patch:
content: "class NT2(NamedTuple):\n a: int\n b: str = 'foo'\n c: list[bool] = [True]"
location:
row: 8
column: 0
end_location:
row: 12
column: 1
applied: false
- kind:
ConvertNamedTupleFunctionalToClass: NT3
location:
row: 15
column: 0
end_location:
row: 15
column: 56
fix:
patch:
content: "class NT3(typing.NamedTuple):\n a: int\n b: str"
location:
row: 15
column: 0
end_location:
row: 15
column: 56
applied: false