Compare commits

...

85 Commits

Author SHA1 Message Date
Charlie Marsh
58aac21a36 Bump version to 0.0.136 2022-11-23 17:41:17 -05:00
Charlie Marsh
77e0be3464 Visit iter prior to target in comprehensions (#895) 2022-11-23 10:13:21 -05:00
Charlie Marsh
bd08fc359d Upload wheels back to GitHub Releases (#884) 2022-11-23 00:06:36 -05:00
Harutaka Kawamura
19ad6ab4f5 Add --explain (#887) 2022-11-23 00:06:25 -05:00
Charlie Marsh
4b2df99e78 Set rust-version in Cargo.toml (#886) 2022-11-22 23:33:39 -05:00
Charlie Marsh
66975876b2 Bump version to 0.0.135 2022-11-22 19:21:53 -05:00
Charlie Marsh
7316b120ba Log errors in add_noqa and autoformat calls (#881) 2022-11-22 19:21:36 -05:00
Charlie Marsh
fec887e481 Apply a limit to the number of fix iterations (#882) 2022-11-22 19:21:31 -05:00
Charlie Marsh
bdd32c0850 Enforce most pedantic lints on CI (#878) 2022-11-22 18:55:57 -05:00
Charlie Marsh
10dcd5fd0a Remove unused imports 2022-11-22 18:45:24 -05:00
Charlie Marsh
b922e6ecc8 Fix clippy::unnecessary_wraps (pedantic) (#880)
https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps
2022-11-22 18:25:30 -05:00
Charlie Marsh
f59799e0c4 Remove lingering empty lines 2022-11-22 17:54:24 -05:00
Charlie Marsh
814ddeb7ea Remove always-inline (#879) 2022-11-22 17:13:25 -05:00
Charlie Marsh
9315b9f459 Remove Mode from various internal checkers (#877) 2022-11-22 16:57:47 -05:00
Charlie Marsh
113b5a10bf Return Vec<Check> from check_tokens (#876) 2022-11-22 16:43:32 -05:00
Charlie Marsh
fe77fb70a1 Apply autofixes iteratively until code is stabilized (#875) 2022-11-22 16:37:52 -05:00
Charlie Marsh
c3f6170503 Update README with list of projects (#874) 2022-11-22 14:28:02 -05:00
Anders Kaseorg
a46160f0e2 Fix clippy::unreadable_literal (pedantic)
https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
9a66cf2ffb Fix clippy::uninlined_format_args (pedantic)
https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
3205473612 Ignore clippy::struct_excessive_bools (pedantic)
“consider using a state machine or refactoring bools into two-variant
enums”

https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
0bb8b14ae1 Fix clippy::single_match_else (pedantic)
https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
9cf4621071 Fix clippy::redundant_else (pedantic)
https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
bbc9ed1b21 Fix clippy::redundant_closure_for_method_calls (pedantic)
https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
517ca2604a Fix clippy::needless_pass_by_value (pedantic)
“this argument is passed by value, but not consumed in the function
body”

https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
348ff509c0 Fix clippy::mut_mut (pedantic)
“this expression mutably borrows a mutable reference. Consider
reborrowing”

https://rust-lang.github.io/rust-clippy/master/index.html#mut_mut

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
fb545551f8 Fix clippy::match_bool (pedantic)
https://rust-lang.github.io/rust-clippy/master/index.html#match_bool

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
3b33a431d6 Fix clippy::map_unwrap_or (pedantic)
https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
bc95690725 Fix clippy::manual_string_new (pedantic)
https://rust-lang.github.io/rust-clippy/master/index.html#manual_string_new

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
9dc788d089 Fix clippy::let_underscore_drop (pedantic)
https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_drop

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
15f63494a7 Fix clippy::items_after_statements (pedantic)
“adding items after statements is confusing, since items exist from
the start of the scope”

https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
68668c20e8 Fix clippy::if_not_else (pedantic)
https://rust-lang.github.io/rust-clippy/master/index.html#if_not_else

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
9462335371 Fix clippy::from_iter_instead_of_collect (pedantic)
https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
3dd6522b4f Fix clippy::explicit_iter_loop (pedantic)
“it is more concise to loop over references to containers instead of
using explicit iteration methods”

https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
6b935121a7 Fix clippy::explicit_deref_methods (pedantic)
https://rust-lang.github.io/rust-clippy/master/index.html#explicit_deref_methods

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
8d9d9b3204 Fix clippy::documentation_markdown (pedantic)
https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
0a506eff34 Fix clippy::cloned_instead_of_copied (pedantic)
“used `cloned` where `copied` could be used instead”

https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
7c7489c1dd Ignore clippy::cast_possible_truncation (pedantic)
“casting `usize` to `u8` may truncate the value”

https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
ae90eccb7f Fix clippy::cast_lossless (pedantic)
“casting `bool` to `u8` is more cleanly stated with `u8::from(_)`”

https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
2022-11-21 23:22:28 -05:00
Anders Kaseorg
7a61edbe46 Fix clippy::default-trait-access (pedantic) (#867) 2022-11-21 21:00:38 -05:00
Charlie Marsh
59615486d8 Bump version to 0.0.134 2022-11-21 16:15:23 -05:00
Andri Bergsson
ce116a80ad Automatically remove redundant open modes #640 (#843) 2022-11-21 16:06:41 -05:00
Anders Kaseorg
731fba9006 Ignore clippy::unreadable-literal (pedantic) for CONFUSABLES (#864) 2022-11-21 16:00:05 -05:00
Anders Kaseorg
9bcf194fdc Ignore clippy::match-same-arms (pedantic) in a few places (#863) 2022-11-21 15:59:58 -05:00
Anders Kaseorg
58949b564e Fix clippy::trivially-copy-pass-by-ref (pedantic) (#862) 2022-11-21 15:59:51 -05:00
Anders Kaseorg
6b9e57fb78 Fix clippy::sort-unstable (pedantic) (#861) 2022-11-21 15:59:41 -05:00
Anders Kaseorg
cb119401a7 Fix clippy::inefficient-to-string (pedantic) (#860) 2022-11-21 15:59:35 -05:00
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
Charlie Marsh
4be09b45ea Bump version to 0.0.129 2022-11-19 19:52:40 -05:00
Harutaka Kawamura
13e8ed0a0a Implement autofix for E731 (#814) 2022-11-19 19:51:41 -05:00
Anders Kaseorg
4161d4ae32 Exempt parameters with immutable annotations from B006 (#821) 2022-11-19 19:46:08 -05:00
Charlie Marsh
99f7854d8c Mark nonlocal variables as used in parent scopes (#822) 2022-11-19 19:21:02 -05:00
Harutaka Kawamura
a580d1a858 Adjust UnusedNOQA start location (#817) 2022-11-19 09:30:02 -05:00
Martin Lehoux
86806a9e39 U013: Also convert typing.TypedDict (#810) 2022-11-19 09:29:05 -05:00
Charlie Marsh
89afc9db74 Bump version to 0.0.128 2022-11-18 18:50:03 -05:00
Charlie Marsh
0f34cdb7a3 Enable customization of autofixable error codes (#811) 2022-11-18 18:49:13 -05:00
Charlie Marsh
437b6f23b9 Remove warn_on checks (#812) 2022-11-18 18:48:24 -05:00
Charlie Marsh
0fe2b15676 Change NotInTest to NotIsTest 2022-11-18 18:23:40 -05:00
Harutaka Kawamura
e81efa5a3d Implement a --show-source setting (#698) 2022-11-18 14:02:29 -05:00
259 changed files with 4951 additions and 3277 deletions

View File

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

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 }}
@@ -295,10 +287,14 @@ jobs:
with:
name: wheels
- uses: actions/setup-python@v4
- name: Publish to PyPi
- name: Publish to PyPI
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.RUFF_TOKEN }}
run: |
pip install --upgrade twine
twine upload --skip-existing *
- name: Publish to GitHub Releases
uses: softprops/action-gh-release@v1
with:
files: *

View File

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

674
Cargo.lock generated
View File

@@ -49,6 +49,16 @@ version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7021ce4924a3f25f802b2cccd1af585e39ea1a363a1aa2e72afe54b67a3a7a7"
[[package]]
name = "annotate-snippets"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3b9d411ecbaf79885c6df4d75fff75858d5995ff25385657a28af47e82f9c36"
dependencies = [
"unicode-width",
"yansi-term",
]
[[package]]
name = "anyhow"
version = "1.0.66"
@@ -84,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"
@@ -228,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"
@@ -273,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"
@@ -347,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"
@@ -417,7 +215,7 @@ version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5b5db619f3556839cb2223ae86ff3f9a09da2c5013be42bc9af08c9589bf70c"
dependencies = [
"annotate-snippets",
"annotate-snippets 0.6.1",
]
[[package]]
@@ -572,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"
@@ -606,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"
@@ -709,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"
@@ -775,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"
@@ -880,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"
@@ -930,15 +670,15 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8-to-ruff"
version = "0.0.127-dev.0"
version = "0.0.136-dev.0"
dependencies = [
"anyhow",
"clap 4.0.22",
"configparser",
"fnv",
"once_cell",
"regex",
"ruff",
"rustc-hash",
"serde",
"serde_json",
"toml",
@@ -1004,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"
@@ -1157,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"
@@ -1199,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"
@@ -1353,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"
@@ -1498,7 +1082,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if 1.0.0",
"value-bag",
]
[[package]]
@@ -1522,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"
@@ -1717,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"
@@ -1913,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"
@@ -1953,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"
@@ -2238,14 +1768,15 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.127"
version = "0.0.136"
dependencies = [
"annotate-snippets 0.9.1",
"anyhow",
"assert_cmd",
"atty",
"bincode",
"bitflags",
"cacache",
"cachedir",
"chrono",
"clap 4.0.22",
"clearscreen",
@@ -2255,7 +1786,6 @@ dependencies = [
"dirs 4.0.0",
"fern",
"filetime",
"fnv",
"getrandom 0.2.8",
"glob",
"insta",
@@ -2270,6 +1800,7 @@ dependencies = [
"rayon",
"regex",
"ropey",
"rustc-hash",
"rustpython-ast",
"rustpython-common",
"rustpython-parser",
@@ -2287,7 +1818,7 @@ dependencies = [
[[package]]
name = "ruff_dev"
version = "0.0.127"
version = "0.0.136"
dependencies = [
"anyhow",
"clap 4.0.22",
@@ -2302,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"
@@ -2317,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",
@@ -2327,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",
@@ -2350,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",
@@ -2367,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",
@@ -2379,6 +1916,7 @@ dependencies = [
"num-traits",
"phf 0.10.1",
"phf_codegen 0.10.0",
"rustc-hash",
"rustpython-ast",
"rustpython-compiler-core",
"thiserror",
@@ -2468,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"
@@ -2570,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"
@@ -2845,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"
@@ -2989,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",
@@ -3013,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"
@@ -3044,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"
@@ -3098,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"
@@ -3168,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"
@@ -3312,3 +2713,12 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]
[[package]]
name = "yansi-term"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe5c30ade05e61656247b2e334a031dfd0cc466fadef865bdcdea8d537951bf1"
dependencies = [
"winapi 0.3.9",
]

View File

@@ -6,17 +6,20 @@ members = [
[package]
name = "ruff"
version = "0.0.127"
version = "0.0.136"
edition = "2021"
rust-version = "1.65.0"
[lib]
name = "ruff"
[dependencies]
annotate-snippets = { version = "0.9.1", features = ["color"] }
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" }
@@ -24,7 +27,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" }
@@ -37,9 +39,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"] }
@@ -51,7 +54,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

@@ -32,12 +32,14 @@ of plugins), [`isort`](https://pypi.org/project/isort/), [`pydocstyle`](https://
and [`autoflake`](https://pypi.org/project/autoflake/) all while executing tens or hundreds of times
faster than any individual tool.
(Coming from Flake8? Try [`flake8-to-ruff`](https://pypi.org/project/flake8-to-ruff/) to
automatically convert your existing configuration.)
Ruff is actively developed and used in major open-source projects like:
Ruff is actively developed and used in major open-source projects
like [FastAPI](https://github.com/tiangolo/fastapi), [Zulip](https://github.com/zulip/zulip),
[pydantic](https://github.com/pydantic/pydantic), and [Saleor](https://github.com/saleor/saleor).
- [FastAPI](https://github.com/tiangolo/fastapi)
- [Bokeh](https://github.com/bokeh/bokeh)
- [Zulip](https://github.com/zulip/zulip)
- [Pydantic](https://github.com/pydantic/pydantic)
- [Saleor](https://github.com/saleor/saleor)
- [Hatch](https://github.com/pypa/hatch)
Read the [launch blog post](https://notes.crmarsh.com/python-tooling-could-be-much-much-faster).
@@ -105,7 +107,7 @@ Ruff also works with [pre-commit](https://pre-commit.com):
```yaml
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.116
rev: v0.0.136
hooks:
- id: ruff
```
@@ -122,7 +124,7 @@ default configuration is equivalent to:
[tool.ruff]
line-length = 88
# Enable Flake's "E" and "F" codes by default.
# Enable Pyflakes `E` and `F` codes by default.
select = ["E", "F"]
ignore = []
@@ -155,20 +157,30 @@ 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
violations (`E501`) and (2) ignore unused import rules in `__init__.py` files:
As an example, the following would configure Ruff to: (1) avoid checking for line-length
violations (`E501`); (2), always autofix, but never remove unused imports (`F401`); and (3) ignore
import-at-top-of-file errors (`E402`) in `__init__.py` files:
```toml
[tool.ruff]
# Enable Pyflakes and pycodestyle rules.
select = ["E", "F"]
# Never enforce `E501`.
# Never enforce `E501` (line length violations).
ignore = ["E501"]
# Ignore `F401` violations in any `__init__.py` file, and in `path/to/file.py`.
per-file-ignores = {"__init__.py" = ["F401"], "path/to/file.py" = ["F401"]}
# Always autofix, but never try to fix `F401` (unused imports).
fix = true
unfixable = ["F401"]
# Ignore `E402` (import violations in any `__init__.py` file, and in `path/to/file.py`.
per-file-ignores = {"__init__.py" = ["E402"], "path/to/file.py" = ["E402"]}
```
Plugin configurations should be expressed as subsections, e.g.:
@@ -191,7 +203,7 @@ ruff path/to/code/ --select F401 --select F403
See `ruff --help` for more:
```shell
ruff: An extremely fast Python linter.
Ruff: An extremely fast Python linter.
Usage: ruff [OPTIONS] <FILES>...
@@ -227,14 +239,20 @@ Options:
List of paths, used to exclude files and/or directories from checks
--extend-exclude <EXTEND_EXCLUDE>
Like --exclude, but adds additional files and directories on top of the excluded ones
--fixable <FIXABLE>
List of error codes to treat as eligible for autofix. Only applicable when autofix itself is enabled (e.g., via `--fix`)
--unfixable <UNFIXABLE>
List of error codes to treat as ineligible for autofix. Only applicable when autofix itself is enabled (e.g., via `--fix`)
--per-file-ignores <PER_FILE_IGNORES>
List of mappings from file pattern to code to exclude
--format <FORMAT>
Output serialization format for error messages [default: text] [possible values: text, json]
--show-source
Show violations with source code
--show-files
See the files ruff will be run against with the current settings
See the files Ruff will be run against with the current settings
--show-settings
See ruff's settings
See Ruff's settings
--add-noqa
Enable automatic additions of noqa directives to failing lines
--dummy-variable-rgx <DUMMY_VARIABLE_RGX>
@@ -244,7 +262,7 @@ Options:
--line-length <LINE_LENGTH>
Set the line-length for length-associated checks and automatic formatting
--max-complexity <MAX_COMPLEXITY>
Set the maximum cyclomatic complexity for complexity-associated checks
Max McCabe complexity allowed for a function
--stdin-filename <STDIN_FILENAME>
The name of the file when passing it through stdin
-h, --help
@@ -368,7 +386,7 @@ For more, see [pycodestyle](https://pypi.org/project/pycodestyle/2.9.1/) on PyPI
| E714 | NotIsTest | Test for object identity should be `is not` | 🛠 |
| E721 | TypeComparison | Do not compare types, use `isinstance()` | |
| E722 | DoNotUseBareExcept | Do not use bare `except` | |
| E731 | DoNotAssignLambda | Do not assign a lambda expression, use a def | |
| E731 | DoNotAssignLambda | Do not assign a lambda expression, use a def | 🛠 |
| E741 | AmbiguousVariableName | Ambiguous variable name: `...` | |
| E742 | AmbiguousClassName | Ambiguous class name: `...` | |
| E743 | AmbiguousFunctionName | Ambiguous function name: `...` | |
@@ -453,7 +471,9 @@ 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 | 🛠 |
| U015 | RedundantOpenModes | Unnecessary open mode parameters | 🛠 |
### pep8-naming
@@ -537,10 +557,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? | |
@@ -651,6 +671,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
@@ -808,7 +829,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/) (16/33)
- [`autoflake`](https://pypi.org/project/autoflake/) (1/7)
Beyond rule-set parity, Ruff suffers from the following limitations vis-à-vis Flake8:
@@ -840,7 +861,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/) (16/33).
If you're looking to use Ruff, but rely on an unsupported Flake8 plugin, free to file an Issue.
@@ -879,7 +900,7 @@ select = [
"E",
"W",
# isort
"I"
"I001"
]
src = ["src", "tests"]

View File

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

View File

@@ -771,7 +771,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8_to_ruff"
version = "0.0.127"
version = "0.0.136"
dependencies = [
"anyhow",
"clap",
@@ -1975,7 +1975,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.127"
version = "0.0.136"
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.127-dev.0"
version = "0.0.136-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

@@ -18,7 +18,7 @@ pub fn convert(
plugins: Option<Vec<Plugin>>,
) -> Result<Pyproject> {
// Extract all referenced check code prefixes, to power plugin inference.
let mut referenced_codes: BTreeSet<CheckCodePrefix> = Default::default();
let mut referenced_codes: BTreeSet<CheckCodePrefix> = BTreeSet::default();
for (key, value) in flake8 {
if let Some(value) = value {
match key.as_str() {
@@ -70,13 +70,13 @@ pub fn convert(
.unwrap_or_default();
// Parse each supported option.
let mut options: Options = Default::default();
let mut flake8_annotations: flake8_annotations::settings::Options = Default::default();
let mut flake8_bugbear: flake8_bugbear::settings::Options = Default::default();
let mut flake8_quotes: flake8_quotes::settings::Options = Default::default();
let mut flake8_tidy_imports: flake8_tidy_imports::settings::Options = Default::default();
let mut mccabe: mccabe::settings::Options = Default::default();
let mut pep8_naming: pep8_naming::settings::Options = Default::default();
let mut options = Options::default();
let mut flake8_annotations = flake8_annotations::settings::Options::default();
let mut flake8_bugbear = flake8_bugbear::settings::Options::default();
let mut flake8_quotes = flake8_quotes::settings::Options::default();
let mut flake8_tidy_imports = flake8_tidy_imports::settings::Options::default();
let mut mccabe = mccabe::settings::Options::default();
let mut pep8_naming = pep8_naming::settings::Options::default();
for (key, value) in flake8 {
if let Some(value) = value {
match key.as_str() {
@@ -110,7 +110,7 @@ pub fn convert(
match parser::parse_files_to_codes_mapping(value.as_ref()) {
Ok(per_file_ignores) => {
options.per_file_ignores =
Some(parser::collect_per_file_ignores(per_file_ignores))
Some(parser::collect_per_file_ignores(per_file_ignores));
}
Err(e) => eprintln!("Unable to parse '{key}' property: {e}"),
}
@@ -181,7 +181,7 @@ pub fn convert(
"ban-relative-imports" | "ban_relative_imports" => match value.trim() {
"true" => flake8_tidy_imports.ban_relative_imports = Some(Strictness::All),
"parents" => {
flake8_tidy_imports.ban_relative_imports = Some(Strictness::Parents)
flake8_tidy_imports.ban_relative_imports = Some(Strictness::Parents);
}
_ => eprintln!("Unexpected '{key}' value: {value}"),
},
@@ -203,22 +203,22 @@ pub fn convert(
// Deduplicate and sort.
options.select = Some(Vec::from_iter(select));
options.ignore = Some(Vec::from_iter(ignore));
if flake8_annotations != Default::default() {
if flake8_annotations != flake8_annotations::settings::Options::default() {
options.flake8_annotations = Some(flake8_annotations);
}
if flake8_bugbear != Default::default() {
if flake8_bugbear != flake8_bugbear::settings::Options::default() {
options.flake8_bugbear = Some(flake8_bugbear);
}
if flake8_quotes != Default::default() {
if flake8_quotes != flake8_quotes::settings::Options::default() {
options.flake8_quotes = Some(flake8_quotes);
}
if flake8_tidy_imports != Default::default() {
if flake8_tidy_imports != flake8_tidy_imports::settings::Options::default() {
options.flake8_tidy_imports = Some(flake8_tidy_imports);
}
if mccabe != Default::default() {
if mccabe != mccabe::settings::Options::default() {
options.mccabe = Some(mccabe);
}
if pep8_naming != Default::default() {
if pep8_naming != pep8_naming::settings::Options::default() {
options.pep8_naming = Some(pep8_naming);
}
@@ -243,22 +243,25 @@ mod tests {
fn it_converts_empty() -> Result<()> {
let actual = convert(&HashMap::from([]), None)?;
let expected = Pyproject::new(Options {
line_length: None,
src: None,
fix: None,
dummy_variable_rgx: None,
exclude: None,
extend_exclude: None,
extend_ignore: None,
extend_select: None,
fix: None,
fixable: None,
ignore: Some(vec![]),
line_length: None,
per_file_ignores: None,
select: Some(vec![
CheckCodePrefix::E,
CheckCodePrefix::F,
CheckCodePrefix::W,
]),
extend_select: None,
ignore: Some(vec![]),
extend_ignore: None,
per_file_ignores: None,
dummy_variable_rgx: None,
show_source: None,
src: None,
target_version: None,
unfixable: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_quotes: None,
@@ -279,22 +282,25 @@ mod tests {
Some(vec![]),
)?;
let expected = Pyproject::new(Options {
line_length: Some(100),
src: None,
fix: None,
dummy_variable_rgx: None,
exclude: None,
extend_exclude: None,
extend_ignore: None,
extend_select: None,
fix: None,
fixable: None,
ignore: Some(vec![]),
line_length: Some(100),
per_file_ignores: None,
select: Some(vec![
CheckCodePrefix::E,
CheckCodePrefix::F,
CheckCodePrefix::W,
]),
extend_select: None,
ignore: Some(vec![]),
extend_ignore: None,
per_file_ignores: None,
dummy_variable_rgx: None,
show_source: None,
src: None,
target_version: None,
unfixable: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_quotes: None,
@@ -315,22 +321,25 @@ mod tests {
Some(vec![]),
)?;
let expected = Pyproject::new(Options {
line_length: Some(100),
src: None,
fix: None,
dummy_variable_rgx: None,
exclude: None,
extend_exclude: None,
extend_ignore: None,
extend_select: None,
fix: None,
fixable: None,
ignore: Some(vec![]),
line_length: Some(100),
per_file_ignores: None,
select: Some(vec![
CheckCodePrefix::E,
CheckCodePrefix::F,
CheckCodePrefix::W,
]),
extend_select: None,
ignore: Some(vec![]),
extend_ignore: None,
per_file_ignores: None,
dummy_variable_rgx: None,
show_source: None,
src: None,
target_version: None,
unfixable: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_quotes: None,
@@ -351,22 +360,25 @@ mod tests {
Some(vec![]),
)?;
let expected = Pyproject::new(Options {
line_length: None,
src: None,
fix: None,
dummy_variable_rgx: None,
exclude: None,
extend_exclude: None,
extend_ignore: None,
extend_select: None,
fix: None,
fixable: None,
ignore: Some(vec![]),
line_length: None,
per_file_ignores: None,
select: Some(vec![
CheckCodePrefix::E,
CheckCodePrefix::F,
CheckCodePrefix::W,
]),
extend_select: None,
ignore: Some(vec![]),
extend_ignore: None,
per_file_ignores: None,
dummy_variable_rgx: None,
show_source: None,
src: None,
target_version: None,
unfixable: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_quotes: None,
@@ -387,22 +399,25 @@ mod tests {
Some(vec![]),
)?;
let expected = Pyproject::new(Options {
line_length: None,
src: None,
fix: None,
dummy_variable_rgx: None,
exclude: None,
extend_exclude: None,
extend_ignore: None,
extend_select: None,
fix: None,
fixable: None,
ignore: Some(vec![]),
line_length: None,
per_file_ignores: None,
select: Some(vec![
CheckCodePrefix::E,
CheckCodePrefix::F,
CheckCodePrefix::W,
]),
extend_select: None,
ignore: Some(vec![]),
extend_ignore: None,
per_file_ignores: None,
dummy_variable_rgx: None,
show_source: None,
src: None,
target_version: None,
unfixable: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_quotes: Some(flake8_quotes::settings::Options {
@@ -431,11 +446,16 @@ mod tests {
Some(vec![Plugin::Flake8Docstrings]),
)?;
let expected = Pyproject::new(Options {
line_length: None,
src: None,
fix: None,
dummy_variable_rgx: None,
exclude: None,
extend_exclude: None,
extend_ignore: None,
extend_select: None,
fix: None,
fixable: None,
ignore: Some(vec![]),
line_length: None,
per_file_ignores: None,
select: Some(vec![
CheckCodePrefix::D100,
CheckCodePrefix::D101,
@@ -476,12 +496,10 @@ mod tests {
CheckCodePrefix::F,
CheckCodePrefix::W,
]),
extend_select: None,
ignore: Some(vec![]),
extend_ignore: None,
per_file_ignores: None,
dummy_variable_rgx: None,
show_source: None,
src: None,
target_version: None,
unfixable: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_quotes: None,
@@ -502,23 +520,26 @@ mod tests {
None,
)?;
let expected = Pyproject::new(Options {
line_length: None,
src: None,
fix: None,
dummy_variable_rgx: None,
exclude: None,
extend_exclude: None,
extend_ignore: None,
extend_select: None,
fix: None,
fixable: None,
ignore: Some(vec![]),
line_length: None,
per_file_ignores: None,
select: Some(vec![
CheckCodePrefix::E,
CheckCodePrefix::F,
CheckCodePrefix::Q,
CheckCodePrefix::W,
]),
extend_select: None,
ignore: Some(vec![]),
extend_ignore: None,
per_file_ignores: None,
dummy_variable_rgx: None,
show_source: None,
src: None,
target_version: None,
unfixable: None,
flake8_annotations: None,
flake8_bugbear: None,
flake8_quotes: Some(flake8_quotes::settings::Options {

View File

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

View File

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

View File

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

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

View File

@@ -34,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

@@ -35,3 +35,25 @@ def f4():
_ = 1
__ = 1
_discarded = 1
a = 1
def f5():
global a
# Used in `f7` via `nonlocal`.
b = 1
def f6():
# F841
b = 1
def f7():
nonlocal b
def f6():
annotations = []
assert len([annotations for annotations in annotations])

View File

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

View File

@@ -30,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:
...

View File

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

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"],
)

38
resources/test/fixtures/U015.py vendored Normal file
View File

@@ -0,0 +1,38 @@
open("foo", "U")
open("foo", "Ur")
open("foo", "Ub")
open("foo", "rUb")
open("foo", "r")
open("foo", "rt")
open("f", "r", encoding="UTF-8")
open("f", "wt")
with open("foo", "U") as f:
pass
with open("foo", "Ur") as f:
pass
with open("foo", "Ub") as f:
pass
with open("foo", "rUb") as f:
pass
with open("foo", "r") as f:
pass
with open("foo", "rt") as f:
pass
with open("foo", "r", encoding="UTF-8") as f:
pass
with open("foo", "wt") as f:
pass
open(f("a", "b", "c"), "U")
open(f("a", "b", "c"), "Ub")
with open(f("a", "b", "c"), "U") as f:
pass
with open(f("a", "b", "c"), "Ub") as f:
pass
with open("foo", "U") as fa, open("bar", "U") as fb:
pass
with open("foo", "Ub") as fa, open("bar", "Ub") as fb:
pass

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.127"
version = "0.0.136"
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

@@ -24,7 +24,7 @@ pub struct Cli {
pub fn main(cli: &Cli) -> Result<()> {
// Build up a map from prefix to matching CheckCodes.
let mut prefix_to_codes: BTreeMap<String, BTreeSet<CheckCode>> = Default::default();
let mut prefix_to_codes: BTreeMap<String, BTreeSet<CheckCode>> = BTreeMap::default();
for check_code in CheckCode::iter() {
let as_ref: String = check_code.as_ref().to_string();
let prefix_len = as_ref
@@ -77,6 +77,7 @@ pub fn main(cli: &Cli) -> Result<()> {
.arg_ref_self()
.ret(Type::new("Vec<CheckCode>"))
.vis("pub")
.line("#[allow(clippy::match_same_arms)]")
.line("match self {");
for (prefix, codes) in &prefix_to_codes {
gen = gen.line(format!(
@@ -96,6 +97,7 @@ pub fn main(cli: &Cli) -> Result<()> {
.arg_ref_self()
.ret(Type::new("PrefixSpecificity"))
.vis("pub")
.line("#[allow(clippy::match_same_arms)]")
.line("match self {");
for prefix in prefix_to_codes.keys() {
let num_numeric = prefix.chars().filter(|char| char.is_numeric()).count();
@@ -104,7 +106,7 @@ pub fn main(cli: &Cli) -> Result<()> {
2 => "Tens",
1 => "Hundreds",
0 => "Category",
_ => panic!("Invalid prefix: {}", prefix),
_ => panic!("Invalid prefix: {prefix}"),
};
gen = gen.line(format!(
"CheckCodePrefix::{prefix} => PrefixSpecificity::{},",
@@ -130,10 +132,10 @@ pub fn main(cli: &Cli) -> Result<()> {
// Write the output to `src/checks_gen.rs` (or stdout).
if cli.dry_run {
println!("{}", output);
println!("{output}");
} else {
let mut f = OpenOptions::new().write(true).truncate(true).open(FILE)?;
write!(f, "{}", output)?;
write!(f, "{output}")?;
}
Ok(())

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,9 +1,11 @@
use fnv::{FnvHashMap, FnvHashSet};
use once_cell::sync::Lazy;
use regex::Regex;
use rustpython_ast::{Excepthandler, ExcepthandlerKind, Expr, ExprKind, Location, StmtKind};
use rustc_hash::{FxHashMap, FxHashSet};
use rustpython_ast::{Excepthandler, ExcepthandlerKind, Expr, ExprKind, Location, Stmt, StmtKind};
use crate::ast::types::Range;
use crate::SourceCodeLocator;
#[inline(always)]
fn collect_call_path_inner<'a>(expr: &'a Expr, parts: &mut Vec<&'a str>) {
match &expr.node {
ExprKind::Call { func, .. } => {
@@ -21,7 +23,6 @@ fn collect_call_path_inner<'a>(expr: &'a Expr, parts: &mut Vec<&'a str>) {
}
/// Convert an `Expr` to its call path (like `List`, or `typing.List`).
#[inline(always)]
pub fn compose_call_path(expr: &Expr) -> Option<String> {
let segments = collect_call_paths(expr);
if segments.is_empty() {
@@ -32,7 +33,6 @@ pub fn compose_call_path(expr: &Expr) -> Option<String> {
}
/// Convert an `Expr` to its call path segments (like ["typing", "List"]).
#[inline(always)]
pub fn collect_call_paths(expr: &Expr) -> Vec<&str> {
let mut segments = vec![];
collect_call_path_inner(expr, &mut segments);
@@ -42,7 +42,7 @@ pub fn collect_call_paths(expr: &Expr) -> Vec<&str> {
/// Rewrite any import aliases on a call path.
pub fn dealias_call_path<'a>(
call_path: Vec<&'a str>,
import_aliases: &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) {
@@ -76,8 +76,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),
@@ -94,7 +94,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();
@@ -118,10 +118,9 @@ pub fn match_call_path(
// `Match`).
if num_segments == 0 {
module.is_empty()
|| from_imports
.get(module)
.map(|imports| imports.contains(member) || imports.contains("*"))
.unwrap_or(false)
|| from_imports.get(module).map_or(false, |imports| {
imports.contains(member) || imports.contains("*")
})
} else {
let components: Vec<&str> = module.split('.').collect();
@@ -145,8 +144,7 @@ pub fn match_call_path(
let member = components[cut];
if from_imports
.get(&module.as_str())
.map(|imports| imports.contains(member))
.unwrap_or(false)
.map_or(false, |imports| imports.contains(member))
{
return true;
}
@@ -250,7 +248,7 @@ pub fn to_module_and_member(target: &str) -> (&str, &str) {
/// Convert a location within a file (relative to `base`) to an absolute
/// position.
pub fn to_absolute(relative: &Location, base: &Location) -> Location {
pub fn to_absolute(relative: Location, base: Location) -> Location {
if relative.row() == 1 {
Location::new(
relative.row() + base.row() - 1,
@@ -261,10 +259,38 @@ pub fn to_absolute(relative: &Location, base: &Location) -> Location {
}
}
/// Return `true` if a `Stmt` has leading content.
pub fn match_leading_content(stmt: &Stmt, locator: &SourceCodeLocator) -> bool {
let range = Range {
location: Location::new(stmt.location.row(), 0),
end_location: stmt.location,
};
let prefix = locator.slice_source_code_range(&range);
prefix.chars().any(|char| !char.is_whitespace())
}
/// Return `true` if a `Stmt` has trailing content.
pub fn match_trailing_content(stmt: &Stmt, locator: &SourceCodeLocator) -> bool {
let range = Range {
location: stmt.end_location.unwrap(),
end_location: Location::new(stmt.end_location.unwrap().row() + 1, 0),
};
let suffix = locator.slice_source_code_range(&range);
for char in suffix.chars() {
if char == '#' {
return false;
}
if !char.is_whitespace() {
return true;
}
}
false
}
#[cfg(test)]
mod tests {
use anyhow::Result;
use fnv::{FnvHashMap, FnvHashSet};
use rustc_hash::{FxHashMap, FxHashSet};
use rustpython_parser::parser;
use crate::ast::helpers::match_module_member;
@@ -276,8 +302,8 @@ mod tests {
&expr,
"",
"list",
&FnvHashMap::default(),
&FnvHashMap::default(),
&FxHashMap::default(),
&FxHashMap::default(),
));
Ok(())
}
@@ -289,8 +315,8 @@ mod tests {
&expr,
"typing.re",
"Match",
&FnvHashMap::default(),
&FnvHashMap::default(),
&FxHashMap::default(),
&FxHashMap::default(),
));
Ok(())
}
@@ -302,16 +328,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(())
}
@@ -323,8 +349,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(())
}
@@ -336,8 +362,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(())
}
@@ -349,8 +375,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>")?;
@@ -358,8 +384,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>")?;
@@ -367,8 +393,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(())
}
@@ -380,8 +406,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(())
}
@@ -393,8 +419,8 @@ mod tests {
&expr,
"typing.re",
"Match",
&FnvHashMap::default(),
&FnvHashMap::from_iter([("t", "typing.re")]),
&FxHashMap::default(),
&FxHashMap::from_iter([("t", "typing.re")]),
));
Ok(())
}
@@ -406,8 +432,8 @@ mod tests {
&expr,
"typing.re",
"Match",
&FnvHashMap::default(),
&FnvHashMap::from_iter([("t", "typing")]),
&FxHashMap::default(),
&FxHashMap::from_iter([("t", "typing")]),
));
Ok(())
}

View File

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

View File

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

View File

@@ -1,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(),
}
}
}
@@ -83,6 +81,7 @@ pub enum BindingKind {
Binding,
LoopVar,
Global,
Nonlocal,
Builtin,
ClassDefinition,
Definition,

View File

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

View File

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

View File

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

View File

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

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;
@@ -71,9 +63,10 @@ impl Mode {
impl From<bool> for Mode {
fn from(value: bool) -> Self {
match value {
true => Mode::ReadWrite,
false => Mode::None,
if value {
Mode::ReadWrite
} else {
Mode::None
}
}
}
@@ -82,28 +75,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 +139,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 +150,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,19 +168,16 @@ 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(),
if let Err(e) = write_sync(
cache_key(path, settings, autofix),
bincode::serialize(&check_result).unwrap(),
&bincode::serialize(&check_result).unwrap(),
) {
error!("Failed to write to cache: {e:?}")
error!("Failed to write to cache: {e:?}");
}
}

View File

@@ -1,12 +1,11 @@
//! Lint rules based on AST traversal.
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,
@@ -19,11 +18,11 @@ use crate::ast::helpers::{
use crate::ast::operations::extract_all_names;
use crate::ast::relocate::relocate_expr;
use crate::ast::types::{
Binding, BindingContext, BindingKind, ClassScope, ImportKind, Range, Scope, ScopeKind,
Binding, BindingContext, BindingKind, ClassScope, FunctionScope, ImportKind, Range, Scope,
ScopeKind,
};
use crate::ast::visitor::{walk_excepthandler, Visitor};
use crate::ast::{helpers, operations, visitor};
use crate::autofix::fixer;
use crate::checks::{Check, CheckCode, CheckKind};
use crate::docstrings::definition::{Definition, DefinitionKind, Documentable};
use crate::python::builtins::{BUILTINS, MAGIC_GLOBALS};
@@ -37,15 +36,16 @@ 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;
#[allow(clippy::struct_excessive_bools)]
pub struct Checker<'a> {
// Input data.
path: &'a Path,
autofix: &'a fixer::Mode,
autofix: bool,
pub(crate) settings: &'a Settings,
pub(crate) locator: &'a SourceCodeLocator<'a>,
// Computed checks.
@@ -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,
@@ -85,7 +86,7 @@ pub struct Checker<'a> {
impl<'a> Checker<'a> {
pub fn new(
settings: &'a Settings,
autofix: &'a fixer::Mode,
autofix: bool,
path: &'a Path,
locator: &'a SourceCodeLocator,
) -> Checker<'a> {
@@ -94,39 +95,39 @@ impl<'a> Checker<'a> {
autofix,
path,
locator,
checks: Default::default(),
definitions: Default::default(),
deletions: Default::default(),
from_imports: Default::default(),
import_aliases: Default::default(),
parents: Default::default(),
parent_stack: Default::default(),
scopes: Default::default(),
scope_stack: Default::default(),
dead_scopes: Default::default(),
deferred_string_annotations: Default::default(),
deferred_annotations: Default::default(),
deferred_functions: Default::default(),
deferred_lambdas: Default::default(),
deferred_assignments: Default::default(),
checks: vec![],
definitions: vec![],
deletions: FxHashSet::default(),
from_imports: FxHashMap::default(),
import_aliases: FxHashMap::default(),
parents: vec![],
parent_stack: vec![],
scopes: vec![],
scope_stack: vec![],
dead_scopes: vec![],
deferred_string_annotations: vec![],
deferred_annotations: vec![],
deferred_functions: vec![],
deferred_lambdas: vec![],
deferred_assignments: vec![],
// Internal, derivative state.
visible_scope: VisibleScope {
modifier: Modifier::Module,
visibility: module_visibility(path),
},
in_f_string: Default::default(),
in_annotation: Default::default(),
in_literal: Default::default(),
in_subscript: Default::default(),
seen_import_boundary: Default::default(),
in_f_string: None,
in_annotation: false,
in_deferred_string_annotation: false,
in_literal: false,
in_subscript: false,
seen_import_boundary: false,
futures_allowed: true,
annotations_future_enabled: Default::default(),
except_handlers: Default::default(),
annotations_future_enabled: false,
except_handlers: vec![],
}
}
/// Add a `Check` to the `Checker`.
#[inline(always)]
pub(crate) fn add_check(&mut self, check: Check) {
// If we're in an f-string, override the location. RustPython doesn't produce
// reliable locations for expressions within f-strings, so we use the
@@ -143,7 +144,6 @@ impl<'a> Checker<'a> {
}
/// Add multiple `Check` items to the `Checker`.
#[inline(always)]
pub(crate) fn add_checks(&mut self, checks: impl Iterator<Item = Check>) {
for check in checks {
self.add_check(check);
@@ -152,10 +152,10 @@ impl<'a> Checker<'a> {
/// Return `true` if a patch should be generated under the given autofix
/// `Mode`.
pub fn patch(&self) -> bool {
pub fn patch(&self, code: &CheckCode) -> bool {
// TODO(charlie): We can't fix errors in f-strings until RustPython adds
// location data.
self.autofix.patch() && self.in_f_string.is_none()
self.autofix && self.in_f_string.is_none() && self.settings.fixable.contains(code)
}
/// Return `true` if the `Expr` is a reference to `typing.${target}`.
@@ -213,21 +213,74 @@ where
// Pre-visit.
match &stmt.node {
StmtKind::Global { names } | StmtKind::Nonlocal { names } => {
let global_scope_id = self.scopes[GLOBAL_SCOPE_INDEX].id;
let scope =
&mut self.scopes[*(self.scope_stack.last().expect("No current scope found."))];
if scope.id != global_scope_id {
StmtKind::Global { names } => {
let scope_index = *self.scope_stack.last().expect("No current scope found.");
if scope_index != GLOBAL_SCOPE_INDEX {
let scope = &mut self.scopes[scope_index];
let usage = Some((scope.id, Range::from_located(stmt)));
for name in names {
// Add a binding to the current scope.
scope.values.insert(
name,
Binding {
kind: BindingKind::Global,
used: Some((global_scope_id, Range::from_located(stmt))),
used: usage,
range: Range::from_located(stmt),
},
);
}
// Mark the binding in the global scope as used.
for name in names {
if let Some(mut existing) = self.scopes[GLOBAL_SCOPE_INDEX]
.values
.get_mut(&name.as_str())
{
existing.used = usage;
}
}
}
if self.settings.enabled.contains(&CheckCode::E741) {
let location = Range::from_located(stmt);
self.add_checks(
names
.iter()
.filter_map(|name| {
pycodestyle::checks::ambiguous_variable_name(name, location)
})
.into_iter(),
);
}
}
StmtKind::Nonlocal { names } => {
let scope_index = *self.scope_stack.last().expect("No current scope found.");
if scope_index != GLOBAL_SCOPE_INDEX {
let scope = &mut self.scopes[scope_index];
let usage = Some((scope.id, Range::from_located(stmt)));
for name in names {
// Add a binding to the current scope.
scope.values.insert(
name,
Binding {
kind: BindingKind::Global,
used: usage,
range: Range::from_located(stmt),
},
);
}
// Mark the binding in the defining scopes as used too. (Skip the global scope
// and the current scope.)
for name in names {
for index in self.scope_stack.iter().skip(1).rev().skip(1) {
if let Some(mut existing) =
self.scopes[*index].values.get_mut(&name.as_str())
{
existing.used = usage;
}
}
}
}
if self.settings.enabled.contains(&CheckCode::E741) {
@@ -418,7 +471,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
@@ -481,20 +534,20 @@ where
self.check_builtin_shadowing(name, Range::from_located(stmt), false);
for expr in bases {
self.visit_expr(expr)
self.visit_expr(expr);
}
for keyword in keywords {
self.visit_keyword(keyword)
self.visit_keyword(keyword);
}
for expr in decorator_list {
self.visit_expr(expr)
self.visit_expr(expr);
}
self.push_scope(Scope::new(ScopeKind::Class(ClassScope {
name,
bases,
keywords,
decorator_list,
})))
})));
}
StmtKind::Import { names } => {
if self.settings.enabled.contains(&CheckCode::E402) {
@@ -523,7 +576,7 @@ where
used: None,
range: Range::from_located(stmt),
},
)
);
} else {
if let Some(asname) = &alias.node.asname {
self.check_builtin_shadowing(asname, Range::from_located(stmt), false);
@@ -548,8 +601,7 @@ where
.node
.asname
.as_ref()
.map(|asname| asname == &alias.node.name)
.unwrap_or(false)
.map_or(false, |asname| asname == &alias.node.name)
{
Some((
self.scopes[*(self
@@ -564,7 +616,7 @@ where
},
range: Range::from_located(stmt),
},
)
);
}
if let Some(asname) = &alias.node.asname {
@@ -634,13 +686,13 @@ where
if let Some(module) = module {
self.from_imports
.entry(module)
.or_insert_with(FnvHashSet::default)
.or_insert_with(FxHashSet::default)
.extend(
names
.iter()
.filter(|alias| alias.node.asname.is_none())
.map(|alias| alias.node.name.as_str()),
)
);
}
for alias in names {
if let Some(asname) = &alias.node.asname {
@@ -689,7 +741,7 @@ where
}
if self.settings.enabled.contains(&CheckCode::F407) {
if !ALL_FEATURE_NAMES.contains(&alias.node.name.deref()) {
if !ALL_FEATURE_NAMES.contains(&&*alias.node.name) {
self.add_check(Check::new(
CheckKind::FutureFeatureNotDefined(alias.node.name.to_string()),
Range::from_located(stmt),
@@ -752,7 +804,7 @@ where
let name = alias.node.asname.as_ref().unwrap_or(&alias.node.name);
let full_name = match module {
None => alias.node.name.to_string(),
Some(parent) => format!("{}.{}", parent, alias.node.name),
Some(parent) => format!("{parent}.{}", alias.node.name),
};
self.add_binding(
name,
@@ -768,8 +820,7 @@ where
.node
.asname
.as_ref()
.map(|asname| asname == &alias.node.name)
.unwrap_or(false)
.map_or(false, |asname| asname == &alias.node.name)
{
Some((
self.scopes[*(self
@@ -784,7 +835,7 @@ where
},
range: Range::from_located(stmt),
},
)
);
}
if self.settings.enabled.contains(&CheckCode::I252) {
@@ -885,7 +936,7 @@ where
self,
stmt,
test,
msg.as_ref().map(|expr| expr.deref()),
msg.as_ref().map(|expr| &**expr),
);
}
if self.settings.enabled.contains(&CheckCode::S101) {
@@ -928,11 +979,7 @@ where
StmtKind::Assign { targets, value, .. } => {
if self.settings.enabled.contains(&CheckCode::E731) {
if let [target] = &targets[..] {
if let Some(check) =
pycodestyle::checks::do_not_assign_lambda(target, value, stmt)
{
self.add_check(check);
}
pycodestyle::plugins::do_not_assign_lambda(self, target, value, stmt);
}
}
if self.settings.enabled.contains(&CheckCode::U001) {
@@ -953,22 +1000,23 @@ 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) {
if let Some(value) = value {
if let Some(check) =
pycodestyle::checks::do_not_assign_lambda(target, value, stmt)
{
self.add_check(check);
}
pycodestyle::plugins::do_not_assign_lambda(self, target, value, stmt);
}
}
}
StmtKind::Delete { .. } => {}
StmtKind::Expr { value, .. } => {
if self.settings.enabled.contains(&CheckCode::B015) {
flake8_bugbear::plugins::useless_comparison(self, value)
flake8_bugbear::plugins::useless_comparison(self, value);
}
}
_ => {}
@@ -1033,7 +1081,7 @@ where
}
self.except_handlers.pop();
for excepthandler in handlers {
self.visit_excepthandler(excepthandler)
self.visit_excepthandler(excepthandler);
}
for stmt in orelse {
self.visit_stmt(stmt);
@@ -1100,7 +1148,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);
@@ -1138,7 +1187,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,
@@ -1256,7 +1306,7 @@ where
args,
keywords,
self.locator,
self.patch(),
self.patch(&CheckCode::C400),
Range::from_located(expr),
) {
self.add_check(check);
@@ -1270,7 +1320,7 @@ where
args,
keywords,
self.locator,
self.patch(),
self.patch(&CheckCode::C401),
Range::from_located(expr),
) {
self.add_check(check);
@@ -1284,7 +1334,7 @@ where
args,
keywords,
self.locator,
self.patch(),
self.patch(&CheckCode::C402),
Range::from_located(expr),
) {
self.add_check(check);
@@ -1299,7 +1349,7 @@ where
args,
keywords,
self.locator,
self.patch(),
self.patch(&CheckCode::C403),
Range::from_located(expr),
)
{
@@ -1315,7 +1365,7 @@ where
args,
keywords,
self.locator,
self.patch(),
self.patch(&CheckCode::C404),
Range::from_located(expr),
)
{
@@ -1330,7 +1380,7 @@ where
args,
keywords,
self.locator,
self.patch(),
self.patch(&CheckCode::C405),
Range::from_located(expr),
) {
self.add_check(check);
@@ -1344,7 +1394,7 @@ where
args,
keywords,
self.locator,
self.patch(),
self.patch(&CheckCode::C406),
Range::from_located(expr),
) {
self.add_check(check);
@@ -1358,7 +1408,7 @@ where
args,
keywords,
self.locator,
self.patch(),
self.patch(&CheckCode::C408),
Range::from_located(expr),
) {
self.add_check(check);
@@ -1372,7 +1422,7 @@ where
func,
args,
self.locator,
self.patch(),
self.patch(&CheckCode::C409),
Range::from_located(expr),
)
{
@@ -1387,7 +1437,7 @@ where
func,
args,
self.locator,
self.patch(),
self.patch(&CheckCode::C410),
Range::from_located(expr),
)
{
@@ -1401,7 +1451,7 @@ where
func,
args,
self.locator,
self.patch(),
self.patch(&CheckCode::C411),
Range::from_located(expr),
) {
self.add_check(check);
@@ -1415,7 +1465,7 @@ where
func,
args,
self.locator,
self.patch(),
self.patch(&CheckCode::C413),
Range::from_located(expr),
)
{
@@ -1462,6 +1512,10 @@ where
pyupgrade::plugins::type_of_primitive(self, expr, func, args);
}
if self.settings.enabled.contains(&CheckCode::U015) {
pyupgrade::plugins::redundant_open_modes(self, expr);
}
// flake8-boolean-trap
if self.settings.enabled.contains(&CheckCode::FBT003) {
flake8_boolean_trap::plugins::check_boolean_positional_value_in_function_call(
@@ -1477,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);
@@ -1561,7 +1620,7 @@ where
comparators,
check_none_comparisons,
check_true_false_comparisons,
)
);
}
if self.settings.enabled.contains(&CheckCode::F632) {
@@ -1658,7 +1717,7 @@ where
for expr in &args.defaults {
self.visit_expr(expr);
}
self.push_scope(Scope::new(ScopeKind::Lambda))
self.push_scope(Scope::new(ScopeKind::Lambda));
}
ExprKind::ListComp { elt, generators } | ExprKind::SetComp { elt, generators } => {
if self.settings.enabled.contains(&CheckCode::C416) {
@@ -1667,16 +1726,16 @@ where
elt,
generators,
self.locator,
self.patch(),
self.patch(&CheckCode::C416),
Range::from_located(expr),
) {
self.add_check(check);
};
}
self.push_scope(Scope::new(ScopeKind::Generator))
self.push_scope(Scope::new(ScopeKind::Generator));
}
ExprKind::GeneratorExp { .. } | ExprKind::DictComp { .. } => {
self.push_scope(Scope::new(ScopeKind::Generator))
self.push_scope(Scope::new(ScopeKind::Generator));
}
_ => {}
};
@@ -1835,7 +1894,7 @@ where
error!(
"Found non-ExprKind::Tuple argument to PEP 593 \
Annotation."
)
);
}
}
}
@@ -1955,10 +2014,10 @@ where
.extend(pyflakes::checks::duplicate_arguments(arguments));
}
if self.settings.enabled.contains(&CheckCode::B006) {
flake8_bugbear::plugins::mutable_argument_default(self, arguments)
flake8_bugbear::plugins::mutable_argument_default(self, arguments);
}
if self.settings.enabled.contains(&CheckCode::B008) {
flake8_bugbear::plugins::function_call_argument_default(self, arguments)
flake8_bugbear::plugins::function_call_argument_default(self, arguments);
}
// flake8-boolean-trap
@@ -2098,7 +2157,7 @@ impl<'a> Checker<'a> {
builtin,
Binding {
kind: BindingKind::Builtin,
range: Default::default(),
range: Range::default(),
used: None,
},
);
@@ -2108,7 +2167,7 @@ impl<'a> Checker<'a> {
builtin,
Binding {
kind: BindingKind::Builtin,
range: Default::default(),
range: Range::default(),
used: None,
},
);
@@ -2119,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."))]
}
@@ -2126,7 +2189,7 @@ impl<'a> Checker<'a> {
pub fn binding_context(&self) -> BindingContext {
let mut rev = self.parent_stack.iter().rev().fuse();
let defined_by = *rev.next().expect("Expected to bind within a statement.");
let defined_in = rev.next().cloned();
let defined_in = rev.next().copied();
BindingContext {
defined_by,
defined_in,
@@ -2138,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!(
@@ -2163,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 {
@@ -2179,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;
@@ -2248,7 +2310,7 @@ impl<'a> Checker<'a> {
self.add_check(Check::new(
CheckKind::UndefinedName(id.clone()),
Range::from_located(expr),
))
));
}
}
}
@@ -2275,23 +2337,24 @@ impl<'a> Checker<'a> {
.current_scope()
.values
.get(id)
.map(|binding| matches!(binding.kind, BindingKind::Global))
.unwrap_or(false)
.map_or(false, |binding| matches!(binding.kind, BindingKind::Global))
{
pep8_naming::plugins::non_lowercase_variable_in_function(self, expr, parent, id)
pep8_naming::plugins::non_lowercase_variable_in_function(
self, expr, parent, id,
);
}
}
}
if self.settings.enabled.contains(&CheckCode::N815) {
if matches!(self.current_scope().kind, ScopeKind::Class(..)) {
pep8_naming::plugins::mixed_case_variable_in_class_scope(self, expr, parent, id)
pep8_naming::plugins::mixed_case_variable_in_class_scope(self, expr, parent, id);
}
}
if self.settings.enabled.contains(&CheckCode::N816) {
if matches!(self.current_scope().kind, ScopeKind::Module) {
pep8_naming::plugins::mixed_case_variable_in_global_scope(self, expr, parent, id)
pep8_naming::plugins::mixed_case_variable_in_global_scope(self, expr, parent, id);
}
}
@@ -2335,7 +2398,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!(
@@ -2387,7 +2450,7 @@ impl<'a> Checker<'a> {
self.add_check(Check::new(
CheckKind::UndefinedName(id.to_string()),
Range::from_located(expr),
))
));
}
}
}
@@ -2444,9 +2507,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;
}
}
@@ -2455,7 +2520,7 @@ impl<'a> Checker<'a> {
self.parent_stack = parents;
self.scope_stack = scopes;
self.visible_scope = visibility;
self.push_scope(Scope::new(ScopeKind::Function(Default::default())));
self.push_scope(Scope::new(ScopeKind::Function(FunctionScope::default())));
match &stmt.node {
StmtKind::FunctionDef { body, args, .. }
@@ -2520,9 +2585,7 @@ impl<'a> Checker<'a> {
let all_binding: Option<&Binding> = scope.values.get("__all__");
let all_names: Option<Vec<&str>> =
all_binding.and_then(|binding| match &binding.kind {
BindingKind::Export(names) => {
Some(names.iter().map(|name| name.as_str()).collect())
}
BindingKind::Export(names) => Some(names.iter().map(String::as_str).collect()),
_ => None,
});
@@ -2530,7 +2593,7 @@ impl<'a> Checker<'a> {
if !scope.import_starred && !self.path.ends_with("__init__.py") {
if let Some(all_binding) = all_binding {
if let Some(names) = &all_names {
for name in names {
for &name in names {
if !scope.values.contains_key(name) {
checks.push(Check::new(
CheckKind::UndefinedExport(name.to_string()),
@@ -2558,7 +2621,7 @@ impl<'a> Checker<'a> {
}
from_list.sort();
for name in names {
for &name in names {
if !scope.values.contains_key(name) {
checks.push(Check::new(
CheckKind::ImportStarUsage(
@@ -2580,7 +2643,7 @@ impl<'a> Checker<'a> {
let mut unused: BTreeMap<(ImportKind, usize, Option<usize>), Vec<&str>> =
BTreeMap::new();
for (name, binding) in scope.values.iter() {
for (name, binding) in &scope.values {
if !matches!(
binding.kind,
BindingKind::Importation(..)
@@ -2628,7 +2691,7 @@ impl<'a> Checker<'a> {
let child = self.parents[defined_by];
let parent = defined_in.map(|defined_in| self.parents[defined_in]);
let fix = if self.patch() {
let fix = if self.patch(&CheckCode::F401) {
let deleted: Vec<&Stmt> = self
.deletions
.iter()
@@ -2824,7 +2887,7 @@ pub fn check_ast(
python_ast: &Suite,
locator: &SourceCodeLocator,
settings: &Settings,
autofix: &fixer::Mode,
autofix: bool,
path: &Path,
) -> Vec<Check> {
let mut checker = Checker::new(settings, autofix, path, locator);

View File

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

View File

@@ -6,7 +6,7 @@ use regex::Regex;
use rustpython_parser::ast::Location;
use crate::ast::types::Range;
use crate::autofix::{fixer, Fix};
use crate::autofix::Fix;
use crate::checks::{Check, CheckCode, CheckKind};
use crate::noqa;
use crate::noqa::Directive;
@@ -37,7 +37,7 @@ pub fn check_lines(
contents: &str,
noqa_line_for: &IntMap<usize, usize>,
settings: &Settings,
autofix: &fixer::Mode,
autofix: bool,
) {
let enforce_unnecessary_coding_comment = settings.enabled.contains(&CheckCode::U009);
let enforce_line_too_long = settings.enabled.contains(&CheckCode::E501);
@@ -73,7 +73,7 @@ pub fn check_lines(
end_location: Location::new(lineno + 1, line_length + 1),
},
);
if autofix.patch() {
if autofix && settings.fixable.contains(check.kind.code()) {
check.amend(Fix::deletion(
Location::new(lineno + 1, 0),
Location::new(lineno + 1, line_length + 1),
@@ -101,9 +101,9 @@ pub fn check_lines(
match noqa {
(Directive::All(..), matches) => {
matches.push(check.kind.code().as_ref());
ignored.push(index)
ignored.push(index);
}
(Directive::Codes(_, _, codes), matches) => {
(Directive::Codes(.., codes), matches) => {
if codes.contains(&check.kind.code().as_ref()) {
matches.push(check.kind.code().as_ref());
ignored.push(index);
@@ -133,7 +133,7 @@ pub fn check_lines(
(Directive::All(..), matches) => {
matches.push(check.kind.code().as_ref());
}
(Directive::Codes(_, _, codes), matches) => {
(Directive::Codes(.., codes), matches) => {
if codes.contains(&check.kind.code().as_ref()) {
matches.push(check.kind.code().as_ref());
} else {
@@ -170,7 +170,7 @@ pub fn check_lines(
(Directive::All(..), matches) => {
matches.push(check.kind.code().as_ref());
}
(Directive::Codes(_, _, codes), matches) => {
(Directive::Codes(.., codes), matches) => {
if codes.contains(&check.kind.code().as_ref()) {
matches.push(check.kind.code().as_ref());
} else {
@@ -186,7 +186,7 @@ pub fn check_lines(
if enforce_noqa {
for (row, (directive, matches)) in noqa_directives {
match directive {
Directive::All(start, end) => {
Directive::All(spaces, start, end) => {
if matches.is_empty() {
let mut check = Check::new(
CheckKind::UnusedNOQA(None),
@@ -195,23 +195,23 @@ pub fn check_lines(
end_location: Location::new(row + 1, end),
},
);
if autofix.patch() {
if autofix && settings.fixable.contains(check.kind.code()) {
check.amend(Fix::deletion(
Location::new(row + 1, start),
Location::new(row + 1, start - spaces),
Location::new(row + 1, lines[row].chars().count()),
));
}
line_checks.push(check);
}
}
Directive::Codes(start, end, codes) => {
Directive::Codes(spaces, start, end, codes) => {
let mut invalid_codes = vec![];
let mut valid_codes = vec![];
for code in codes {
if !matches.contains(&code) {
invalid_codes.push(code.to_string());
} else {
if matches.contains(&code) {
valid_codes.push(code.to_string());
} else {
invalid_codes.push(code.to_string());
}
}
@@ -223,15 +223,15 @@ pub fn check_lines(
end_location: Location::new(row + 1, end),
},
);
if autofix.patch() {
if autofix && settings.fixable.contains(check.kind.code()) {
if valid_codes.is_empty() {
check.amend(Fix::deletion(
Location::new(row + 1, start),
Location::new(row + 1, start - spaces),
Location::new(row + 1, lines[row].chars().count()),
));
} else {
check.amend(Fix::replacement(
format!(" # noqa: {}", valid_codes.join(", ")),
format!("# noqa: {}", valid_codes.join(", ")),
Location::new(row + 1, start),
Location::new(row + 1, lines[row].chars().count()),
));
@@ -245,7 +245,7 @@ pub fn check_lines(
}
}
ignored.sort();
ignored.sort_unstable();
for index in ignored.iter().rev() {
checks.swap_remove(*index);
}
@@ -257,14 +257,13 @@ mod tests {
use nohash_hasher::IntMap;
use super::check_lines;
use crate::autofix::fixer;
use crate::checks::{Check, CheckCode};
use crate::settings::Settings;
#[test]
fn e501_non_ascii_char() {
let line = "'\u{4e9c}' * 2"; // 7 in UTF-32, 9 in UTF-8.
let noqa_line_for: IntMap<usize, usize> = Default::default();
let noqa_line_for: IntMap<usize, usize> = IntMap::default();
let check_with_max_line_length = |line_length: usize| {
let mut checks: Vec<Check> = vec![];
check_lines(
@@ -275,7 +274,7 @@ mod tests {
line_length,
..Settings::for_rule(CheckCode::E501)
},
&fixer::Mode::Generate,
true,
);
checks
};

View File

@@ -2,7 +2,6 @@
use rustpython_parser::lexer::{LexResult, Tok};
use crate::autofix::fixer;
use crate::checks::{Check, CheckCode};
use crate::lex::docstring_detection::StateMachine;
use crate::rules::checks::Context;
@@ -10,12 +9,13 @@ use crate::source_code_locator::SourceCodeLocator;
use crate::{flake8_quotes, pycodestyle, rules, Settings};
pub fn check_tokens(
checks: &mut Vec<Check>,
locator: &SourceCodeLocator,
tokens: &[LexResult],
settings: &Settings,
autofix: &fixer::Mode,
) {
autofix: bool,
) -> Vec<Check> {
let mut checks: Vec<Check> = vec![];
let enforce_ambiguous_unicode_character = settings.enabled.contains(&CheckCode::RUF001)
|| settings.enabled.contains(&CheckCode::RUF002)
|| settings.enabled.contains(&CheckCode::RUF003);
@@ -25,8 +25,8 @@ pub fn check_tokens(
|| settings.enabled.contains(&CheckCode::Q003);
let enforce_invalid_escape_sequence = settings.enabled.contains(&CheckCode::W605);
let mut state_machine: StateMachine = Default::default();
for (start, tok, end) in tokens.iter().flatten() {
let mut state_machine = StateMachine::default();
for &(start, ref tok, end) in tokens.iter().flatten() {
let is_docstring = if enforce_ambiguous_unicode_character || enforce_quotes {
state_machine.consume(tok)
} else {
@@ -36,7 +36,7 @@ pub fn check_tokens(
// RUF001, RUF002, RUF003
if enforce_ambiguous_unicode_character {
if matches!(tok, Tok::String { .. } | Tok::Comment) {
for check in rules::checks::ambiguous_unicode_character(
checks.extend(rules::checks::ambiguous_unicode_character(
locator,
start,
end,
@@ -49,12 +49,9 @@ pub fn check_tokens(
} else {
Context::Comment
},
autofix.patch(),
) {
if settings.enabled.contains(check.kind.code()) {
checks.push(check);
}
}
settings,
autofix,
));
}
}
@@ -84,4 +81,6 @@ pub fn check_tokens(
}
}
}
checks
}

View File

@@ -170,6 +170,8 @@ pub enum CheckCode {
U011,
U012,
U013,
U014,
U015,
// pydocstyle
D100,
D101,
@@ -248,6 +250,7 @@ pub enum CheckCode {
RUF001,
RUF002,
RUF003,
RUF101,
// Meta
M001,
}
@@ -500,7 +503,9 @@ pub enum CheckKind {
UnnecessaryFutureImport(Vec<String>),
UnnecessaryLRUCacheParams,
UnnecessaryEncodeUTF8,
ConvertTypedDictFunctionalToClass,
ConvertTypedDictFunctionalToClass(String),
ConvertNamedTupleFunctionalToClass(String),
RedundantOpenModes,
// pydocstyle
BlankLineAfterLastSection(String),
BlankLineAfterSection(String),
@@ -581,6 +586,7 @@ pub enum CheckKind {
AmbiguousUnicodeCharacterString(char, char),
AmbiguousUnicodeCharacterDocstring(char, char),
AmbiguousUnicodeCharacterComment(char, char),
ConvertExitToSysExit,
// Meta
UnusedNOQA(Option<Vec<String>>),
}
@@ -607,7 +613,7 @@ impl CheckCode {
}
}
/// A placeholder representation of the CheckKind for the check.
/// A placeholder representation of the `CheckKind` for the check.
pub fn kind(&self) -> CheckKind {
match self {
// pycodestyle errors
@@ -773,7 +779,9 @@ 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()),
CheckCode::U015 => CheckKind::RedundantOpenModes,
// pydocstyle
CheckCode::D100 => CheckKind::PublicModule,
CheckCode::D101 => CheckKind::PublicClass,
@@ -869,12 +877,14 @@ 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),
}
}
pub fn category(&self) -> CheckCategory {
#[allow(clippy::match_same_arms)]
match self {
CheckCode::E402 => CheckCategory::Pycodestyle,
CheckCode::E501 => CheckCategory::Pycodestyle,
@@ -1005,6 +1015,8 @@ impl CheckCode {
CheckCode::U011 => CheckCategory::Pyupgrade,
CheckCode::U012 => CheckCategory::Pyupgrade,
CheckCode::U013 => CheckCategory::Pyupgrade,
CheckCode::U014 => CheckCategory::Pyupgrade,
CheckCode::U015 => CheckCategory::Pyupgrade,
CheckCode::D100 => CheckCategory::Pydocstyle,
CheckCode::D101 => CheckCategory::Pydocstyle,
CheckCode::D102 => CheckCategory::Pydocstyle,
@@ -1078,6 +1090,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 +1240,9 @@ 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,
CheckKind::RedundantOpenModes => &CheckCode::U015,
// pydocstyle
CheckKind::BlankLineAfterLastSection(_) => &CheckCode::D413,
CheckKind::BlankLineAfterSection(_) => &CheckCode::D410,
@@ -1308,6 +1323,7 @@ impl CheckKind {
CheckKind::AmbiguousUnicodeCharacterString(..) => &CheckCode::RUF001,
CheckKind::AmbiguousUnicodeCharacterDocstring(..) => &CheckCode::RUF002,
CheckKind::AmbiguousUnicodeCharacterComment(..) => &CheckCode::RUF003,
CheckKind::ConvertExitToSysExit => &CheckCode::RUF101,
// Meta
CheckKind::UnusedNOQA(_) => &CheckCode::M001,
}
@@ -1318,13 +1334,13 @@ impl CheckKind {
match self {
// pycodestyle errors
CheckKind::AmbiguousClassName(name) => {
format!("Ambiguous class name: `{}`", name)
format!("Ambiguous class name: `{name}`")
}
CheckKind::AmbiguousFunctionName(name) => {
format!("Ambiguous function name: `{}`", name)
format!("Ambiguous function name: `{name}`")
}
CheckKind::AmbiguousVariableName(name) => {
format!("Ambiguous variable name: `{}`", name)
format!("Ambiguous variable name: `{name}`")
}
CheckKind::AssertTuple => {
"Assert test is a non-empty tuple, which is always `True`".to_string()
@@ -1367,7 +1383,7 @@ impl CheckKind {
CheckKind::ImportStarUsage(name, sources) => {
let sources = sources
.iter()
.map(|source| format!("`{}`", source))
.map(|source| format!("`{source}`"))
.join(", ");
format!("`{name}` may be undefined, or defined from star imports: {sources}")
}
@@ -1405,24 +1421,18 @@ impl CheckKind {
CheckKind::ExpressionsInStarAssignment => {
"Too many expressions in star-unpacking assignment".to_string()
}
CheckKind::TrueFalseComparison(value, op) => match *value {
true => match op {
RejectedCmpop::Eq => {
"Comparison to `True` should be `cond is True`".to_string()
}
RejectedCmpop::NotEq => {
"Comparison to `True` should be `cond is not True`".to_string()
}
},
false => match op {
RejectedCmpop::Eq => {
"Comparison to `False` should be `cond is False`".to_string()
}
RejectedCmpop::NotEq => {
"Comparison to `False` should be `cond is not False`".to_string()
}
},
},
CheckKind::TrueFalseComparison(true, RejectedCmpop::Eq) => {
"Comparison to `True` should be `cond is True`".to_string()
}
CheckKind::TrueFalseComparison(true, RejectedCmpop::NotEq) => {
"Comparison to `True` should be `cond is not True`".to_string()
}
CheckKind::TrueFalseComparison(false, RejectedCmpop::Eq) => {
"Comparison to `False` should be `cond is False`".to_string()
}
CheckKind::TrueFalseComparison(false, RejectedCmpop::NotEq) => {
"Comparison to `False` should be `cond is not False`".to_string()
}
CheckKind::TwoStarredExpressions => "Two starred expressions in assignment".to_string(),
CheckKind::TypeComparison => "Do not compare types, use `isinstance()`".to_string(),
CheckKind::UndefinedExport(name) => {
@@ -1776,8 +1786,12 @@ 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::RedundantOpenModes => "Unnecessary open mode parameters".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 +2008,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(),
@@ -2038,46 +2055,56 @@ impl CheckKind {
pub fn fixable(&self) -> bool {
matches!(
self,
CheckKind::AmbiguousUnicodeCharacterString(_, _)
| CheckKind::AmbiguousUnicodeCharacterDocstring(_, _)
| CheckKind::BlankLineAfterLastSection(_)
| CheckKind::BlankLineAfterSection(_)
CheckKind::AmbiguousUnicodeCharacterString(..)
| CheckKind::AmbiguousUnicodeCharacterDocstring(..)
| CheckKind::BlankLineAfterLastSection(..)
| CheckKind::BlankLineAfterSection(..)
| CheckKind::BlankLineAfterSummary
| CheckKind::BlankLineBeforeSection(_)
| CheckKind::CapitalizeSectionName(_)
| CheckKind::DashedUnderlineAfterSection(_)
| CheckKind::DeprecatedUnittestAlias(_, _)
| CheckKind::BlankLineBeforeSection(..)
| CheckKind::CapitalizeSectionName(..)
| CheckKind::ConvertExitToSysExit
| CheckKind::ConvertNamedTupleFunctionalToClass(..)
| CheckKind::ConvertTypedDictFunctionalToClass(..)
| CheckKind::DashedUnderlineAfterSection(..)
| CheckKind::DeprecatedUnittestAlias(..)
| CheckKind::DoNotAssertFalse
| CheckKind::DuplicateHandlerException(_)
| CheckKind::DoNotAssignLambda
| CheckKind::DuplicateHandlerException(..)
| CheckKind::GetAttrWithConstant
| CheckKind::IsLiteral
| CheckKind::NewLineAfterLastParagraph
| CheckKind::NewLineAfterSectionName(_)
| CheckKind::NoBlankLineAfterFunction(_)
| CheckKind::NoBlankLineBeforeClass(_)
| CheckKind::NoBlankLineBeforeFunction(_)
| CheckKind::NoBlankLinesBetweenHeaderAndContent(_)
| CheckKind::NewLineAfterSectionName(..)
| CheckKind::NoBlankLineAfterFunction(..)
| CheckKind::NoBlankLineBeforeClass(..)
| CheckKind::NoBlankLineBeforeFunction(..)
| CheckKind::NoBlankLinesBetweenHeaderAndContent(..)
| CheckKind::NoOverIndentation
| CheckKind::NoSurroundingWhitespace
| CheckKind::NoUnderIndentation
| CheckKind::OneBlankLineAfterClass(_)
| CheckKind::OneBlankLineBeforeClass(_)
| CheckKind::NoneComparison(..)
| CheckKind::NotInTest
| CheckKind::NotIsTest
| CheckKind::OneBlankLineAfterClass(..)
| CheckKind::OneBlankLineBeforeClass(..)
| CheckKind::PEP3120UnnecessaryCodingComment
| CheckKind::PPrintFound
| CheckKind::PrintFound
| CheckKind::RaiseNotImplemented
| CheckKind::SectionNameEndsInColon(_)
| CheckKind::SectionNotOverIndented(_)
| CheckKind::SectionUnderlineAfterName(_)
| CheckKind::SectionUnderlineMatchesSectionLength(_)
| CheckKind::SectionUnderlineNotOverIndented(_)
| CheckKind::RedundantTupleInExceptionHandler(..)
| CheckKind::SectionNameEndsInColon(..)
| CheckKind::SectionNotOverIndented(..)
| CheckKind::SectionUnderlineAfterName(..)
| CheckKind::SectionUnderlineMatchesSectionLength(..)
| CheckKind::SectionUnderlineNotOverIndented(..)
| CheckKind::SetAttrWithConstant
| CheckKind::SuperCallWithParameters
| CheckKind::TypeOfPrimitive(_)
| CheckKind::UnnecessaryCollectionCall(_)
| CheckKind::UnnecessaryComprehension(_)
| CheckKind::TrueFalseComparison(..)
| CheckKind::TypeOfPrimitive(..)
| CheckKind::UnnecessaryCollectionCall(..)
| CheckKind::UnnecessaryComprehension(..)
| CheckKind::UnnecessaryEncodeUTF8
| CheckKind::ConvertTypedDictFunctionalToClass
| CheckKind::UnnecessaryFutureImport(_)
| CheckKind::UnnecessaryFutureImport(..)
| CheckKind::RedundantOpenModes
| CheckKind::UnnecessaryGeneratorDict
| CheckKind::UnnecessaryGeneratorList
| CheckKind::UnnecessaryGeneratorSet
@@ -2085,18 +2112,18 @@ impl CheckKind {
| CheckKind::UnnecessaryListCall
| CheckKind::UnnecessaryListComprehensionDict
| CheckKind::UnnecessaryListComprehensionSet
| CheckKind::UnnecessaryLiteralDict(_)
| CheckKind::UnnecessaryLiteralSet(_)
| CheckKind::UnnecessaryLiteralWithinListCall(_)
| CheckKind::UnnecessaryLiteralWithinTupleCall(_)
| CheckKind::UnnecessaryLiteralDict(..)
| CheckKind::UnnecessaryLiteralSet(..)
| CheckKind::UnnecessaryLiteralWithinListCall(..)
| CheckKind::UnnecessaryLiteralWithinTupleCall(..)
| CheckKind::UnsortedImports
| CheckKind::UnusedImport(_, false)
| CheckKind::UnusedLoopControlVariable(_)
| CheckKind::UnusedNOQA(_)
| CheckKind::UsePEP585Annotation(_)
| CheckKind::UnusedLoopControlVariable(..)
| CheckKind::UnusedNOQA(..)
| CheckKind::UsePEP585Annotation(..)
| CheckKind::UsePEP604Annotation
| CheckKind::UselessMetaclassType
| CheckKind::UselessObjectInheritance(_)
| CheckKind::UselessObjectInheritance(..)
)
}
}
@@ -2128,13 +2155,12 @@ impl Check {
mod tests {
use std::str::FromStr;
use anyhow::Result;
use strum::IntoEnumIterator;
use crate::checks::CheckCode;
#[test]
fn check_code_serialization() -> Result<()> {
fn check_code_serialization() {
for check_code in CheckCode::iter() {
assert!(
CheckCode::from_str(check_code.as_ref()).is_ok(),
@@ -2142,6 +2168,5 @@ mod tests {
check_code
);
}
Ok(())
}
}

View File

@@ -1,4 +1,4 @@
//! File automatically generated by examples/generate_check_code_prefix.rs.
//! File automatically generated by `examples/generate_check_code_prefix.rs`.
use serde::{Deserialize, Serialize};
use strum_macros::EnumString;
@@ -267,6 +267,9 @@ pub enum CheckCodePrefix {
RUF001,
RUF002,
RUF003,
RUF1,
RUF10,
RUF101,
S,
S1,
S10,
@@ -297,6 +300,8 @@ pub enum CheckCodePrefix {
U011,
U012,
U013,
U014,
U015,
W,
W2,
W29,
@@ -333,6 +338,7 @@ pub enum PrefixSpecificity {
impl CheckCodePrefix {
pub fn codes(&self) -> Vec<CheckCode> {
#[allow(clippy::match_same_arms)]
match self {
CheckCodePrefix::A => vec![CheckCode::A001, CheckCode::A002, CheckCode::A003],
CheckCodePrefix::A0 => vec![CheckCode::A001, CheckCode::A002, CheckCode::A003],
@@ -1057,12 +1063,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 +1125,8 @@ impl CheckCodePrefix {
CheckCode::U011,
CheckCode::U012,
CheckCode::U013,
CheckCode::U014,
CheckCode::U015,
],
CheckCodePrefix::U0 => vec![
CheckCode::U001,
@@ -1125,6 +1141,8 @@ impl CheckCodePrefix {
CheckCode::U011,
CheckCode::U012,
CheckCode::U013,
CheckCode::U014,
CheckCode::U015,
],
CheckCodePrefix::U00 => vec![
CheckCode::U001,
@@ -1149,11 +1167,15 @@ impl CheckCodePrefix {
CheckCode::U011,
CheckCode::U012,
CheckCode::U013,
CheckCode::U014,
CheckCode::U015,
],
CheckCodePrefix::U010 => vec![CheckCode::U010],
CheckCodePrefix::U011 => vec![CheckCode::U011],
CheckCodePrefix::U012 => vec![CheckCode::U012],
CheckCodePrefix::U013 => vec![CheckCode::U013],
CheckCodePrefix::U014 => vec![CheckCode::U014],
CheckCodePrefix::U015 => vec![CheckCode::U015],
CheckCodePrefix::W => vec![CheckCode::W292, CheckCode::W605],
CheckCodePrefix::W2 => vec![CheckCode::W292],
CheckCodePrefix::W29 => vec![CheckCode::W292],
@@ -1205,6 +1227,7 @@ impl CheckCodePrefix {
impl CheckCodePrefix {
pub fn specificity(&self) -> PrefixSpecificity {
#[allow(clippy::match_same_arms)]
match self {
CheckCodePrefix::A => PrefixSpecificity::Category,
CheckCodePrefix::A0 => PrefixSpecificity::Hundreds,
@@ -1466,6 +1489,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 +1522,8 @@ impl CheckCodePrefix {
CheckCodePrefix::U011 => PrefixSpecificity::Explicit,
CheckCodePrefix::U012 => PrefixSpecificity::Explicit,
CheckCodePrefix::U013 => PrefixSpecificity::Explicit,
CheckCodePrefix::U014 => PrefixSpecificity::Explicit,
CheckCodePrefix::U015 => PrefixSpecificity::Explicit,
CheckCodePrefix::W => PrefixSpecificity::Category,
CheckCodePrefix::W2 => PrefixSpecificity::Hundreds,
CheckCodePrefix::W29 => PrefixSpecificity::Tens,

View File

@@ -1,20 +1,20 @@
use std::fmt;
use std::path::PathBuf;
use anyhow::Result;
use clap::{command, Parser};
use fnv::FnvHashMap;
use log::warn;
use regex::Regex;
use rustc_hash::FxHashMap;
use crate::checks::CheckCode;
use crate::checks_gen::CheckCodePrefix;
use crate::logging::LogLevel;
use crate::printer::SerializationFormat;
use crate::settings::configuration::Configuration;
use crate::settings::types::{PatternPrefixPair, PerFileIgnore, PythonVersion};
#[derive(Debug, Parser)]
#[command(author, about = "ruff: An extremely fast Python linter.")]
#[command(author, about = "Ruff: An extremely fast Python linter.")]
#[command(version)]
#[allow(clippy::struct_excessive_bools)]
pub struct Cli {
#[arg(required = true)]
pub files: Vec<PathBuf>,
@@ -66,16 +66,27 @@ pub struct Cli {
/// excluded ones.
#[arg(long, value_delimiter = ',')]
pub extend_exclude: Vec<String>,
/// List of error codes to treat as eligible for autofix. Only applicable
/// when autofix itself is enabled (e.g., via `--fix`).
#[arg(long, value_delimiter = ',')]
pub fixable: Vec<CheckCodePrefix>,
/// List of error codes to treat as ineligible for autofix. Only applicable
/// when autofix itself is enabled (e.g., via `--fix`).
#[arg(long, value_delimiter = ',')]
pub unfixable: Vec<CheckCodePrefix>,
/// List of mappings from file pattern to code to exclude
#[arg(long, value_delimiter = ',')]
pub per_file_ignores: Vec<PatternPrefixPair>,
/// Output serialization format for error messages.
#[arg(long, value_enum, default_value_t=SerializationFormat::Text)]
pub format: SerializationFormat,
/// See the files ruff will be run against with the current settings.
/// Show violations with source code.
#[arg(long)]
pub show_source: bool,
/// See the files Ruff will be run against with the current settings.
#[arg(long)]
pub show_files: bool,
/// See ruff's settings.
/// See Ruff's settings.
#[arg(long)]
pub show_settings: bool,
/// Enable automatic additions of noqa directives to failing lines.
@@ -101,6 +112,9 @@ pub struct Cli {
/// The name of the file when passing it through stdin.
#[arg(long)]
pub stdin_filename: Option<String>,
/// Explain a rule.
#[arg(long)]
pub explain: Option<CheckCode>,
}
impl Cli {
@@ -134,70 +148,12 @@ pub fn extract_log_level(cli: &Cli) -> LogLevel {
}
}
pub enum Warnable {
Select,
ExtendSelect,
}
impl fmt::Display for Warnable {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self {
Warnable::Select => fmt.write_str("--select"),
Warnable::ExtendSelect => fmt.write_str("--extend-select"),
}
}
}
/// Warn the user if they attempt to enable a code that won't be respected.
pub fn warn_on(
flag: Warnable,
codes: &[CheckCodePrefix],
cli_ignore: &[CheckCodePrefix],
cli_extend_ignore: &[CheckCodePrefix],
pyproject_configuration: &Configuration,
pyproject_path: Option<&PathBuf>,
) {
for code in codes {
if !cli_ignore.is_empty() {
if cli_ignore.contains(code) {
warn!("{code:?} was passed to {flag}, but ignored via --ignore")
}
} else if pyproject_configuration.ignore.contains(code) {
if let Some(path) = pyproject_path {
warn!(
"{code:?} was passed to {flag}, but ignored by the `ignore` field in {}",
path.to_string_lossy()
)
} else {
warn!("{code:?} was passed to {flag}, but ignored by the default `ignore` field",)
}
}
if !cli_extend_ignore.is_empty() {
if cli_extend_ignore.contains(code) {
warn!("{code:?} was passed to {flag}, but ignored via --extend-ignore")
}
} else if pyproject_configuration.extend_ignore.contains(code) {
if let Some(path) = pyproject_path {
warn!(
"{code:?} was passed to {flag}, but ignored by the `extend_ignore` field in {}",
path.to_string_lossy()
)
} else {
warn!(
"{code:?} was passed to {flag}, but ignored by the default `extend_ignore` \
field"
)
}
}
}
}
/// Convert a list of `PatternPrefixPair` structs to `PerFileIgnore`.
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)

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -1,10 +1,10 @@
//! Abstractions for Google-style docstrings.
use fnv::FnvHashSet;
use once_cell::sync::Lazy;
use rustc_hash::FxHashSet;
pub(crate) static GOOGLE_SECTION_NAMES: Lazy<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,7 +1,7 @@
pub mod constants;
pub mod definition;
pub mod extraction;
pub mod google;
pub mod helpers;
pub mod numpy;
pub mod sections;
pub mod styles;

View File

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

View File

@@ -1,5 +1,5 @@
use fnv::FnvHashSet;
use once_cell::sync::Lazy;
use rustc_hash::FxHashSet;
use crate::docstrings::google::{GOOGLE_SECTION_NAMES, LOWERCASE_GOOGLE_SECTION_NAMES};
use crate::docstrings::numpy::{LOWERCASE_NUMPY_SECTION_NAMES, NUMPY_SECTION_NAMES};
@@ -10,14 +10,14 @@ pub(crate) enum SectionStyle {
}
impl SectionStyle {
pub(crate) fn section_names(&self) -> &Lazy<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

@@ -7,7 +7,6 @@ mod tests {
use anyhow::Result;
use crate::autofix::fixer;
use crate::checks::CheckCode;
use crate::linter::test_path;
use crate::{flake8_annotations, Settings};
@@ -31,7 +30,7 @@ mod tests {
CheckCode::ANN401,
])
},
&fixer::Mode::Generate,
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(checks);
@@ -57,7 +56,7 @@ mod tests {
CheckCode::ANN102,
])
},
&fixer::Mode::Generate,
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(checks);
@@ -83,7 +82,7 @@ mod tests {
CheckCode::ANN206,
])
},
&fixer::Mode::Generate,
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(checks);
@@ -109,7 +108,7 @@ mod tests {
CheckCode::ANN206,
])
},
&fixer::Mode::Generate,
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(checks);
@@ -129,7 +128,7 @@ mod tests {
},
..Settings::for_rules(vec![CheckCode::ANN401])
},
&fixer::Mode::Generate,
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(checks);

View File

@@ -1,5 +1,3 @@
use std::ops::Deref;
use rustpython_ast::{Arguments, Constant, Expr, ExprKind, Stmt, StmtKind};
use crate::ast::types::Range;
@@ -25,16 +23,14 @@ where
StmtKind::FunctionDef { .. } | StmtKind::AsyncFunctionDef { .. } => {
// No recurse.
}
StmtKind::Return { value } => {
self.returns.push(value.as_ref().map(|expr| expr.deref()))
}
StmtKind::Return { value } => self.returns.push(value.as_ref().map(|expr| &**expr)),
_ => visitor::walk_stmt(self, stmt),
}
}
}
fn is_none_returning(body: &[Stmt]) -> bool {
let mut visitor: ReturnStatementVisitor = Default::default();
let mut visitor = ReturnStatementVisitor::default();
for stmt in body {
visitor.visit_stmt(stmt);
}

View File

@@ -21,6 +21,7 @@ pub struct Options {
}
#[derive(Debug, Hash, Default)]
#[allow(clippy::struct_excessive_bools)]
pub struct Settings {
pub mypy_init_return: bool,
pub suppress_dummy_args: bool,
@@ -29,6 +30,7 @@ pub struct Settings {
}
impl Settings {
#[allow(clippy::needless_pass_by_value)]
pub fn from_options(options: Options) -> Self {
Self {
mypy_init_return: options.mypy_init_return.unwrap_or_default(),

View File

@@ -51,7 +51,7 @@ pub fn check_boolean_default_value_in_function_definition(
checker: &mut Checker,
arguments: &Arguments,
) {
for arg in arguments.defaults.iter() {
for arg in &arguments.defaults {
add_if_boolean(
checker,
arg,

View File

@@ -7,7 +7,6 @@ mod tests {
use anyhow::Result;
use crate::autofix::fixer;
use crate::checks::CheckCode;
use crate::linter::test_path;
use crate::{flake8_bugbear, Settings};
@@ -26,7 +25,7 @@ mod tests {
},
..Settings::for_rules(vec![CheckCode::B008])
},
&fixer::Mode::Generate,
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(snapshot, checks);

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,16 +9,15 @@ 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
.node
.arg
.as_ref()
.map(|a| a == "metaclass")
.unwrap_or(false)
.map_or(false, |a| a == "metaclass")
&& match_module_member(
&keyword.node.value,
"abc",
@@ -46,16 +45,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 rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Stmt, StmtKind};
use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Location, Stmt, StmtKind};
use crate::ast::types::Range;
use crate::autofix::Fix;
@@ -8,16 +8,16 @@ use crate::code_gen::SourceGenerator;
fn assertion_error(msg: Option<&Expr>) -> Stmt {
Stmt::new(
Default::default(),
Default::default(),
Location::default(),
Location::default(),
StmtKind::Raise {
exc: Some(Box::new(Expr::new(
Default::default(),
Default::default(),
Location::default(),
Location::default(),
ExprKind::Call {
func: Box::new(Expr::new(
Default::default(),
Default::default(),
Location::default(),
Location::default(),
ExprKind::Name {
id: "AssertionError".to_string(),
ctx: ExprContext::Load,
@@ -36,6 +36,7 @@ fn assertion_error(msg: Option<&Expr>) -> Stmt {
)
}
/// B011
pub fn assert_false(checker: &mut Checker, stmt: &Stmt, test: &Expr, msg: Option<&Expr>) {
if let ExprKind::Constant {
value: Constant::Bool(false),
@@ -43,16 +44,15 @@ pub fn assert_false(checker: &mut Checker, stmt: &Stmt, test: &Expr, msg: Option
} = &test.node
{
let mut check = Check::new(CheckKind::DoNotAssertFalse, Range::from_located(test));
if checker.patch() {
if checker.patch(check.kind.code()) {
let mut generator = SourceGenerator::new();
if let Ok(()) = generator.unparse_stmt(&assertion_error(msg)) {
if let Ok(content) = generator.generate() {
check.amend(Fix::replacement(
content,
stmt.location,
stmt.end_location.unwrap(),
));
}
generator.unparse_stmt(&assertion_error(msg));
if let Ok(content) = generator.generate() {
check.amend(Fix::replacement(
content,
stmt.location,
stmt.end_location.unwrap(),
));
}
}
checker.add_check(check);

View File

@@ -1,7 +1,9 @@
use std::collections::BTreeSet;
use itertools::Itertools;
use rustpython_ast::{Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind, Stmt};
use rustpython_ast::{
Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind, Location, Stmt,
};
use crate::ast::helpers;
use crate::ast::types::Range;
@@ -12,8 +14,8 @@ use crate::code_gen::SourceGenerator;
fn type_pattern(elts: Vec<&Expr>) -> Expr {
Expr::new(
Default::default(),
Default::default(),
Location::default(),
Location::default(),
ExprKind::Tuple {
elts: elts.into_iter().cloned().collect(),
ctx: ExprContext::Load,
@@ -26,9 +28,9 @@ fn duplicate_handler_exceptions<'a>(
expr: &'a Expr,
elts: &'a [Expr],
) -> BTreeSet<Vec<&'a str>> {
let mut seen: BTreeSet<Vec<&str>> = Default::default();
let mut duplicates: BTreeSet<Vec<&str>> = Default::default();
let mut unique_elts: Vec<&Expr> = Default::default();
let mut seen: BTreeSet<Vec<&str>> = BTreeSet::default();
let mut duplicates: BTreeSet<Vec<&str>> = BTreeSet::default();
let mut unique_elts: Vec<&Expr> = Vec::default();
for type_ in elts {
let call_path = helpers::collect_call_paths(type_);
if !call_path.is_empty() {
@@ -54,17 +56,16 @@ fn duplicate_handler_exceptions<'a>(
),
Range::from_located(expr),
);
if checker.patch() {
if checker.patch(check.kind.code()) {
// TODO(charlie): If we have a single element, remove the tuple.
let mut generator = SourceGenerator::new();
if let Ok(()) = generator.unparse_expr(&type_pattern(unique_elts), 0) {
if let Ok(content) = generator.generate() {
check.amend(Fix::replacement(
content,
expr.location,
expr.end_location.unwrap(),
))
}
generator.unparse_expr(&type_pattern(unique_elts), 0);
if let Ok(content) = generator.generate() {
check.amend(Fix::replacement(
content,
expr.location,
expr.end_location.unwrap(),
));
}
}
checker.add_check(check);
@@ -75,8 +76,8 @@ fn duplicate_handler_exceptions<'a>(
}
pub fn duplicate_exceptions(checker: &mut Checker, stmt: &Stmt, handlers: &[Excepthandler]) {
let mut seen: BTreeSet<Vec<&str>> = Default::default();
let mut duplicates: BTreeSet<Vec<&str>> = Default::default();
let mut seen: BTreeSet<Vec<&str>> = BTreeSet::default();
let mut duplicates: BTreeSet<Vec<&str>> = BTreeSet::default();
for handler in handlers {
match &handler.node {
ExcepthandlerKind::ExceptHandler { type_, .. } => {

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>
@@ -60,9 +60,9 @@ where
self.checks.push((
CheckKind::FunctionCallArgumentDefault(compose_call_path(expr)),
Range::from_located(expr),
))
));
}
visitor::walk_expr(self, expr)
visitor::walk_expr(self, expr);
}
ExprKind::Lambda { .. } => {}
_ => visitor::walk_expr(self, expr),

View File

@@ -1,4 +1,4 @@
use rustpython_ast::{Constant, Expr, ExprContext, ExprKind};
use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Location};
use crate::ast::types::Range;
use crate::autofix::Fix;
@@ -10,8 +10,8 @@ use crate::python::keyword::KWLIST;
fn attribute(value: &Expr, attr: &str) -> Expr {
Expr::new(
Default::default(),
Default::default(),
Location::default(),
Location::default(),
ExprKind::Attribute {
value: Box::new(value.clone()),
attr: attr.to_string(),
@@ -20,6 +20,7 @@ fn attribute(value: &Expr, attr: &str) -> Expr {
)
}
/// B009
pub fn getattr_with_constant(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) {
if let ExprKind::Name { id, .. } = &func.node {
if id == "getattr" {
@@ -32,16 +33,15 @@ pub fn getattr_with_constant(checker: &mut Checker, expr: &Expr, func: &Expr, ar
if IDENTIFIER_REGEX.is_match(value) && !KWLIST.contains(&value.as_str()) {
let mut check =
Check::new(CheckKind::GetAttrWithConstant, Range::from_located(expr));
if checker.patch() {
if checker.patch(check.kind.code()) {
let mut generator = SourceGenerator::new();
if let Ok(()) = generator.unparse_expr(&attribute(obj, value), 0) {
if let Ok(content) = generator.generate() {
check.amend(Fix::replacement(
content,
expr.location,
expr.end_location.unwrap(),
));
}
generator.unparse_expr(&attribute(obj, value), 0);
if let Ok(content) = generator.generate() {
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};
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>
@@ -31,7 +31,7 @@ where
}
ExprKind::Lambda { args, body } => {
visitor::walk_expr(self, body);
for arg in args.args.iter() {
for arg in &args.args {
self.names.remove(arg.node.arg.as_str());
}
}

View File

@@ -1,12 +1,12 @@
use fnv::{FnvHashMap, FnvHashSet};
use rustpython_ast::{Arguments, Expr, ExprKind};
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};
use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::checks::{Check, CheckKind};
const MUTABLE_FUNCS: [(&str, &str); 7] = [
const MUTABLE_FUNCS: &[(&str, &str)] = &[
("", "dict"),
("", "list"),
("", "set"),
@@ -16,10 +16,51 @@ const MUTABLE_FUNCS: [(&str, &str); 7] = [
("collections", "deque"),
];
const IMMUTABLE_TYPES: &[(&str, &str)] = &[
("", "bool"),
("", "bytes"),
("", "complex"),
("", "float"),
("", "frozenset"),
("", "int"),
("", "object"),
("", "range"),
("", "str"),
("collections.abc", "Sized"),
("typing", "LiteralString"),
("typing", "Sized"),
];
const IMMUTABLE_GENERIC_TYPES: &[(&str, &str)] = &[
("", "tuple"),
("collections.abc", "ByteString"),
("collections.abc", "Collection"),
("collections.abc", "Container"),
("collections.abc", "Iterable"),
("collections.abc", "Mapping"),
("collections.abc", "Reversible"),
("collections.abc", "Sequence"),
("collections.abc", "Set"),
("typing", "AbstractSet"),
("typing", "ByteString"),
("typing", "Callable"),
("typing", "Collection"),
("typing", "Container"),
("typing", "FrozenSet"),
("typing", "Iterable"),
("typing", "Literal"),
("typing", "Mapping"),
("typing", "Never"),
("typing", "NoReturn"),
("typing", "Reversible"),
("typing", "Sequence"),
("typing", "Tuple"),
];
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
@@ -27,34 +68,106 @@ pub fn is_mutable_func(
.any(|(module, member)| match_call_path(&call_path, module, member, from_imports))
}
fn is_mutable_expr(
expr: &Expr,
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
import_aliases: &FxHashMap<&str, &str>,
) -> bool {
match &expr.node {
ExprKind::List { .. }
| ExprKind::Dict { .. }
| ExprKind::Set { .. }
| ExprKind::ListComp { .. }
| ExprKind::DictComp { .. }
| ExprKind::SetComp { .. } => true,
ExprKind::Call { func, .. } => is_mutable_func(func, from_imports, import_aliases),
_ => false,
}
}
fn is_immutable_annotation(
expr: &Expr,
from_imports: &FxHashMap<&str, FxHashSet<&str>>,
import_aliases: &FxHashMap<&str, &str>,
) -> bool {
match &expr.node {
ExprKind::Name { .. } | ExprKind::Attribute { .. } => {
let call_path = dealias_call_path(collect_call_paths(expr), import_aliases);
IMMUTABLE_TYPES
.iter()
.chain(IMMUTABLE_GENERIC_TYPES)
.any(|(module, member)| match_call_path(&call_path, module, member, from_imports))
}
ExprKind::Subscript { value, slice, .. } => {
let call_path = dealias_call_path(collect_call_paths(value), import_aliases);
if IMMUTABLE_GENERIC_TYPES
.iter()
.any(|(module, member)| match_call_path(&call_path, module, member, from_imports))
{
true
} else if match_call_path(&call_path, "typing", "Union", from_imports) {
if let ExprKind::Tuple { elts, .. } = &slice.node {
elts.iter()
.all(|elt| is_immutable_annotation(elt, from_imports, import_aliases))
} else {
false
}
} else if match_call_path(&call_path, "typing", "Optional", from_imports) {
is_immutable_annotation(slice, from_imports, import_aliases)
} else if match_call_path(&call_path, "typing", "Annotated", from_imports) {
if let ExprKind::Tuple { elts, .. } = &slice.node {
elts.first().map_or(false, |elt| {
is_immutable_annotation(elt, from_imports, import_aliases)
})
} else {
false
}
} else {
false
}
}
ExprKind::BinOp {
left,
op: Operator::BitOr,
right,
} => {
is_immutable_annotation(left, from_imports, import_aliases)
&& is_immutable_annotation(right, from_imports, import_aliases)
}
ExprKind::Constant {
value: Constant::None,
..
} => true,
_ => false,
}
}
/// B006
pub fn mutable_argument_default(checker: &mut Checker, arguments: &Arguments) {
for expr in arguments
.defaults
// Scan in reverse order to right-align zip()
for (arg, default) in arguments
.kwonlyargs
.iter()
.chain(arguments.kw_defaults.iter())
.rev()
.zip(arguments.kw_defaults.iter().rev())
.chain(
arguments
.args
.iter()
.rev()
.chain(arguments.posonlyargs.iter().rev())
.zip(arguments.defaults.iter().rev()),
)
{
match &expr.node {
ExprKind::List { .. }
| ExprKind::Dict { .. }
| ExprKind::Set { .. }
| ExprKind::ListComp { .. }
| ExprKind::DictComp { .. }
| ExprKind::SetComp { .. } => {
checker.add_check(Check::new(
CheckKind::MutableArgumentDefault,
Range::from_located(expr),
));
}
ExprKind::Call { func, .. } => {
if is_mutable_func(func, &checker.from_imports, &checker.import_aliases) {
checker.add_check(Check::new(
CheckKind::MutableArgumentDefault,
Range::from_located(expr),
));
}
}
_ => {}
if is_mutable_expr(default, &checker.from_imports, &checker.import_aliases)
&& arg.node.annotation.as_ref().map_or(true, |expr| {
!is_immutable_annotation(expr, &checker.from_imports, &checker.import_aliases)
})
{
checker.add_check(Check::new(
CheckKind::MutableArgumentDefault,
Range::from_located(default),
));
}
}
}

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,28 @@ 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();
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,62 @@
use rustpython_ast::{Constant, Expr, ExprKind};
use anyhow::Result;
use log::error;
use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Location, 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) -> Result<String> {
let stmt = Stmt::new(
Location::default(),
Location::default(),
StmtKind::Assign {
targets: vec![Expr::new(
Location::default(),
Location::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();
generator.unparse_stmt(&stmt);
generator.generate().map_err(std::convert::Into::into)
}
/// 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()) {
match assignment(obj, name, value) {
Ok(content) => check.amend(Fix::replacement(
content,
expr.location,
expr.end_location.unwrap(),
)),
Err(e) => error!("Failed to fix invalid comparison: {}", e),
};
}
checker.add_check(check);
}
}
}

View File

@@ -12,7 +12,7 @@ pub fn unary_prefix_increment(checker: &mut Checker, expr: &Expr, op: &Unaryop,
checker.add_check(Check::new(
CheckKind::UnaryPrefixIncrement,
Range::from_located(expr),
))
));
}
}
}

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,13 +11,13 @@ 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<'_> {
fn new() -> Self {
NameFinder {
names: Default::default(),
names: FxHashMap::default(),
}
}
}
@@ -65,13 +65,13 @@ pub fn unused_loop_control_variable(checker: &mut Checker, target: &Expr, body:
CheckKind::UnusedLoopControlVariable(name.to_string()),
Range::from_located(expr),
);
if checker.patch() {
if checker.patch(check.kind.code()) {
// Prefix the variable name with an underscore.
check.amend(Fix::replacement(
format!("_{name}"),
expr.location,
expr.end_location.unwrap(),
))
));
}
checker.add_check(check);
}

View File

@@ -1,3 +1,4 @@
#[derive(Clone, Copy)]
pub enum ShadowingType {
Variable,
Argument,

View File

@@ -390,11 +390,6 @@ pub fn unnecessary_double_cast_or_process(
args: &[Expr],
location: Range,
) -> Option<Check> {
let outer = function_name(func)?;
if !["list", "tuple", "set", "reversed", "sorted"].contains(&outer) {
return None;
}
fn new_check(inner: &str, outer: &str, location: Range) -> Check {
Check::new(
CheckKind::UnnecessaryDoubleCastOrProcess(inner.to_string(), outer.to_string()),
@@ -402,6 +397,11 @@ pub fn unnecessary_double_cast_or_process(
)
}
let outer = function_name(func)?;
if !["list", "tuple", "set", "reversed", "sorted"].contains(&outer) {
return None;
}
if let ExprKind::Call { func, .. } = &args.first()?.node {
let inner = function_name(func)?;
// Ex) set(tuple(...))

View File

@@ -1,9 +1,9 @@
use anyhow::Result;
use libcst_native::{
Arg, AssignEqual, Call, Codegen, Dict, DictComp, DictElement, Element, Expr, Expression,
LeftCurlyBrace, LeftParen, LeftSquareBracket, List, ListComp, Name, ParenthesizableWhitespace,
RightCurlyBrace, RightParen, RightSquareBracket, Set, SetComp, SimpleString, SimpleWhitespace,
Tuple,
Arg, AssignEqual, Call, Codegen, CodegenState, Dict, DictComp, DictElement, Element, Expr,
Expression, LeftCurlyBrace, LeftParen, LeftSquareBracket, List, ListComp, Name,
ParenthesizableWhitespace, RightCurlyBrace, RightParen, RightSquareBracket, Set, SetComp,
SimpleString, SimpleWhitespace, Tuple,
};
use crate::ast::types::Range;
@@ -60,7 +60,7 @@ pub fn fix_unnecessary_generator_list(
rpar: generator_exp.rpar.clone(),
}));
let mut state = Default::default();
let mut state = CodegenState::default();
tree.codegen(&mut state);
Ok(Fix::replacement(
@@ -103,7 +103,7 @@ pub fn fix_unnecessary_generator_set(
rpar: generator_exp.rpar.clone(),
}));
let mut state = Default::default();
let mut state = CodegenState::default();
tree.codegen(&mut state);
Ok(Fix::replacement(
@@ -163,13 +163,13 @@ pub fn fix_unnecessary_generator_dict(
rbrace: RightCurlyBrace {
whitespace_before: arg.whitespace_after_arg.clone(),
},
lpar: Default::default(),
rpar: Default::default(),
whitespace_before_colon: Default::default(),
lpar: vec![],
rpar: vec![],
whitespace_before_colon: ParenthesizableWhitespace::default(),
whitespace_after_colon: ParenthesizableWhitespace::SimpleWhitespace(SimpleWhitespace(" ")),
}));
let mut state = Default::default();
let mut state = CodegenState::default();
tree.codegen(&mut state);
Ok(Fix::replacement(
@@ -211,7 +211,7 @@ pub fn fix_unnecessary_list_comprehension_set(
rpar: list_comp.rpar.clone(),
}));
let mut state = Default::default();
let mut state = CodegenState::default();
tree.codegen(&mut state);
Ok(Fix::replacement(
@@ -269,7 +269,7 @@ pub fn fix_unnecessary_list_comprehension_dict(
rpar: list_comp.rpar.clone(),
}));
let mut state = Default::default();
let mut state = CodegenState::default();
tree.codegen(&mut state);
Ok(Fix::replacement(
@@ -312,12 +312,12 @@ pub fn fix_unnecessary_literal_set(
rbrace: RightCurlyBrace {
whitespace_before: arg.whitespace_after_arg.clone(),
},
lpar: Default::default(),
rpar: Default::default(),
lpar: vec![],
rpar: vec![],
}));
}
let mut state = Default::default();
let mut state = CodegenState::default();
tree.codegen(&mut state);
Ok(Fix::replacement(
@@ -363,7 +363,7 @@ pub fn fix_unnecessary_literal_dict(
key: key.clone(),
value: value.clone(),
comma: comma.clone(),
whitespace_before_colon: Default::default(),
whitespace_before_colon: ParenthesizableWhitespace::default(),
whitespace_after_colon: ParenthesizableWhitespace::SimpleWhitespace(
SimpleWhitespace(" "),
),
@@ -385,11 +385,11 @@ pub fn fix_unnecessary_literal_dict(
rbrace: RightCurlyBrace {
whitespace_before: arg.whitespace_after_arg.clone(),
},
lpar: Default::default(),
rpar: Default::default(),
lpar: vec![],
rpar: vec![],
}));
let mut state = Default::default();
let mut state = CodegenState::default();
tree.codegen(&mut state);
Ok(Fix::replacement(
@@ -422,28 +422,28 @@ pub fn fix_unnecessary_collection_call(
match name.value {
"tuple" => {
body.value = Expression::Tuple(Box::new(Tuple {
elements: Default::default(),
lpar: vec![Default::default()],
rpar: vec![Default::default()],
elements: vec![],
lpar: vec![LeftParen::default()],
rpar: vec![RightParen::default()],
}));
}
"list" => {
body.value = Expression::List(Box::new(List {
elements: Default::default(),
lbracket: Default::default(),
rbracket: Default::default(),
lpar: Default::default(),
rpar: Default::default(),
elements: vec![],
lbracket: LeftSquareBracket::default(),
rbracket: RightSquareBracket::default(),
lpar: vec![],
rpar: vec![],
}));
}
"dict" => {
if call.args.is_empty() {
body.value = Expression::Dict(Box::new(Dict {
elements: Default::default(),
lbrace: Default::default(),
rbrace: Default::default(),
lpar: Default::default(),
rpar: Default::default(),
elements: vec![],
lbrace: LeftCurlyBrace::default(),
rbrace: RightCurlyBrace::default(),
lpar: vec![],
rpar: vec![],
}));
} else {
// Quote each argument.
@@ -465,12 +465,12 @@ pub fn fix_unnecessary_collection_call(
.map(|(i, arg)| DictElement::Simple {
key: Expression::SimpleString(Box::new(SimpleString {
value: &arena[i],
lpar: Default::default(),
rpar: Default::default(),
lpar: vec![],
rpar: vec![],
})),
value: arg.value.clone(),
comma: arg.comma.clone(),
whitespace_before_colon: Default::default(),
whitespace_before_colon: ParenthesizableWhitespace::default(),
whitespace_after_colon: ParenthesizableWhitespace::SimpleWhitespace(
SimpleWhitespace(" "),
),
@@ -490,8 +490,8 @@ pub fn fix_unnecessary_collection_call(
.whitespace_after_arg
.clone(),
},
lpar: Default::default(),
rpar: Default::default(),
lpar: vec![],
rpar: vec![],
}));
}
}
@@ -502,7 +502,7 @@ pub fn fix_unnecessary_collection_call(
}
};
let mut state = Default::default();
let mut state = CodegenState::default();
tree.codegen(&mut state);
Ok(Fix::replacement(
@@ -558,7 +558,7 @@ pub fn fix_unnecessary_literal_within_tuple_call(
}],
}));
let mut state = Default::default();
let mut state = CodegenState::default();
tree.codegen(&mut state);
Ok(Fix::replacement(
@@ -612,11 +612,11 @@ pub fn fix_unnecessary_literal_within_list_call(
rbracket: RightSquareBracket {
whitespace_before: whitespace_before.clone(),
},
lpar: Default::default(),
rpar: Default::default(),
lpar: vec![],
rpar: vec![],
}));
let mut state = Default::default();
let mut state = CodegenState::default();
tree.codegen(&mut state);
Ok(Fix::replacement(
@@ -640,7 +640,7 @@ pub fn fix_unnecessary_list_call(
body.value = arg.value.clone();
let mut state = Default::default();
let mut state = CodegenState::default();
tree.codegen(&mut state);
Ok(Fix::replacement(
@@ -695,22 +695,22 @@ pub fn fix_unnecessary_call_around_sorted(
args.push(Arg {
value: Expression::Name(Box::new(Name {
value: "True",
lpar: Default::default(),
rpar: Default::default(),
lpar: vec![],
rpar: vec![],
})),
keyword: Some(Name {
value: "reverse",
lpar: Default::default(),
rpar: Default::default(),
lpar: vec![],
rpar: vec![],
}),
equal: Some(AssignEqual {
whitespace_before: Default::default(),
whitespace_after: Default::default(),
whitespace_before: ParenthesizableWhitespace::default(),
whitespace_after: ParenthesizableWhitespace::default(),
}),
comma: Default::default(),
star: Default::default(),
whitespace_after_star: Default::default(),
whitespace_after_arg: Default::default(),
comma: None,
star: "",
whitespace_after_star: ParenthesizableWhitespace::default(),
whitespace_after_arg: ParenthesizableWhitespace::default(),
});
args
};
@@ -722,11 +722,11 @@ pub fn fix_unnecessary_call_around_sorted(
rpar: inner_call.rpar.clone(),
whitespace_after_func: inner_call.whitespace_after_func.clone(),
whitespace_before_args: inner_call.whitespace_before_args.clone(),
}))
}));
}
}
let mut state = Default::default();
let mut state = CodegenState::default();
tree.codegen(&mut state);
Ok(Fix::replacement(
@@ -750,45 +750,45 @@ pub fn fix_unnecessary_comprehension(
body.value = Expression::Call(Box::new(Call {
func: Box::new(Expression::Name(Box::new(Name {
value: "list",
lpar: Default::default(),
rpar: Default::default(),
lpar: vec![],
rpar: vec![],
}))),
args: vec![Arg {
value: inner.for_in.iter.clone(),
keyword: Default::default(),
equal: Default::default(),
comma: Default::default(),
star: Default::default(),
whitespace_after_star: Default::default(),
whitespace_after_arg: Default::default(),
keyword: None,
equal: None,
comma: None,
star: "",
whitespace_after_star: ParenthesizableWhitespace::default(),
whitespace_after_arg: ParenthesizableWhitespace::default(),
}],
lpar: Default::default(),
rpar: Default::default(),
whitespace_after_func: Default::default(),
whitespace_before_args: Default::default(),
}))
lpar: vec![],
rpar: vec![],
whitespace_after_func: ParenthesizableWhitespace::default(),
whitespace_before_args: ParenthesizableWhitespace::default(),
}));
}
Expression::SetComp(inner) => {
body.value = Expression::Call(Box::new(Call {
func: Box::new(Expression::Name(Box::new(Name {
value: "set",
lpar: Default::default(),
rpar: Default::default(),
lpar: vec![],
rpar: vec![],
}))),
args: vec![Arg {
value: inner.for_in.iter.clone(),
keyword: Default::default(),
equal: Default::default(),
comma: Default::default(),
star: Default::default(),
whitespace_after_star: Default::default(),
whitespace_after_arg: Default::default(),
keyword: None,
equal: None,
comma: None,
star: "",
whitespace_after_star: ParenthesizableWhitespace::default(),
whitespace_after_arg: ParenthesizableWhitespace::default(),
}],
lpar: Default::default(),
rpar: Default::default(),
whitespace_after_func: Default::default(),
whitespace_before_args: Default::default(),
}))
lpar: vec![],
rpar: vec![],
whitespace_after_func: ParenthesizableWhitespace::default(),
whitespace_before_args: ParenthesizableWhitespace::default(),
}));
}
_ => {
return Err(anyhow::anyhow!(
@@ -797,7 +797,7 @@ pub fn fix_unnecessary_comprehension(
}
}
let mut state = Default::default();
let mut state = CodegenState::default();
tree.codegen(&mut state);
Ok(Fix::replacement(

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,13 +10,12 @@ 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),
Range::from_located(expr),
) {
if checker.patch() {
if checker.patch(check.kind.code()) {
let context = checker.binding_context();
if matches!(
checker.parents[context.defined_by].node,
@@ -36,7 +35,7 @@ pub fn print_call(checker: &mut Checker, expr: &Expr, func: &Expr) {
if fix.patch.content.is_empty() || fix.patch.content == "pass" {
checker.deletions.insert(context.defined_by);
}
check.amend(fix)
check.amend(fix);
}
Err(e) => error!("Failed to remove print call: {}", e),
}

View File

@@ -42,14 +42,14 @@ fn good_docstring(quote: &Quote) -> &str {
pub fn quotes(
locator: &SourceCodeLocator,
start: &Location,
end: &Location,
start: Location,
end: Location,
is_docstring: bool,
settings: &Settings,
) -> Option<Check> {
let text = locator.slice_source_code_range(&Range {
location: *start,
end_location: *end,
location: start,
end_location: end,
});
// Remove any prefixes (e.g., remove `u` from `u"foo"`).
@@ -74,13 +74,13 @@ pub fn quotes(
return None;
}
return Some(Check::new(
Some(Check::new(
CheckKind::BadQuotesDocstring(settings.docstring_quotes.clone()),
Range {
location: *start,
end_location: *end,
location: start,
end_location: end,
},
));
))
} else if is_multiline {
// If our string is or contains a known good string, ignore it.
if raw_text.contains(good_multiline(&settings.multiline_quotes)) {
@@ -92,13 +92,13 @@ pub fn quotes(
return None;
}
return Some(Check::new(
Some(Check::new(
CheckKind::BadQuotesMultilineString(settings.multiline_quotes.clone()),
Range {
location: *start,
end_location: *end,
location: start,
end_location: end,
},
));
))
} else {
let string_contents = &raw_text[1..raw_text.len() - 1];
@@ -113,8 +113,8 @@ pub fn quotes(
return Some(Check::new(
CheckKind::AvoidQuoteEscape,
Range {
location: *start,
end_location: *end,
location: start,
end_location: end,
},
));
}
@@ -126,12 +126,12 @@ pub fn quotes(
return Some(Check::new(
CheckKind::BadQuotesInlineString(settings.inline_quotes.clone()),
Range {
location: *start,
end_location: *end,
location: start,
end_location: end,
},
));
}
}
None
None
}
}

View File

@@ -8,7 +8,6 @@ mod tests {
use anyhow::Result;
use test_case::test_case;
use crate::autofix::fixer;
use crate::checks::CheckCode;
use crate::flake8_quotes::settings::Quote;
use crate::linter::test_path;
@@ -39,7 +38,7 @@ mod tests {
CheckCode::Q003,
])
},
&fixer::Mode::Generate,
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(snapshot, checks);
@@ -71,7 +70,7 @@ mod tests {
CheckCode::Q003,
])
},
&fixer::Mode::Generate,
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(snapshot, checks);
@@ -108,7 +107,7 @@ mod tests {
CheckCode::Q003,
])
},
&fixer::Mode::Generate,
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(snapshot, checks);
@@ -145,7 +144,7 @@ mod tests {
CheckCode::Q003,
])
},
&fixer::Mode::Generate,
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(snapshot, checks);

View File

@@ -7,7 +7,6 @@ mod tests {
use anyhow::Result;
use crate::autofix::fixer;
use crate::checks::CheckCode;
use crate::flake8_tidy_imports::settings::Strictness;
use crate::linter::test_path;
@@ -23,7 +22,7 @@ mod tests {
},
..Settings::for_rules(vec![CheckCode::I252])
},
&fixer::Mode::Generate,
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(checks);
@@ -40,7 +39,7 @@ mod tests {
},
..Settings::for_rules(vec![CheckCode::I252])
},
&fixer::Mode::Generate,
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(checks);

View File

@@ -1,13 +1,12 @@
use std::borrow::Cow;
use std::fs::File;
use std::io::{BufReader, Read};
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;
@@ -102,8 +101,8 @@ pub fn iter_python_files<'a>(
true
}
}
Err(_) => {
debug!("Ignored path due to error in parsing: {:?}", path);
Err(e) => {
debug!("Ignored path due to error in parsing: {:?}: {}", path, e);
true
}
}
@@ -121,7 +120,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()
@@ -155,7 +154,7 @@ pub(crate) fn normalize_path_to(path: &Path, project_root: &Path) -> PathBuf {
/// Convert an absolute path to be relative to the current working directory.
pub(crate) fn relativize_path(path: &Path) -> Cow<str> {
if let Ok(path) = path.strip_prefix(path_dedot::CWD.deref()) {
if let Ok(path) = path.strip_prefix(&*path_dedot::CWD) {
return path.to_string_lossy();
}
path.to_string_lossy()
@@ -203,7 +202,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 +210,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 +220,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 +228,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 +238,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 +248,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 +258,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

@@ -21,7 +21,7 @@ pub fn categorize(
known_third_party: &BTreeSet<String>,
extra_standard_library: &BTreeSet<String>,
) -> ImportType {
if level.map(|level| *level > 0).unwrap_or(false) {
if level.map_or(false, |level| *level > 0) {
ImportType::LocalFolder
} else if known_first_party.contains(module_base) {
ImportType::FirstParty

View File

@@ -22,8 +22,8 @@ pub fn collect_comments<'a>(range: &Range, locator: &'a SourceCodeLocator) -> Ve
.flatten()
.filter_map(|(start, tok, end)| {
if matches!(tok, Tok::Comment) {
let start = helpers::to_absolute(&start, &range.location);
let end = helpers::to_absolute(&end, &range.location);
let start = helpers::to_absolute(start, range.location);
let end = helpers::to_absolute(end, range.location);
Some(Comment {
value: locator.slice_source_code_range(&Range {
location: start,

View File

@@ -38,7 +38,7 @@ pub fn format_import_from(
import_from: &ImportFromData,
comments: &CommentSet,
aliases: &[(AliasData, CommentSet)],
line_length: &usize,
line_length: usize,
is_first: bool,
) -> String {
// We can only inline if: (1) none of the aliases have atop comments, and (3)
@@ -54,7 +54,7 @@ pub fn format_import_from(
{
let (single_line, import_length) =
format_single_line(import_from, comments, aliases, is_first);
if import_length <= *line_length {
if import_length <= line_length {
return single_line;
}
}

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};
@@ -127,7 +128,7 @@ fn annotate_imports<'a>(
asname: alias.node.asname.as_ref(),
atop: alias_atop,
inline: alias_inline,
})
});
}
annotated.push(AnnotatedImport::ImportFrom {
@@ -145,7 +146,7 @@ fn annotate_imports<'a>(
}
fn normalize_imports(imports: Vec<AnnotatedImport>) -> ImportBlock {
let mut block: ImportBlock = Default::default();
let mut block = ImportBlock::default();
for import in imports {
match import {
AnnotatedImport::Import {
@@ -203,7 +204,7 @@ fn normalize_imports(imports: Vec<AnnotatedImport>) -> ImportBlock {
entry.inline.push(comment.value);
}
} else {
let entry = &mut block
let entry = block
.import_from_as
.entry((
ImportFromData { module, level },
@@ -273,7 +274,7 @@ fn categorize_imports<'a>(
known_third_party: &BTreeSet<String>,
extra_standard_library: &BTreeSet<String>,
) -> BTreeMap<ImportType, ImportBlock<'a>> {
let mut block_by_type: BTreeMap<ImportType, ImportBlock> = Default::default();
let mut block_by_type: BTreeMap<ImportType, ImportBlock> = BTreeMap::default();
// Categorize `StmtKind::Import`.
for (alias, comments) in block.import {
let import_type = categorize(
@@ -326,7 +327,7 @@ fn categorize_imports<'a>(
}
fn sort_imports(block: ImportBlock) -> OrderedImportBlock {
let mut ordered: OrderedImportBlock = Default::default();
let mut ordered = OrderedImportBlock::default();
// Sort `StmtKind::Import`.
ordered.import.extend(
@@ -353,12 +354,12 @@ fn sort_imports(block: ImportBlock) -> OrderedImportBlock {
(
CommentSet {
atop: comments.atop,
inline: Default::default(),
inline: vec![],
},
FnvHashMap::from_iter([(
FxHashMap::from_iter([(
alias,
CommentSet {
atop: Default::default(),
atop: vec![],
inline: comments.inline,
},
)]),
@@ -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()
@@ -398,7 +400,7 @@ fn sort_imports(block: ImportBlock) -> OrderedImportBlock {
pub fn format_imports(
block: &[&Stmt],
comments: Vec<Comment>,
line_length: &usize,
line_length: usize,
src: &[PathBuf],
known_first_party: &BTreeSet<String>,
known_third_party: &BTreeSet<String>,
@@ -426,22 +428,22 @@ pub fn format_imports(
let import_block = sort_imports(import_block);
// Add a blank line between every section.
if !is_first_block {
output.append("\n");
} else {
if is_first_block {
is_first_block = false;
} else {
output.append("\n");
}
let mut is_first_statement = true;
// Format `StmtKind::Import` statements.
for (alias, comments) in import_block.import.iter() {
for (alias, comments) in &import_block.import {
output.append(&format::format_import(alias, comments, is_first_statement));
is_first_statement = false;
}
// Format `StmtKind::ImportFrom` statements.
for (import_from, comments, aliases) in import_block.import_from.iter() {
for (import_from, comments, aliases) in &import_block.import_from {
output.append(&format::format_import_from(
import_from,
comments,
@@ -462,7 +464,6 @@ mod tests {
use anyhow::Result;
use test_case::test_case;
use crate::autofix::fixer;
use crate::checks::CheckCode;
use crate::linter::test_path;
use crate::Settings;
@@ -477,6 +478,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"))]
@@ -498,7 +500,7 @@ mod tests {
src: vec![Path::new("resources/test/fixtures/isort").to_path_buf()],
..Settings::for_rule(CheckCode::I001)
},
&fixer::Mode::Generate,
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(snapshot, checks);

View File

@@ -1,10 +1,11 @@
use rustpython_ast::{Location, Stmt};
use textwrap::{dedent, indent};
use crate::ast::helpers::{match_leading_content, match_trailing_content};
use crate::ast::types::Range;
use crate::autofix::{fixer, Fix};
use crate::ast::whitespace::leading_space;
use crate::autofix::Fix;
use crate::checks::CheckKind;
use crate::docstrings::helpers::leading_space;
use crate::isort::{comments, format_imports};
use crate::{Check, Settings, SourceCodeLocator};
@@ -27,43 +28,15 @@ fn extract_indentation(body: &[&Stmt], locator: &SourceCodeLocator) -> String {
leading_space(&existing)
}
fn match_leading_content(body: &[&Stmt], locator: &SourceCodeLocator) -> bool {
let location = body.first().unwrap().location;
let range = Range {
location: Location::new(location.row(), 0),
end_location: location,
};
let prefix = locator.slice_source_code_range(&range);
prefix.chars().any(|char| !char.is_whitespace())
}
fn match_trailing_content(body: &[&Stmt], locator: &SourceCodeLocator) -> bool {
let end_location = body.last().unwrap().end_location.unwrap();
let range = Range {
location: end_location,
end_location: Location::new(end_location.row() + 1, 0),
};
let suffix = locator.slice_source_code_range(&range);
for char in suffix.chars() {
if char == '#' {
return false;
}
if !char.is_whitespace() {
return true;
}
}
false
}
/// I001
pub fn check_imports(
body: Vec<&Stmt>,
body: &[&Stmt],
locator: &SourceCodeLocator,
settings: &Settings,
autofix: &fixer::Mode,
autofix: bool,
) -> Option<Check> {
let range = extract_range(&body);
let indentation = extract_indentation(&body, locator);
let range = extract_range(body);
let indentation = extract_indentation(body, locator);
// Extract comments. Take care to grab any inline comments from the last line.
let comments = comments::collect_comments(
@@ -75,14 +48,14 @@ pub fn check_imports(
);
// Special-cases: there's leading or trailing content in the import block.
let has_leading_content = match_leading_content(&body, locator);
let has_trailing_content = match_trailing_content(&body, locator);
let has_leading_content = match_leading_content(body.first().unwrap(), locator);
let has_trailing_content = match_trailing_content(body.last().unwrap(), locator);
// Generate the sorted import block.
let expected = format_imports(
&body,
body,
comments,
&(settings.line_length - indentation.len()),
settings.line_length - indentation.len(),
&settings.src,
&settings.isort.known_first_party,
&settings.isort.known_third_party,
@@ -91,7 +64,7 @@ pub fn check_imports(
if has_leading_content || has_trailing_content {
let mut check = Check::new(CheckKind::UnsortedImports, range);
if autofix.patch() {
if autofix && settings.fixable.contains(check.kind.code()) {
let mut content = String::new();
if has_leading_content {
content.push('\n');
@@ -117,9 +90,11 @@ pub fn check_imports(
end_location: Location::new(range.end_location.row() + 1, 0),
};
let actual = dedent(&locator.slice_source_code_range(&range));
if actual != expected {
if actual == expected {
None
} else {
let mut check = Check::new(CheckKind::UnsortedImports, range);
if autofix.patch() {
if autofix && settings.fixable.contains(check.kind.code()) {
check.amend(Fix::replacement(
indent(&expected, &indentation),
range.location,
@@ -127,8 +102,6 @@ pub fn check_imports(
));
}
Some(check)
} else {
None
}
}
}

View File

@@ -18,5 +18,4 @@ expression: checks
end_location:
row: 8
column: 0
applied: false

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