Compare commits

...

35 Commits

Author SHA1 Message Date
Charlie Marsh
1d1662cb9c Bump version to 0.0.159 2022-12-05 11:22:02 -05:00
Charlie Marsh
55ce7bd0df Migrate invalid_literal_comparisons fix to token-based logic (#1065) 2022-12-05 11:16:59 -05:00
Charlie Marsh
e695f6eb25 Avoid false-positive on PLR1701 for multi-type isinstance calls (#1063) 2022-12-05 10:07:05 -05:00
messense
fb2c457a9b Upgrade to notify 5.0.0 (#1048) 2022-12-05 09:58:42 -05:00
Jeong YunWon
523cf62eda Style fixes (#1049) 2022-12-05 09:57:48 -05:00
Charlie Marsh
7024ad7cc7 Bump version to 0.0.158 2022-12-04 21:22:24 -05:00
Charlie Marsh
871ac511ae Add an option to force one-member-per-line for aliased import-froms (#1047) 2022-12-04 21:22:00 -05:00
Charlie Marsh
0685af8a4f Update RustPython (#1045) 2022-12-04 20:09:28 -05:00
Charlie Marsh
5e9a8fcf53 Bump version to 0.0.157 2022-12-04 14:46:46 -05:00
Charlie Marsh
76439235af Remove unused imports in __init__.py files by default (#1042) 2022-12-04 14:45:54 -05:00
Charlie Marsh
0c9c6a1c1c Remove extraneous test file 2022-12-04 13:59:55 -05:00
Charlie Marsh
46a99243cd Fix D205 autofix by detecting summary line (#1041) 2022-12-04 13:56:51 -05:00
Charlie Marsh
d06dc4c72d Bump version to 0.0.156 2022-12-04 10:22:09 -05:00
Jonathan Plasse
8f6b2fb32b Extend and rename RUF004 to PLR1722 (#1036) 2022-12-04 10:20:12 -05:00
Charlie Marsh
1d61db5b62 Allow import builtins under T100 (#1037) 2022-12-04 10:13:17 -05:00
Charlie Marsh
e15c0c68cb Fix PLW0120 snapshot 2022-12-04 10:10:00 -05:00
Harutaka Kawamura
abb2adc4d8 Implement useless-import-alias (#1025) 2022-12-04 09:48:56 -05:00
Harutaka Kawamura
e070166409 Implement useless-else-on-loop (#1031) 2022-12-04 09:22:04 -05:00
Jonathan Plasse
0ae6890094 Fix Table of Contents (#1030) 2022-12-04 09:14:09 -05:00
Harutaka Kawamura
21cace0973 Fix PLR0402 (#1024) 2022-12-04 09:13:47 -05:00
Jakub Kuczys
4994b72ba2 Fix README header links in isort config section (#1033) 2022-12-04 09:12:25 -05:00
Anders Kaseorg
dbb1a6e44b Remove sloppy match_name_or_attr helper (#1027) 2022-12-04 09:12:03 -05:00
Charlie Marsh
4b0c3e3bc9 Bump version to 0.0.155 2022-12-04 00:12:00 -05:00
Charlie Marsh
ddae3586d5 Add support for combine-as-imports import formatting (#1022) 2022-12-04 00:11:48 -05:00
Charlie Marsh
97684b7215 Mark MisplacedComparisonConstant as fixable 2022-12-04 00:11:19 -05:00
Harutaka Kawamura
20e6ff112a Implement misplaced-comparison-constant (#1023) 2022-12-04 00:10:07 -05:00
Charlie Marsh
6cb84d29f0 Rename M001 to RUF100 (#1021) 2022-12-03 22:51:05 -05:00
Charlie Marsh
d069054792 Rename RUF101 fixtures 2022-12-03 22:47:16 -05:00
Charlie Marsh
c659cb86d9 Rename RUF101 to RUF004 (#1020) 2022-12-03 22:46:13 -05:00
Charlie Marsh
8a3f29497b Add backwards compatible redirect map for U-to-UP rename (#1019) 2022-12-03 22:43:00 -05:00
Harutaka Kawamura
58e7baedd0 Implement consider-using-from-import (#1018) 2022-12-03 22:36:43 -05:00
Charlie Marsh
00eff2b09a Rename pyupgrade rules from UXXX to UPXXX (#957) 2022-12-03 22:35:42 -05:00
Charlie Marsh
b6b509811d Bump version to 0.0.154 2022-12-03 20:14:38 -05:00
Charlie Marsh
87bdda3cfa Make some flake8-return rules auto-fixable (#1017) 2022-12-03 20:13:17 -05:00
Charlie Marsh
291727a27d Implement flake8-return plugin (#1016) 2022-12-03 20:12:11 -05:00
203 changed files with 6511 additions and 2613 deletions

View File

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

400
Cargo.lock generated
View File

@@ -21,9 +21,9 @@ dependencies = [
[[package]]
name = "aho-corasick"
version = "0.7.19"
version = "0.7.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
dependencies = [
"memchr",
]
@@ -82,9 +82,9 @@ dependencies = [
[[package]]
name = "assert_cmd"
version = "2.0.6"
version = "2.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba45b8163c49ab5f972e59a8a5a03b6d2972619d486e19ec9fe744f7c2753d3c"
checksum = "fa3d466004a8b4cb1bc34044240a2fd29d17607e2e3bd613eb44fd48e8100da3"
dependencies = [
"bstr 1.0.1",
"doc-comment",
@@ -100,9 +100,9 @@ version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"hermit-abi 0.1.19",
"libc",
"winapi 0.3.9",
"winapi",
]
[[package]]
@@ -193,9 +193,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cc"
version = "1.0.75"
version = "1.0.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41ca34107f97baef6cfb231b32f36115781856b8f8208e8c580e0bcaea374842"
checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
[[package]]
name = "cfg-if"
@@ -230,7 +230,7 @@ dependencies = [
"num-traits",
"time",
"wasm-bindgen",
"winapi 0.3.9",
"winapi",
]
[[package]]
@@ -280,14 +280,14 @@ dependencies = [
[[package]]
name = "clap"
version = "4.0.22"
version = "4.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91b9970d7505127a162fdaa9b96428d28a479ba78c9ec7550a63a5d9863db682"
checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d"
dependencies = [
"atty",
"bitflags",
"clap_derive",
"clap_lex 0.3.0",
"is-terminal",
"once_cell",
"strsim",
"termcolor",
@@ -299,7 +299,7 @@ version = "4.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7b3c9eae0de7bf8e3f904a5e40612b21fb2e2e566456d177809a48b892d24da"
dependencies = [
"clap 4.0.22",
"clap 4.0.29",
]
[[package]]
@@ -308,7 +308,7 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4160b4a4f72ef58bd766bad27c09e6ef1cc9d82a22f6a0f55d152985a4a48e31"
dependencies = [
"clap 4.0.22",
"clap 4.0.29",
"clap_complete",
"clap_complete_fig",
]
@@ -319,7 +319,7 @@ version = "4.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46b30e010e669cd021e5004f3be26cff6b7c08d2a8a0d65b48d43a8cc0efd6c3"
dependencies = [
"clap 4.0.22",
"clap 4.0.29",
"clap_complete",
]
@@ -364,7 +364,7 @@ dependencies = [
"terminfo",
"thiserror",
"which",
"winapi 0.3.9",
"winapi",
]
[[package]]
@@ -394,7 +394,7 @@ checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd"
dependencies = [
"atty",
"lazy_static",
"winapi 0.3.9",
"winapi",
]
[[package]]
@@ -419,7 +419,7 @@ dependencies = [
"lazy_static",
"libc",
"terminal_size",
"winapi 0.3.9",
"winapi",
]
[[package]]
@@ -496,9 +496,9 @@ dependencies = [
[[package]]
name = "crossbeam-epoch"
version = "0.9.11"
version = "0.9.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348"
checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a"
dependencies = [
"autocfg",
"cfg-if 1.0.0",
@@ -509,9 +509,9 @@ dependencies = [
[[package]]
name = "crossbeam-utils"
version = "0.8.12"
version = "0.8.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac"
checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
dependencies = [
"cfg-if 1.0.0",
]
@@ -524,9 +524,9 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
name = "cxx"
version = "1.0.81"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97abf9f0eca9e52b7f81b945524e76710e6cb2366aead23b7d4fbf72e281f888"
checksum = "bdf07d07d6531bfcdbe9b8b739b104610c6508dcc4d63b410585faf338241daf"
dependencies = [
"cc",
"cxxbridge-flags",
@@ -536,9 +536,9 @@ dependencies = [
[[package]]
name = "cxx-build"
version = "1.0.81"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cc32cc5fea1d894b77d269ddb9f192110069a8a9c1f1d441195fba90553dea3"
checksum = "d2eb5b96ecdc99f72657332953d4d9c50135af1bac34277801cc3937906ebd39"
dependencies = [
"cc",
"codespan-reporting",
@@ -551,15 +551,15 @@ dependencies = [
[[package]]
name = "cxxbridge-flags"
version = "1.0.81"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ca220e4794c934dc6b1207c3b42856ad4c302f2df1712e9f8d2eec5afaacf1f"
checksum = "ac040a39517fd1674e0f32177648334b0f4074625b5588a64519804ba0553b12"
[[package]]
name = "cxxbridge-macro"
version = "1.0.81"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b846f081361125bfc8dc9d3940c84e1fd83ba54bbca7b17cd29483c828be0704"
checksum = "1362b0ddcfc4eb0a1f57b68bd77dd99f0e826958a96abd0ae9bd092e114ffed6"
dependencies = [
"proc-macro2",
"quote",
@@ -624,7 +624,7 @@ checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
dependencies = [
"libc",
"redox_users",
"winapi 0.3.9",
"winapi",
]
[[package]]
@@ -635,7 +635,7 @@ checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
dependencies = [
"libc",
"redox_users",
"winapi 0.3.9",
"winapi",
]
[[package]]
@@ -665,6 +665,27 @@ version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
name = "errno"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
dependencies = [
"errno-dragonfly",
"libc",
"winapi",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "fastrand"
version = "1.8.0"
@@ -703,10 +724,10 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8-to-ruff"
version = "0.0.153-dev.0"
version = "0.0.159-dev.0"
dependencies = [
"anyhow",
"clap 4.0.22",
"clap 4.0.29",
"configparser",
"once_cell",
"regex",
@@ -719,9 +740,9 @@ dependencies = [
[[package]]
name = "flate2"
version = "1.0.24"
version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6"
checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
dependencies = [
"crc32fast",
"miniz_oxide",
@@ -742,41 +763,15 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "fsevent"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6"
dependencies = [
"bitflags",
"fsevent-sys",
]
[[package]]
name = "fsevent-sys"
version = "2.0.1"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f41b048a94555da0f42f1d632e2e19510084fb8e303b0daa2816e733fb3644a0"
checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2"
dependencies = [
"libc",
]
[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
dependencies = [
"bitflags",
"fuchsia-zircon-sys",
]
[[package]]
name = "fuchsia-zircon-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
[[package]]
name = "getrandom"
version = "0.1.16"
@@ -844,6 +839,15 @@ dependencies = [
"libc",
]
[[package]]
name = "hermit-abi"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
dependencies = [
"libc",
]
[[package]]
name = "hexf-parse"
version = "0.2.1"
@@ -861,7 +865,7 @@ dependencies = [
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"winapi 0.3.9",
"winapi",
]
[[package]]
@@ -896,9 +900,9 @@ dependencies = [
[[package]]
name = "inotify"
version = "0.7.1"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4816c66d2c8ae673df83366c18341538f234a26d65a9ecea5c348b453ac1d02f"
checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff"
dependencies = [
"bitflags",
"inotify-sys",
@@ -916,9 +920,9 @@ dependencies = [
[[package]]
name = "insta"
version = "1.21.0"
version = "1.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "581d4e3314cae4536e5d22ffd23189d4a374696c5ef733eadafae0ed273fd303"
checksum = "197f4e300af8b23664d4077bf5c40e0afa9ba66a567bb5a51d3def3c7b287d1c"
dependencies = [
"console",
"lazy_static",
@@ -938,12 +942,25 @@ dependencies = [
]
[[package]]
name = "iovec"
version = "0.1.4"
name = "io-lifetimes"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "is-terminal"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330"
dependencies = [
"hermit-abi 0.2.6",
"io-lifetimes",
"rustix",
"windows-sys",
]
[[package]]
@@ -977,13 +994,23 @@ dependencies = [
]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
name = "kqueue"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
checksum = "2c8fc60ba15bf51257aa9807a48a61013db043fcf3a78cb0d916e8e396dcad98"
dependencies = [
"winapi 0.2.8",
"winapi-build",
"kqueue-sys",
"libc",
]
[[package]]
name = "kqueue-sys"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8367585489f01bc55dd27404dcf56b95e6da061a256a666ab23be9ba96a2e587"
dependencies = [
"bitflags",
"libc",
]
[[package]]
@@ -1024,12 +1051,6 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "lexical-parse-float"
version = "0.8.5"
@@ -1062,9 +1083,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.137"
version = "0.2.138"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
[[package]]
name = "libcst"
@@ -1105,6 +1126,12 @@ version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "linux-raw-sys"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f"
[[package]]
name = "lock_api"
version = "0.4.9"
@@ -1147,74 +1174,32 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "memoffset"
version = "0.6.5"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
dependencies = [
"autocfg",
]
[[package]]
name = "miniz_oxide"
version = "0.5.4"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34"
checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
dependencies = [
"adler",
]
[[package]]
name = "mio"
version = "0.6.23"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4"
checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de"
dependencies = [
"cfg-if 0.1.10",
"fuchsia-zircon",
"fuchsia-zircon-sys",
"iovec",
"kernel32-sys",
"libc",
"log",
"miow",
"net2",
"slab",
"winapi 0.2.8",
]
[[package]]
name = "mio-extras"
version = "2.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19"
dependencies = [
"lazycell",
"log",
"mio",
"slab",
]
[[package]]
name = "miow"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d"
dependencies = [
"kernel32-sys",
"net2",
"winapi 0.2.8",
"ws2_32-sys",
]
[[package]]
name = "net2"
version = "0.2.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74d0df99cfcd2530b2e694f6e17e7f37b8e26bb23983ac530c0c97408837c631"
dependencies = [
"cfg-if 0.1.10",
"libc",
"winapi 0.3.9",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys",
]
[[package]]
@@ -1231,9 +1216,9 @@ checksum = "d906846a98739ed9d73d66e62c2641eef8321f1734b7a1156ab045a0248fb2b3"
[[package]]
name = "nix"
version = "0.24.2"
version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc"
checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069"
dependencies = [
"bitflags",
"cfg-if 1.0.0",
@@ -1258,20 +1243,20 @@ dependencies = [
[[package]]
name = "notify"
version = "4.0.17"
version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae03c8c853dba7bfd23e571ff0cff7bc9dceb40a4cd684cd1681824183f45257"
checksum = "ed2c66da08abae1c024c01d635253e402341b4060a12e99b31c7594063bf490a"
dependencies = [
"bitflags",
"crossbeam-channel",
"filetime",
"fsevent",
"fsevent-sys",
"inotify",
"kqueue",
"libc",
"mio",
"mio-extras",
"walkdir",
"winapi 0.3.9",
"winapi",
]
[[package]]
@@ -1321,7 +1306,7 @@ version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
dependencies = [
"hermit-abi",
"hermit-abi 0.1.19",
"libc",
]
@@ -1339,9 +1324,9 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "os_str_bytes"
version = "6.3.1"
version = "6.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3baf96e39c5359d2eb0dd6ccb42c62b91d9678aa68160d261b9e0ccbf9e9dea9"
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
[[package]]
name = "parking_lot"
@@ -1355,9 +1340,9 @@ dependencies = [
[[package]]
name = "parking_lot_core"
version = "0.9.4"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0"
checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba"
dependencies = [
"cfg-if 1.0.0",
"libc",
@@ -1557,9 +1542,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
[[package]]
name = "predicates"
version = "2.1.2"
version = "2.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab68289ded120dcbf9d571afcf70163233229052aec9b08ab09532f698d0e1e6"
checksum = "f54fc5dc63ed3bbf19494623db4f3af16842c0d975818e469022d09e53f0aa05"
dependencies = [
"difflib",
"itertools",
@@ -1568,15 +1553,15 @@ dependencies = [
[[package]]
name = "predicates-core"
version = "1.0.4"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6e7125585d872860e9955ca571650b27a4979c5823084168c5ed5bbfb016b56"
checksum = "72f883590242d3c6fc5bf50299011695fa6590c2c70eac95ee1bdb9a733ad1a2"
[[package]]
name = "predicates-tree"
version = "1.0.6"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad3f7fa8d61e139cbc7c3edfebf3b6678883a53f5ffac65d1259329a93ee43a5"
checksum = "54ff541861505aabf6ea722d2131ee980b8276e10a1297b94e896dd8b621850d"
dependencies = [
"predicates-core",
"termtree",
@@ -1736,11 +1721,10 @@ dependencies = [
[[package]]
name = "rayon"
version = "1.5.3"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d"
checksum = "1e060280438193c554f654141c9ea9417886713b7acd75974c85b18a69a88e0b"
dependencies = [
"autocfg",
"crossbeam-deque",
"either",
"rayon-core",
@@ -1748,9 +1732,9 @@ dependencies = [
[[package]]
name = "rayon-core"
version = "1.9.3"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f"
checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
@@ -1807,7 +1791,7 @@ version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi 0.3.9",
"winapi",
]
[[package]]
@@ -1822,7 +1806,7 @@ dependencies = [
"spin",
"untrusted",
"web-sys",
"winapi 0.3.9",
"winapi",
]
[[package]]
@@ -1837,7 +1821,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.153"
version = "0.0.159"
dependencies = [
"annotate-snippets 0.9.1",
"anyhow",
@@ -1847,7 +1831,7 @@ dependencies = [
"bitflags",
"cachedir",
"chrono",
"clap 4.0.22",
"clap 4.0.29",
"clap_complete_command",
"clearscreen",
"colored",
@@ -1889,13 +1873,14 @@ dependencies = [
[[package]]
name = "ruff_dev"
version = "0.0.153"
version = "0.0.159"
dependencies = [
"anyhow",
"clap 4.0.22",
"clap 4.0.29",
"codegen",
"itertools",
"libcst",
"once_cell",
"ruff",
"rustpython-ast",
"rustpython-common",
@@ -1910,6 +1895,20 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustix"
version = "0.36.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb93e85278e08bb5788653183213d3a60fc242b10cb9be96586f5a73dcb67c23"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "rustls"
version = "0.20.7"
@@ -1925,7 +1924,7 @@ dependencies = [
[[package]]
name = "rustpython-ast"
version = "0.1.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
source = "git+https://github.com/RustPython/RustPython.git?rev=28f9f65ccc625f00835d84bbb5fba274dce5aa89#28f9f65ccc625f00835d84bbb5fba274dce5aa89"
dependencies = [
"num-bigint",
"rustpython-common",
@@ -1935,7 +1934,7 @@ dependencies = [
[[package]]
name = "rustpython-common"
version = "0.0.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
source = "git+https://github.com/RustPython/RustPython.git?rev=28f9f65ccc625f00835d84bbb5fba274dce5aa89#28f9f65ccc625f00835d84bbb5fba274dce5aa89"
dependencies = [
"ascii",
"cfg-if 1.0.0",
@@ -1958,7 +1957,7 @@ dependencies = [
[[package]]
name = "rustpython-compiler-core"
version = "0.1.2"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
source = "git+https://github.com/RustPython/RustPython.git?rev=28f9f65ccc625f00835d84bbb5fba274dce5aa89#28f9f65ccc625f00835d84bbb5fba274dce5aa89"
dependencies = [
"bincode",
"bitflags",
@@ -1975,7 +1974,7 @@ dependencies = [
[[package]]
name = "rustpython-parser"
version = "0.1.2"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
source = "git+https://github.com/RustPython/RustPython.git?rev=28f9f65ccc625f00835d84bbb5fba274dce5aa89#28f9f65ccc625f00835d84bbb5fba274dce5aa89"
dependencies = [
"ahash",
"anyhow",
@@ -2048,18 +2047,18 @@ checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
[[package]]
name = "serde"
version = "1.0.147"
version = "1.0.148"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965"
checksum = "e53f64bb4ba0191d6d0676e1b141ca55047d83b74f5607e6d8eb88126c52c2dc"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.147"
version = "1.0.148"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852"
checksum = "a55492425aa53521babf6137309e7d34c20bbfbbfcfe2c7f3a047fd1f6b92c0c"
dependencies = [
"proc-macro2",
"quote",
@@ -2068,9 +2067,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.87"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45"
checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
dependencies = [
"itoa",
"ryu",
@@ -2079,9 +2078,9 @@ dependencies = [
[[package]]
name = "similar"
version = "2.2.0"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62ac7f900db32bf3fd12e0117dd3dc4da74bc52ebaac97f39668446d89694803"
checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf"
[[package]]
name = "siphasher"
@@ -2089,15 +2088,6 @@ version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
[[package]]
name = "slab"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
dependencies = [
"autocfg",
]
[[package]]
name = "smallvec"
version = "1.10.0"
@@ -2171,9 +2161,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.103"
version = "1.0.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d"
checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
dependencies = [
"proc-macro2",
"quote",
@@ -2191,7 +2181,7 @@ dependencies = [
"libc",
"redox_syscall",
"remove_dir_all",
"winapi 0.3.9",
"winapi",
]
[[package]]
@@ -2202,7 +2192,7 @@ checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f"
dependencies = [
"dirs-next",
"rustversion",
"winapi 0.3.9",
"winapi",
]
[[package]]
@@ -2221,7 +2211,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
dependencies = [
"libc",
"winapi 0.3.9",
"winapi",
]
[[package]]
@@ -2304,7 +2294,7 @@ checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
dependencies = [
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
"winapi 0.3.9",
"winapi",
]
[[package]]
@@ -2567,7 +2557,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
dependencies = [
"same-file",
"winapi 0.3.9",
"winapi",
"winapi-util",
]
@@ -2689,12 +2679,6 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983"
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
[[package]]
name = "winapi"
version = "0.3.9"
@@ -2705,12 +2689,6 @@ dependencies = [
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
@@ -2723,7 +2701,7 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi 0.3.9",
"winapi",
]
[[package]]
@@ -2789,16 +2767,6 @@ version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
[[package]]
name = "ws2_32-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
dependencies = [
"winapi 0.2.8",
"winapi-build",
]
[[package]]
name = "yaml-rust"
version = "0.4.5"
@@ -2814,5 +2782,5 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe5c30ade05e61656247b2e334a031dfd0cc466fadef865bdcdea8d537951bf1"
dependencies = [
"winapi 0.3.9",
"winapi",
]

View File

@@ -6,7 +6,7 @@ members = [
[package]
name = "ruff"
version = "0.0.153"
version = "0.0.159"
edition = "2021"
rust-version = "1.65.0"
@@ -33,7 +33,7 @@ itertools = { version = "0.10.5" }
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a8725d161fe8b3ed73a6758b21e177" }
log = { version = "0.4.17" }
nohash-hasher = { version = "0.2.0" }
notify = { version = "4.0.17" }
notify = { version = "5.0.0" }
num-bigint = { version = "0.4.3" }
once_cell = { version = "1.16.0" }
path-absolutize = { version = "3.0.14", features = ["once_cell_cache", "use_unix_paths_on_wasm"] }
@@ -42,9 +42,9 @@ rayon = { version = "1.5.3" }
regex = { version = "1.6.0" }
ropey = { version = "1.5.0", features = ["cr_lines", "simd"], default-features = false }
rustc-hash = { version = "1.1.0" }
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "f885db8c61514f069979861f6b3bd83292086231" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "f885db8c61514f069979861f6b3bd83292086231" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "f885db8c61514f069979861f6b3bd83292086231" }
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "28f9f65ccc625f00835d84bbb5fba274dce5aa89" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "28f9f65ccc625f00835d84bbb5fba274dce5aa89" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "28f9f65ccc625f00835d84bbb5fba274dce5aa89" }
serde = { version = "1.0.147", features = ["derive"] }
serde_json = { version = "1.0.87" }
strum = { version = "0.24.1", features = ["strum_macros"] }

25
LICENSE
View File

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

333
README.md
View File

@@ -71,28 +71,28 @@ of [Conda](https://docs.conda.io/en/latest/):
1. [Supported Rules](#supported-rules)
1. [Pyflakes (F)](#pyflakes)
1. [pycodestyle (E, W)](#pycodestyle)
1. [mccabe (C90)](#mccabe)
1. [isort (I)](#isort)
1. [pydocstyle (D)](#pydocstyle)
1. [pyupgrade (U)](#pyupgrade)
1. [pyupgrade (UP)](#pyupgrade)
1. [pep8-naming (N)](#pep8-naming)
1. [eradicate (ERA)](#eradicate)
1. [flake8-2020 (YTT)](#flake8-2020)
1. [flake8-annotations (ANN)](#flake8-annotations)
1. [flake8-bandit (S)](#flake8-bandit)
1. [flake8-comprehensions (C4)](#flake8-comprehensions)
1. [flake8-blind-except (BLE)](#flake8-blind-except)
1. [flake8-boolean-trap (FBT)](#flake8-boolean-trap)
1. [flake8-bugbear (B)](#flake8-bugbear)
1. [flake8-builtins (A)](#flake8-builtins)
1. [flake8-comprehensions (C4)](#flake8-comprehensions)
1. [flake8-debugger (T10)](#flake8-debugger)
1. [flake8-tidy-imports (I25)](#flake8-tidy-imports)
1. [flake8-print (T20)](#flake8-print)
1. [flake8-quotes (Q)](#flake8-quotes)
1. [flake8-annotations (ANN)](#flake8-annotations)
1. [flake8-2020 (YTT)](#flake8-2020)
1. [flake8-blind-except (BLE)](#flake8-blind-except)
1. [mccabe (C90)](#mccabe)
1. [flake8-return (RET)](#flake8-return)
1. [flake8-tidy-imports (I25)](#flake8-tidy-imports)
1. [eradicate (ERA)](#eradicate)
1. [pygrep-hooks (PGH)](#pygrep-hooks)
1. [Pylint (PL)](#pylint)
1. [Ruff-specific rules (RUF)](#ruff-specific-rules)
1. [Meta rules (M)](#meta-rules)
1. [Editor Integrations](#editor-integrations)
1. [FAQ](#faq)
1. [Development](#development)
@@ -145,7 +145,7 @@ Ruff also works with [pre-commit](https://pre-commit.com):
```yaml
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.153
rev: v0.0.159
hooks:
- id: ruff
```
@@ -354,13 +354,13 @@ For targeted exclusions across entire files (e.g., "Ignore all F841 violations i
Ruff supports several workflows to aid in `noqa` management.
First, Ruff provides a special error code, `M001`, to enforce that your `noqa` directives are
First, Ruff provides a special error code, `RUF100`, to enforce that your `noqa` directives are
"valid", in that the errors they _say_ they ignore are actually being triggered on that line (and
thus suppressed). You can run `ruff /path/to/file.py --extend-select M001` to flag unused `noqa`
thus suppressed). You can run `ruff /path/to/file.py --extend-select RUF100` to flag unused `noqa`
directives.
Second, Ruff can _automatically remove_ unused `noqa` directives via its autofix functionality.
You can run `ruff /path/to/file.py --extend-select M001 --fix` to automatically remove unused
You can run `ruff /path/to/file.py --extend-select RUF100 --fix` to automatically remove unused
`noqa` directives.
Third, Ruff can _automatically add_ `noqa` directives to all failing lines. This is useful when
@@ -450,6 +450,14 @@ For more, see [pycodestyle](https://pypi.org/project/pycodestyle/2.9.1/) on PyPI
| W292 | NoNewLineAtEndOfFile | No newline at end of file | |
| W605 | InvalidEscapeSequence | Invalid escape sequence: '\c' | |
### mccabe
For more, see [mccabe](https://pypi.org/project/mccabe/0.7.0/) on PyPI.
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| C901 | FunctionIsTooComplex | `...` is too complex (10) | |
### isort
For more, see [isort](https://pypi.org/project/isort/5.10.1/) on PyPI.
@@ -515,20 +523,20 @@ For more, see [pyupgrade](https://pypi.org/project/pyupgrade/3.2.0/) on PyPI.
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| U001 | UselessMetaclassType | `__metaclass__ = type` is implied | 🛠 |
| U003 | TypeOfPrimitive | Use `str` instead of `type(...)` | 🛠 |
| U004 | UselessObjectInheritance | Class `...` inherits from object | 🛠 |
| U005 | DeprecatedUnittestAlias | `assertEquals` is deprecated, use `assertEqual` instead | 🛠 |
| U006 | UsePEP585Annotation | Use `list` instead of `List` for type annotations | 🛠 |
| U007 | UsePEP604Annotation | Use `X \| Y` for type annotations | 🛠 |
| U008 | SuperCallWithParameters | Use `super()` instead of `super(__class__, self)` | 🛠 |
| U009 | PEP3120UnnecessaryCodingComment | UTF-8 encoding declaration is unnecessary | 🛠 |
| 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 `...` from `TypedDict` functional to class syntax | 🛠 |
| U014 | ConvertNamedTupleFunctionalToClass | Convert `...` from `NamedTuple` functional to class syntax | 🛠 |
| U015 | RedundantOpenModes | Unnecessary open mode parameters | 🛠 |
| UP001 | UselessMetaclassType | `__metaclass__ = type` is implied | 🛠 |
| UP003 | TypeOfPrimitive | Use `str` instead of `type(...)` | 🛠 |
| UP004 | UselessObjectInheritance | Class `...` inherits from object | 🛠 |
| UP005 | DeprecatedUnittestAlias | `assertEquals` is deprecated, use `assertEqual` instead | 🛠 |
| UP006 | UsePEP585Annotation | Use `list` instead of `List` for type annotations | 🛠 |
| UP007 | UsePEP604Annotation | Use `X \| Y` for type annotations | 🛠 |
| UP008 | SuperCallWithParameters | Use `super()` instead of `super(__class__, self)` | 🛠 |
| UP009 | PEP3120UnnecessaryCodingComment | UTF-8 encoding declaration is unnecessary | 🛠 |
| UP010 | UnnecessaryFutureImport | Unnecessary `__future__` import `...` for target Python version | 🛠 |
| UP011 | UnnecessaryLRUCacheParams | Unnecessary parameters to `functools.lru_cache` | 🛠 |
| UP012 | UnnecessaryEncodeUTF8 | Unnecessary call to `encode` as UTF-8 | 🛠 |
| UP013 | ConvertTypedDictFunctionalToClass | Convert `...` from `TypedDict` functional to class syntax | 🛠 |
| UP014 | ConvertNamedTupleFunctionalToClass | Convert `...` from `NamedTuple` functional to class syntax | 🛠 |
| UP015 | RedundantOpenModes | Unnecessary open mode parameters | 🛠 |
### pep8-naming
@@ -552,13 +560,40 @@ For more, see [pep8-naming](https://pypi.org/project/pep8-naming/0.13.2/) on PyP
| N817 | CamelcaseImportedAsAcronym | Camelcase `...` imported as acronym `...` | |
| N818 | ErrorSuffixOnExceptionName | Exception name `...` should be named with an Error suffix | |
### eradicate
### flake8-2020
For more, see [eradicate](https://pypi.org/project/eradicate/2.1.0/) on PyPI.
For more, see [flake8-2020](https://pypi.org/project/flake8-2020/1.7.0/) on PyPI.
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| ERA001 | CommentedOutCode | Found commented-out code | 🛠 |
| YTT101 | SysVersionSlice3Referenced | `sys.version[:3]` referenced (python3.10), use `sys.version_info` | |
| YTT102 | SysVersion2Referenced | `sys.version[2]` referenced (python3.10), use `sys.version_info` | |
| YTT103 | SysVersionCmpStr3 | `sys.version` compared to string (python3.10), use `sys.version_info` | |
| YTT201 | SysVersionInfo0Eq3Referenced | `sys.version_info[0] == 3` referenced (python4), use `>=` | |
| YTT202 | SixPY3Referenced | `six.PY3` referenced (python4), use `not six.PY2` | |
| YTT203 | SysVersionInfo1CmpInt | `sys.version_info[1]` compared to integer (python4), compare `sys.version_info` to tuple | |
| YTT204 | SysVersionInfoMinorCmpInt | `sys.version_info.minor` compared to integer (python4), compare `sys.version_info` to tuple | |
| YTT301 | SysVersion0Referenced | `sys.version[0]` referenced (python10), use `sys.version_info` | |
| YTT302 | SysVersionCmpStr10 | `sys.version` compared to string (python10), use `sys.version_info` | |
| YTT303 | SysVersionSlice1Referenced | `sys.version[:1]` referenced (python10), use `sys.version_info` | |
### flake8-annotations
For more, see [flake8-annotations](https://pypi.org/project/flake8-annotations/2.9.1/) on PyPI.
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| ANN001 | MissingTypeFunctionArgument | Missing type annotation for function argument `...` | |
| ANN002 | MissingTypeArgs | Missing type annotation for `*...` | |
| ANN003 | MissingTypeKwargs | Missing type annotation for `**...` | |
| ANN101 | MissingTypeSelf | Missing type annotation for `...` in method | |
| ANN102 | MissingTypeCls | Missing type annotation for `...` in classmethod | |
| ANN201 | MissingReturnTypePublicFunction | Missing return type annotation for public function `...` | |
| ANN202 | MissingReturnTypePrivateFunction | Missing return type annotation for private function `...` | |
| ANN204 | MissingReturnTypeMagicMethod | Missing return type annotation for magic method `...` | |
| ANN205 | MissingReturnTypeStaticMethod | Missing return type annotation for staticmethod `...` | |
| ANN206 | MissingReturnTypeClassMethod | Missing return type annotation for classmethod `...` | |
| ANN401 | DynamicallyTypedExpression | Dynamically typed expressions (typing.Any) are disallowed in `...` | |
### flake8-bandit
@@ -573,36 +608,13 @@ For more, see [flake8-bandit](https://pypi.org/project/flake8-bandit/4.1.1/) on
| S106 | HardcodedPasswordFuncArg | Possible hardcoded password: `"..."` | |
| S107 | HardcodedPasswordDefault | Possible hardcoded password: `"..."` | |
### flake8-comprehensions
### flake8-blind-except
For more, see [flake8-comprehensions](https://pypi.org/project/flake8-comprehensions/3.10.1/) on PyPI.
For more, see [flake8-blind-except](https://pypi.org/project/flake8-blind-except/0.2.1/) on PyPI.
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| C400 | UnnecessaryGeneratorList | Unnecessary generator (rewrite as a `list` comprehension) | 🛠 |
| C401 | UnnecessaryGeneratorSet | Unnecessary generator (rewrite as a `set` comprehension) | 🛠 |
| C402 | UnnecessaryGeneratorDict | Unnecessary generator (rewrite as a `dict` comprehension) | 🛠 |
| C403 | UnnecessaryListComprehensionSet | Unnecessary `list` comprehension (rewrite as a `set` comprehension) | 🛠 |
| C404 | UnnecessaryListComprehensionDict | Unnecessary `list` comprehension (rewrite as a `dict` comprehension) | 🛠 |
| C405 | UnnecessaryLiteralSet | Unnecessary `(list\|tuple)` literal (rewrite as a `set` literal) | 🛠 |
| C406 | UnnecessaryLiteralDict | Unnecessary `(list\|tuple)` literal (rewrite as a `dict` literal) | 🛠 |
| C408 | UnnecessaryCollectionCall | Unnecessary `(dict\|list\|tuple)` call (rewrite as a literal) | 🛠 |
| C409 | UnnecessaryLiteralWithinTupleCall | Unnecessary `(list\|tuple)` literal passed to `tuple()` (remove the outer call to `tuple()`) | 🛠 |
| C410 | UnnecessaryLiteralWithinListCall | Unnecessary `(list\|tuple)` literal passed to `list()` (rewrite as a `list` literal) | 🛠 |
| C411 | UnnecessaryListCall | Unnecessary `list` call (remove the outer call to `list()`) | 🛠 |
| C413 | UnnecessaryCallAroundSorted | Unnecessary `(list\|reversed)` call around `sorted()` | |
| C414 | UnnecessaryDoubleCastOrProcess | Unnecessary `(list\|reversed\|set\|sorted\|tuple)` call within `(list\|set\|sorted\|tuple)()` | |
| C415 | UnnecessarySubscriptReversal | Unnecessary subscript reversal of iterable within `(reversed\|set\|sorted)()` | |
| C416 | UnnecessaryComprehension | Unnecessary `(list\|set)` comprehension (rewrite using `(list\|set)()`) | 🛠 |
| C417 | UnnecessaryMap | Unnecessary `map` usage (rewrite using a `(list\|set\|dict)` comprehension) | |
### flake8-debugger
For more, see [flake8-debugger](https://pypi.org/project/flake8-debugger/4.1.2/) on PyPI.
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| T100 | Debugger | Import for `...` found | |
| BLE001 | BlindExcept | Blind except Exception: statement | |
### flake8-boolean-trap
@@ -658,13 +670,36 @@ For more, see [flake8-builtins](https://pypi.org/project/flake8-builtins/2.0.1/)
| A002 | BuiltinArgumentShadowing | Argument `...` is shadowing a python builtin | |
| A003 | BuiltinAttributeShadowing | Class attribute `...` is shadowing a python builtin | |
### flake8-tidy-imports
### flake8-comprehensions
For more, see [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports/4.8.0/) on PyPI.
For more, see [flake8-comprehensions](https://pypi.org/project/flake8-comprehensions/3.10.1/) on PyPI.
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| I252 | BannedRelativeImport | Relative imports are banned | |
| C400 | UnnecessaryGeneratorList | Unnecessary generator (rewrite as a `list` comprehension) | 🛠 |
| C401 | UnnecessaryGeneratorSet | Unnecessary generator (rewrite as a `set` comprehension) | 🛠 |
| C402 | UnnecessaryGeneratorDict | Unnecessary generator (rewrite as a `dict` comprehension) | 🛠 |
| C403 | UnnecessaryListComprehensionSet | Unnecessary `list` comprehension (rewrite as a `set` comprehension) | 🛠 |
| C404 | UnnecessaryListComprehensionDict | Unnecessary `list` comprehension (rewrite as a `dict` comprehension) | 🛠 |
| C405 | UnnecessaryLiteralSet | Unnecessary `(list\|tuple)` literal (rewrite as a `set` literal) | 🛠 |
| C406 | UnnecessaryLiteralDict | Unnecessary `(list\|tuple)` literal (rewrite as a `dict` literal) | 🛠 |
| C408 | UnnecessaryCollectionCall | Unnecessary `(dict\|list\|tuple)` call (rewrite as a literal) | 🛠 |
| C409 | UnnecessaryLiteralWithinTupleCall | Unnecessary `(list\|tuple)` literal passed to `tuple()` (remove the outer call to `tuple()`) | 🛠 |
| C410 | UnnecessaryLiteralWithinListCall | Unnecessary `(list\|tuple)` literal passed to `list()` (rewrite as a `list` literal) | 🛠 |
| C411 | UnnecessaryListCall | Unnecessary `list` call (remove the outer call to `list()`) | 🛠 |
| C413 | UnnecessaryCallAroundSorted | Unnecessary `(list\|reversed)` call around `sorted()` | |
| C414 | UnnecessaryDoubleCastOrProcess | Unnecessary `(list\|reversed\|set\|sorted\|tuple)` call within `(list\|set\|sorted\|tuple)()` | |
| C415 | UnnecessarySubscriptReversal | Unnecessary subscript reversal of iterable within `(reversed\|set\|sorted)()` | |
| C416 | UnnecessaryComprehension | Unnecessary `(list\|set)` comprehension (rewrite using `(list\|set)()`) | 🛠 |
| C417 | UnnecessaryMap | Unnecessary `map` usage (rewrite using a `(list\|set\|dict)` comprehension) | |
### flake8-debugger
For more, see [flake8-debugger](https://pypi.org/project/flake8-debugger/4.1.2/) on PyPI.
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| T100 | Debugger | Import for `...` found | |
### flake8-print
@@ -686,56 +721,36 @@ For more, see [flake8-quotes](https://pypi.org/project/flake8-quotes/3.3.1/) on
| Q002 | BadQuotesDocstring | Single quote docstring found but double quotes preferred | |
| Q003 | AvoidQuoteEscape | Change outer quotes to avoid escaping inner quotes | |
### flake8-annotations
### flake8-return
For more, see [flake8-annotations](https://pypi.org/project/flake8-annotations/2.9.1/) on PyPI.
For more, see [flake8-return](https://pypi.org/project/flake8-return/1.2.0/) on PyPI.
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| ANN001 | MissingTypeFunctionArgument | Missing type annotation for function argument `...` | |
| ANN002 | MissingTypeArgs | Missing type annotation for `*...` | |
| ANN003 | MissingTypeKwargs | Missing type annotation for `**...` | |
| ANN101 | MissingTypeSelf | Missing type annotation for `...` in method | |
| ANN102 | MissingTypeCls | Missing type annotation for `...` in classmethod | |
| ANN201 | MissingReturnTypePublicFunction | Missing return type annotation for public function `...` | |
| ANN202 | MissingReturnTypePrivateFunction | Missing return type annotation for private function `...` | |
| ANN204 | MissingReturnTypeMagicMethod | Missing return type annotation for magic method `...` | |
| ANN205 | MissingReturnTypeStaticMethod | Missing return type annotation for staticmethod `...` | |
| ANN206 | MissingReturnTypeClassMethod | Missing return type annotation for classmethod `...` | |
| ANN401 | DynamicallyTypedExpression | Dynamically typed expressions (typing.Any) are disallowed in `...` | |
| RET501 | UnnecessaryReturnNone | Do not explicitly `return None` in function if it is the only possible return value | 🛠 |
| RET502 | ImplicitReturnValue | Do not implicitly `return None` in function able to return non-`None` value | 🛠 |
| RET503 | ImplicitReturn | Missing explicit `return` at the end of function able to return non-`None` value | 🛠 |
| RET504 | UnnecessaryAssign | Unnecessary variable assignment before `return` statement | |
| RET505 | SuperfluousElseReturn | Unnecessary `else` after `return` statement | |
| RET506 | SuperfluousElseRaise | Unnecessary `else` after `raise` statement | |
| RET507 | SuperfluousElseContinue | Unnecessary `else` after `continue` statement | |
| RET508 | SuperfluousElseBreak | Unnecessary `else` after `break` statement | |
### flake8-2020
### flake8-tidy-imports
For more, see [flake8-2020](https://pypi.org/project/flake8-2020/1.7.0/) on PyPI.
For more, see [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports/4.8.0/) on PyPI.
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| YTT101 | SysVersionSlice3Referenced | `sys.version[:3]` referenced (python3.10), use `sys.version_info` | |
| YTT102 | SysVersion2Referenced | `sys.version[2]` referenced (python3.10), use `sys.version_info` | |
| YTT103 | SysVersionCmpStr3 | `sys.version` compared to string (python3.10), use `sys.version_info` | |
| YTT201 | SysVersionInfo0Eq3Referenced | `sys.version_info[0] == 3` referenced (python4), use `>=` | |
| YTT202 | SixPY3Referenced | `six.PY3` referenced (python4), use `not six.PY2` | |
| YTT203 | SysVersionInfo1CmpInt | `sys.version_info[1]` compared to integer (python4), compare `sys.version_info` to tuple | |
| YTT204 | SysVersionInfoMinorCmpInt | `sys.version_info.minor` compared to integer (python4), compare `sys.version_info` to tuple | |
| YTT301 | SysVersion0Referenced | `sys.version[0]` referenced (python10), use `sys.version_info` | |
| YTT302 | SysVersionCmpStr10 | `sys.version` compared to string (python10), use `sys.version_info` | |
| YTT303 | SysVersionSlice1Referenced | `sys.version[:1]` referenced (python10), use `sys.version_info` | |
| I252 | BannedRelativeImport | Relative imports are banned | |
### flake8-blind-except
### eradicate
For more, see [flake8-blind-except](https://pypi.org/project/flake8-blind-except/0.2.1/) on PyPI.
For more, see [eradicate](https://pypi.org/project/eradicate/2.1.0/) on PyPI.
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| BLE001 | BlindExcept | Blind except Exception: statement | |
### mccabe
For more, see [mccabe](https://pypi.org/project/mccabe/0.7.0/) on PyPI.
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| C901 | FunctionIsTooComplex | `...` is too complex (10) | |
| ERA001 | CommentedOutCode | Found commented-out code | 🛠 |
### pygrep-hooks
@@ -751,10 +766,15 @@ For more, see [Pylint](https://pypi.org/project/pylint/2.15.7/) on PyPI.
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| PLR1701 | ConsiderMergingIsinstance | Consider merging these isinstance calls: `isinstance(..., (...))` | |
| PLC0414 | UselessImportAlias | Import alias does not rename original package | 🛠 |
| PLC2201 | MisplacedComparisonConstant | Comparison should be ... | 🛠 |
| PLC3002 | UnnecessaryDirectLambdaCall | Lambda expression called directly. Execute the expression inline instead. | |
| PLR0206 | PropertyWithParameters | Cannot have defined parameters for properties | |
| PLE1142 | AwaitOutsideAsync | `await` should be used within an async function | |
| PLR0206 | PropertyWithParameters | Cannot have defined parameters for properties | |
| PLR0402 | ConsiderUsingFromImport | Consider using `from ... import ...` | |
| PLR1701 | ConsiderMergingIsinstance | Consider merging these isinstance calls: `isinstance(..., (...))` | |
| PLR1722 | ConsiderUsingSysExit | Consider using `sys.exit()` | 🛠 |
| PLW0120 | UselessElseOnLoop | Else clause on loop without a break statement, remove the else and de-indent all the code inside it | |
### Ruff-specific rules
@@ -763,13 +783,7 @@ For more, see [Pylint](https://pypi.org/project/pylint/2.15.7/) 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
| Code | Name | Message | Fix |
| ---- | ---- | ------- | --- |
| M001 | UnusedNOQA | Unused `noqa` directive | 🛠 |
| RUF100 | UnusedNOQA | Unused `noqa` directive | 🛠 |
<!-- End auto-generated sections. -->
@@ -918,6 +932,7 @@ natively, including:
- [`flake8-eradicate`](https://pypi.org/project/flake8-eradicate/)
- [`flake8-print`](https://pypi.org/project/flake8-print/)
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
- [`flake8-return`](https://pypi.org/project/flake8-return/)
- [`flake8-super`](https://pypi.org/project/flake8-super/)
- [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/) (1/3)
- [`mccabe`](https://pypi.org/project/mccabe/)
@@ -965,6 +980,7 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
- [`flake8-eradicate`](https://pypi.org/project/flake8-eradicate/)
- [`flake8-print`](https://pypi.org/project/flake8-print/)
- [`flake8-quotes`](https://pypi.org/project/flake8-quotes/)
- [`flake8-return`](https://pypi.org/project/flake8-return/)
- [`flake8-super`](https://pypi.org/project/flake8-super/)
- [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/) (1/3)
- [`mccabe`](https://pypi.org/project/mccabe/)
@@ -1254,25 +1270,6 @@ Summary
### Options
#### [`dummy_variable_rgx`](#dummy_variable_rgx)
A regular expression used to identify "dummy" variables, or those which should be ignored when evaluating
(e.g.) unused-variable checks.
**Default value**: `"^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"` (matches `_`, `__`, and `_var`, but not `_var_`)
**Type**: `Regex`
**Example usage**:
```toml
[tool.ruff]
# Only ignore variables named "_".
dummy_variable_rgx = "^_$"
```
---
#### [`exclude`](#exclude)
A list of file patterns to exclude from linting.
@@ -1286,7 +1283,7 @@ Exclusions are based on globs, and can be either:
(to exclude any Python files in `directory`). Note that these paths are relative to the
project root (e.g., the directory containing your `pyproject.toml`).
Note that you'll typically want to use [`extend_exclude`](#extend-exclude) to modify the excluded
Note that you'll typically want to use [`extend-exclude`](#extend-exclude) to modify the excluded
paths.
**Default value**: `[".bzr", ".direnv", ".eggs", ".git", ".hg", ".mypy_cache", ".nox", ".pants.d", ".ruff_cache", ".svn", ".tox", ".venv", "__pypackages__", "_build", "buck-out", "build", "dist", "node_modules", "venv"]`
@@ -1493,6 +1490,44 @@ line-length = 120
---
#### [`dummy-variable-rgx`](#dummy-variable-rgx)
A regular expression used to identify "dummy" variables, or those which should be ignored when evaluating
(e.g.) unused-variable checks.
**Default value**: `"^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"` (matches `_`, `__`, and `_var`, but not `_var_`)
**Type**: `Regex`
**Example usage**:
```toml
[tool.ruff]
# Only ignore variables named "_".
dummy-variable-rgx = "^_$"
```
---
#### [`ignore-init-module-imports`](#ignore-init-module-imports)
Avoid automatically removing unused imports in `__init__.py` files. Such imports will still be
flagged, but with a dedicated message suggesting that the import is either added to the module's
`__all__` symbol, or re-exported with a redundant alias (e.g., `import os as os`).
**Default value**: `false`
**Type**: `bool`
**Example usage**:
```toml
[tool.ruff]
ignore-init-module-imports = true
```
---
#### [`format`](#format)
The style in which violation messages should be formatted: `"text"` (default), `"grouped"`
@@ -1772,7 +1807,7 @@ ban-relative-imports = "all"
### `isort`
#### [`known-first-party`](known-first-party)
#### [`known-first-party`](#known-first-party)
A list of modules to consider first-party, regardless of whether they can be identified as such
via introspection of the local filesystem.
@@ -1790,7 +1825,7 @@ known-first-party = ["src"]
---
#### [`known-third-party`](known-third-party)
#### [`known-third-party`](#known-third-party)
A list of modules to consider third-party, regardless of whether they can be identified as such
via introspection of the local filesystem.
@@ -1808,7 +1843,7 @@ known-third-party = ["fastapi"]
---
#### [`extra-standard-library`](extra-standard-library)
#### [`extra-standard-library`](#extra-standard-library)
A list of modules to consider standard-library, in addition to those known to Ruff in advance.
@@ -1823,6 +1858,50 @@ A list of modules to consider standard-library, in addition to those known to Ru
extra-standard-library = ["path"]
```
---
#### [`combine-as-imports`](#combine-as-imports)
Combines as imports on the same line. See isort's [`combine-as-imports`](https://pycqa.github.io/isort/docs/configuration/options.html#combine-as-imports)
option.
**Default value**: `false`
**Type**: `bool`
**Example usage**:
```toml
[tool.ruff.isort]
combine-as-imports = true
```
---
#### [`force-wrap-aliases`](#force-wrap-aliases)
Force `import from` statements with multiple members and at least one alias (e.g., `import A as B`)
to wrap such that every line contains exactly one member. For example, this formatting would be
retained, rather than condensing to a single line:
```py
from .utils import (
test_directory as test_directory,
test_id as test_id
)
```
**Default value**: `false`
**Type**: `bool`
**Example usage**:
```toml
[tool.ruff.isort]
force-wrap-aliases = true
```
### `mccabe`
#### [`max-complexity`](#max-complexity)

11
create_plugin.sh Executable file
View File

@@ -0,0 +1,11 @@
set -euxo pipefail
NAME=$1
mkdir -p src/$1
mkdir -p resources/test/fixtures/$1
touch src/$1/mod.rs
sed -i "" "s/mod flake8_print;/mod flake8_print; mod flake8_return;/g" src/lib.rs
sed -i "" "s|// flake8-print|// flake8-return\n// flake8-print|g" src/checks.rs

View File

@@ -771,7 +771,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8_to_ruff"
version = "0.0.153"
version = "0.0.159"
dependencies = [
"anyhow",
"clap",
@@ -1975,7 +1975,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.153"
version = "0.0.159"
dependencies = [
"anyhow",
"bincode",
@@ -2028,7 +2028,7 @@ dependencies = [
[[package]]
name = "rustpython-ast"
version = "0.1.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
source = "git+https://github.com/RustPython/RustPython.git?rev=28f9f65ccc625f00835d84bbb5fba274dce5aa89#28f9f65ccc625f00835d84bbb5fba274dce5aa89"
dependencies = [
"num-bigint",
"rustpython-common",
@@ -2038,7 +2038,7 @@ dependencies = [
[[package]]
name = "rustpython-common"
version = "0.0.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
source = "git+https://github.com/RustPython/RustPython.git?rev=28f9f65ccc625f00835d84bbb5fba274dce5aa89#28f9f65ccc625f00835d84bbb5fba274dce5aa89"
dependencies = [
"ascii",
"cfg-if 1.0.0",
@@ -2061,7 +2061,7 @@ dependencies = [
[[package]]
name = "rustpython-compiler-core"
version = "0.1.2"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
source = "git+https://github.com/RustPython/RustPython.git?rev=28f9f65ccc625f00835d84bbb5fba274dce5aa89#28f9f65ccc625f00835d84bbb5fba274dce5aa89"
dependencies = [
"bincode",
"bitflags",
@@ -2078,7 +2078,7 @@ dependencies = [
[[package]]
name = "rustpython-parser"
version = "0.1.2"
source = "git+https://github.com/RustPython/RustPython.git?rev=f885db8c61514f069979861f6b3bd83292086231#f885db8c61514f069979861f6b3bd83292086231"
source = "git+https://github.com/RustPython/RustPython.git?rev=28f9f65ccc625f00835d84bbb5fba274dce5aa89#28f9f65ccc625f00835d84bbb5fba274dce5aa89"
dependencies = [
"ahash",
"anyhow",

View File

@@ -1,6 +1,6 @@
[package]
name = "flake8-to-ruff"
version = "0.0.153-dev.0"
version = "0.0.159-dev.0"
edition = "2021"
[lib]

View File

@@ -253,6 +253,7 @@ mod tests {
fixable: None,
format: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: None,
per_file_ignores: None,
select: Some(vec![
@@ -295,6 +296,7 @@ mod tests {
fixable: None,
format: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: Some(100),
per_file_ignores: None,
select: Some(vec![
@@ -337,6 +339,7 @@ mod tests {
fixable: None,
format: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: Some(100),
per_file_ignores: None,
select: Some(vec![
@@ -379,6 +382,7 @@ mod tests {
fixable: None,
format: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: None,
per_file_ignores: None,
select: Some(vec![
@@ -421,6 +425,7 @@ mod tests {
fixable: None,
format: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: None,
per_file_ignores: None,
select: Some(vec![
@@ -471,6 +476,7 @@ mod tests {
fixable: None,
format: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: None,
per_file_ignores: None,
select: Some(vec![
@@ -548,6 +554,7 @@ mod tests {
fixable: None,
format: None,
ignore: Some(vec![]),
ignore_init_module_imports: None,
line_length: None,
per_file_ignores: None,
select: Some(vec![

View File

@@ -17,6 +17,7 @@ pub enum Plugin {
Flake8Eradicate,
Flake8Print,
Flake8Quotes,
Flake8Return,
Flake8TidyImports,
McCabe,
PEP8Naming,
@@ -39,6 +40,7 @@ impl FromStr for Plugin {
"flake8-eradicate" => Ok(Plugin::Flake8BlindExcept),
"flake8-print" => Ok(Plugin::Flake8Print),
"flake8-quotes" => Ok(Plugin::Flake8Quotes),
"flake8-return" => Ok(Plugin::Flake8Return),
"flake8-tidy-imports" => Ok(Plugin::Flake8TidyImports),
"mccabe" => Ok(Plugin::McCabe),
"pep8-naming" => Ok(Plugin::PEP8Naming),
@@ -62,6 +64,7 @@ impl Plugin {
Plugin::Flake8Eradicate => CheckCodePrefix::ERA,
Plugin::Flake8Print => CheckCodePrefix::T2,
Plugin::Flake8Quotes => CheckCodePrefix::Q,
Plugin::Flake8Return => CheckCodePrefix::RET,
Plugin::Flake8TidyImports => CheckCodePrefix::I25,
Plugin::McCabe => CheckCodePrefix::C9,
Plugin::PEP8Naming => CheckCodePrefix::N,
@@ -97,6 +100,7 @@ impl Plugin {
Plugin::Flake8Eradicate => vec![CheckCodePrefix::ERA],
Plugin::Flake8Print => vec![CheckCodePrefix::T2],
Plugin::Flake8Quotes => vec![CheckCodePrefix::Q],
Plugin::Flake8Return => vec![CheckCodePrefix::RET],
Plugin::Flake8TidyImports => vec![CheckCodePrefix::I25],
Plugin::McCabe => vec![CheckCodePrefix::C9],
Plugin::PEP8Naming => vec![CheckCodePrefix::N],
@@ -389,6 +393,7 @@ pub fn infer_plugins_from_codes(codes: &BTreeSet<CheckCodePrefix>) -> Vec<Plugin
Plugin::Flake8Eradicate,
Plugin::Flake8Print,
Plugin::Flake8Quotes,
Plugin::Flake8Return,
Plugin::Flake8TidyImports,
Plugin::PEP8Naming,
Plugin::Pyupgrade,

View File

@@ -31,3 +31,7 @@ build-backend = "maturin"
[tool.maturin]
bindings = "bin"
strip = true
[tool.ruff.isort]
force-wrap-aliases = true
combine-as-imports = true

View File

@@ -2,6 +2,7 @@ breakpoint()
import pdb
import builtins
from builtins import breakpoint
from pdb import set_trace as st
from celery.contrib.rdb import set_trace

View File

@@ -0,0 +1,4 @@
def x(y):
if not y:
return
return None # error

View File

@@ -0,0 +1,65 @@
def x(y):
if not y:
return # error
return 1
# without return value
def x(y):
if not y:
return
return
def x(y):
if y:
return
print()
# with return value
def x(y):
if not y:
return 1
return 2
def x(y):
for i in range(10):
if i > 100:
return i
else:
return 1
def x(y):
try:
return 1
except:
return 2
def x(y):
try:
return 1
finally:
return 2
# inner function
def x(y):
if not y:
return 1
def inner():
return
return 2
def x(y):
if not y:
return
def inner():
return 1

View File

@@ -0,0 +1,116 @@
###
# Errors
###
# if/elif/else
def x(y):
if not y:
return 1
# error
def x(y):
if not y:
print() # error
else:
return 2
def x(y):
if not y:
return 1
print() # error
# for
def x(y):
for i in range(10):
if i > 10:
return i
# error
def x(y):
for i in range(10):
if i > 10:
return i
else:
print() # error
###
# Non-errors
###
# raise as last return
def x(y):
if not y:
return 1
raise Exception
# last line in while loop
def x(y):
while True:
if y > 0:
return 1
y += 1
# exclude empty functions
def x(y):
return None
# return inner with statement
def x(y):
with y:
return 1
async def function():
async with thing as foo:
return foo.bar
# assert as last return
def x(y):
if not y:
return 1
assert False, "Sanity check"
# return value within loop
def bar1(x, y, z):
for i in x:
if i > y:
break
return z
def bar3(x, y, z):
for i in x:
if i > y:
if z:
break
else:
return z
return None
def bar1(x, y, z):
for i in x:
if i < y:
continue
return z
def bar3(x, y, z):
for i in x:
if i < y:
if z:
continue
else:
return z
return None

View File

@@ -0,0 +1,232 @@
###
# Errors
###
def x():
a = 1
return a # error
def x():
b, a = 1, 2
print(b)
return a # error
def x():
a = 1
print()
return a # error
def x():
a = 1
print(a)
a = 2
return a # error
def x():
a = 1
if True:
return a # error
a = 2
print(a)
return a
# Can be refactored false positives
# https://github.com/afonasev/flake8-return/issues/47#issuecomment-1122571066
def get_bar_if_exists(obj):
result = ""
if hasattr(obj, "bar"):
result = str(obj.bar)
return result
# https://github.com/afonasev/flake8-return/issues/47#issue-641117366
def x():
formatted = _USER_AGENT_FORMATTER.format(format_string, **values)
# clean up after any blank components
formatted = formatted.replace("()", "").replace(" ", " ").strip()
return formatted
# https://github.com/afonasev/flake8-return/issues/47#issue-641117366
def user_agent_username(username=None):
if not username:
return ""
username = username.replace(" ", "_") # Avoid spaces or %20.
try:
username.encode("ascii") # just test, but not actually use it
except UnicodeEncodeError:
username = quote(username.encode("utf-8"))
else:
# % is legal in the default $wgLegalTitleChars
# This is so that ops know the real pywikibot will not
# allow a useragent in the username to allow through a hand-coded
# percent-encoded value.
if "%" in username:
username = quote(username)
return username
###
# Non-errors
###
def x(y):
a = 1
print(a)
return a
def x():
a = 1
if y:
return a
a = a + 2
print(a)
return a
def x():
a = {}
a["b"] = 2
return a
def x():
a = []
a.append(2)
return a
def x():
a = lambda x: x
a()
return a
# ignore unpacking
def x():
b, a = [1, 2]
return a
def x():
val = ""
for i in range(5):
val = val + str(i)
return val
def x():
val = ""
i = 5
while i:
val = val + str(i)
i = i - x
return val
# Test cases for using value for assignment then returning it
# See:https://github.com/afonasev/flake8-return/issues/47
def resolve_from_url(self, url: str) -> dict:
local_match = self.local_scope_re.match(url)
if local_match:
schema = get_schema(name=local_match.group(1))
self.store[url] = schema
return schema
raise NotImplementedError(...)
my_dict = {}
def my_func():
foo = calculate_foo()
my_dict["foo_result"] = foo
return foo
# Refactored incorrect false positives
# See test cases above marked: "Can be refactored false positives"
# https://github.com/afonasev/flake8-return/issues/47#issuecomment-1122571066
def get_bar_if_exists(obj):
if hasattr(obj, "bar"):
return str(obj.bar)
return ""
# https://github.com/afonasev/flake8-return/issues/47#issue-641117366
def x():
formatted = _USER_AGENT_FORMATTER.format(format_string, **values)
# clean up after any blank components
return formatted.replace("()", "").replace(" ", " ").strip()
# https://github.com/afonasev/flake8-return/issues/47#issue-641117366
def user_agent_username(username=None):
if not username:
return ""
username = username.replace(" ", "_") # Avoid spaces or %20.
try:
username.encode("ascii") # just test,
# but not actually use it
except UnicodeEncodeError:
username = quote(username.encode("utf-8"))
else:
# % is legal in the default $wgLegalTitleChars
# This is so that ops know the real pywikibot will not
# allow a useragent in the username to allow through a
# hand-coded percent-encoded value.
if "%" in username:
username = quote(username)
return username
# https://github.com/afonasev/flake8-return/issues/116#issue-1367575481
def no_exception_loop():
success = False
for _ in range(10):
try:
success = True
except Exception:
print("exception")
return success
# https://github.com/afonasev/flake8-return/issues/116#issue-1367575481
def no_exception():
success = False
try:
success = True
except Exception:
print("exception")
return success
# https://github.com/afonasev/flake8-return/issues/116#issue-1367575481
def exception():
success = True
try:
print("raising")
raise Exception
except Exception:
success = False
return success
# https://github.com/afonasev/flake8-return/issues/66
def close(self):
any_failed = False
for task in self.tasks:
try:
task()
except BaseException:
any_failed = True
report(traceback.format_exc())
return any_failed

View File

@@ -0,0 +1,141 @@
###
# Error
###
def foo2(x, y, w, z):
if x: # [no-else-return]
a = 1
return y
elif z:
b = 2
return w
else:
c = 3
return z
def foo5(x, y, z):
if x: # [no-else-return]
if y:
a = 4
else:
b = 2
return
elif z:
c = 2
else:
c = 3
return
def foo2(x, y, w, z):
if x:
a = 1
elif z:
b = 2
else:
c = 3
if x: # [no-else-return]
a = 1
return y
elif z:
b = 2
return w
else:
c = 3
return z
def foo1(x, y, z):
if x: # [no-else-return]
a = 1
return y
else:
b = 2
return z
def foo3(x, y, z):
if x:
a = 1
if y: # [no-else-return]
b = 2
return y
else:
c = 3
return x
else:
d = 4
return z
def foo4(x, y):
if x: # [no-else-return]
if y:
a = 4
else:
b = 2
return
else:
c = 3
return
def foo6(x, y):
if x:
if y: # [no-else-return]
a = 4
return
else:
b = 2
else:
c = 3
return
def bar4(x):
if x: # [no-else-return]
return True
else:
try:
return False
except ValueError:
return None
###
# Non-error
###
def bar1(x, y, z):
if x:
return y
return z
def bar2(w, x, y, z):
if x:
return y
if z:
a = 1
else:
return w
return None
def bar3(x, y, z):
if x:
if z:
return y
else:
return z
return None
x = 0
if x == 1:
y = "a"
elif x == 2:
y = "b"
else:
y = "c"

View File

@@ -0,0 +1,112 @@
###
# Errors
###
def foo2(x, y, w, z):
if x: # [no-else-raise]
a = 1
raise Exception(y)
elif z:
b = 2
raise Exception(w)
else:
c = 3
raise Exception(z)
def foo5(x, y, z):
if x: # [no-else-raise]
if y:
a = 4
else:
b = 2
raise Exception(x)
elif z:
raise Exception(y)
else:
c = 3
raise Exception(z)
def foo1(x, y, z):
if x: # [no-else-raise]
a = 1
raise Exception(y)
else:
b = 2
raise Exception(z)
def foo3(x, y, z):
if x:
a = 1
if y: # [no-else-raise]
b = 2
raise Exception(y)
else:
c = 3
raise Exception(x)
else:
d = 4
raise Exception(z)
def foo4(x, y):
if x: # [no-else-raise]
if y:
a = 4
else:
b = 2
raise Exception(x)
else:
c = 3
raise Exception(y)
def foo6(x, y):
if x:
if y: # [no-else-raise]
a = 4
raise Exception(x)
else:
b = 2
else:
c = 3
raise Exception(y)
def bar4(x):
if x: # [no-else-raise]
raise Exception(True)
else:
try:
raise Exception(False)
except ValueError:
raise Exception(None)
###
# Non-errors
###
def bar1(x, y, z):
if x:
raise Exception(y)
raise Exception(z)
def bar2(w, x, y, z):
if x:
raise Exception(y)
if z:
a = 1
else:
raise Exception(w)
raise Exception(None)
def bar3(x, y, z):
if x:
if z:
raise Exception(y)
else:
raise Exception(z)
raise Exception(None)

View File

@@ -0,0 +1,106 @@
###
# Errors
###
def foo2(x, y, w, z):
for i in x:
if i < y: # [no-else-continue]
continue
elif i < w:
continue
else:
a = z
def foo5(x, y, z):
for i in x:
if i < y: # [no-else-continue]
if y:
a = 4
else:
b = 2
continue
elif z:
c = 2
else:
c = 3
continue
###
# Non-errors
###
def foo1(x, y, z):
for i in x:
if i < y: # [no-else-continue]
continue
else:
a = z
def foo3(x, y, z):
for i in x:
if i < y:
a = 1
if z: # [no-else-continue]
b = 2
continue
else:
c = 3
continue
else:
d = 4
continue
def foo4(x, y):
for i in x:
if i < x: # [no-else-continue]
if y:
a = 4
else:
b = 2
continue
else:
c = 3
continue
def foo6(x, y):
for i in x:
if i < x:
if y: # [no-else-continue]
a = 4
continue
else:
b = 2
else:
c = 3
continue
def bar4(x):
for i in range(10):
if x: # [no-else-continue]
continue
else:
try:
return
except ValueError:
continue
def bar1(x, y, z):
for i in x:
if i < y:
continue
return z
def bar3(x, y, z):
for i in x:
if i < y:
if z:
continue
else:
return z
return None

View File

@@ -0,0 +1,138 @@
###
# Errors
###
def foo2(x, y, w, z):
for i in x:
if i > y: # [no-else-break]
break
elif i > w:
break
else:
a = z
def foo5(x, y, z):
for i in x:
if i > y: # [no-else-break]
if y:
a = 4
else:
b = 2
break
elif z:
c = 2
else:
c = 3
break
def foo1(x, y, z):
for i in x:
if i > y: # [no-else-break]
break
else:
a = z
def foo3(x, y, z):
for i in x:
if i > y:
a = 1
if z: # [no-else-break]
b = 2
break
else:
c = 3
break
else:
d = 4
break
def foo4(x, y):
for i in x:
if i > x: # [no-else-break]
if y:
a = 4
else:
b = 2
break
else:
c = 3
break
def foo6(x, y):
for i in x:
if i > x:
if y: # [no-else-break]
a = 4
break
else:
b = 2
else:
c = 3
break
def bar4(x):
for i in range(10):
if x: # [no-else-break]
break
else:
try:
return
except ValueError:
break
###
# Non-errors
###
def bar1(x, y, z):
for i in x:
if i > y:
break
return z
def bar2(w, x, y, z):
for i in w:
if i > x:
break
if z < i:
a = y
else:
break
return
def bar3(x, y, z):
for i in x:
if i > y:
if z:
break
else:
return z
return None
def nested1(x, y, z):
for i in x:
if i > x:
for j in y:
if j < z:
break
else:
a = z
def nested2(x, y, z):
for i in x:
if i > x:
for j in y:
if j > z:
break
break
else:
a = z

View File

@@ -0,0 +1,4 @@
from module import Class as C
from module import CONSTANT
from module import function
from module import function as f

View File

@@ -0,0 +1,3 @@
from .a import a1 as a1, a2 as a2
from .b import b1 as b1
from .c import c1

View File

@@ -4,5 +4,18 @@ if x is "abc":
if 123 is not y:
pass
if 123 is \
not y:
pass
if "123" is x < 3:
pass
if "123" != x is 3:
pass
if ("123" != x) is 3:
pass
if "123" != (x is 3):
pass

View File

@@ -34,4 +34,5 @@ def isinstances():
result = isinstance(var[7], int) or not isinstance(var[7], float)
result = isinstance(var[6], int) or isinstance(var[7], float)
result = isinstance(var[6], int) or isinstance(var[7], int)
result = isinstance(var[6], (float, int)) or False
return result

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,27 @@
# pylint: disable=unused-import, missing-docstring, invalid-name, reimported, import-error, wrong-import-order, no-name-in-module, shadowed-import
# Functional tests for import aliasing
# 1. useless-import-alias
# 2. consider-using-from-import
import collections as collections # [useless-import-alias]
from collections import OrderedDict as OrderedDict # [useless-import-alias]
from collections import OrderedDict as o_dict
import os.path as path # [consider-using-from-import]
import os.path as p
import foo.bar.foobar as foobar # [consider-using-from-import]
import os
import os as OS
from sys import version
from . import bar as bar # [useless-import-alias]
from . import bar as Bar
from . import bar
from ..foo import bar as bar # [useless-import-alias]
from ..foo.bar import foobar as foobar # [useless-import-alias]
from ..foo.bar import foobar as anotherfoobar
from . import foo as foo, foo2 as bar2 # [useless-import-alias]
from . import foo as bar, foo2 as foo2 # [useless-import-alias]
from . import foo as bar, foo2 as bar2
from foo.bar import foobar as foobar # [useless-import-alias]
from foo.bar import foobar as foo
from .foo.bar import f as foobar
from ............a import b # [relative-beyond-top-level]

View File

@@ -0,0 +1,50 @@
"""Check that the constants are on the right side of the comparisons"""
# pylint: disable=singleton-comparison, missing-docstring, too-few-public-methods
# pylint: disable=comparison-of-constants
class MyClass:
def __init__(self):
self.attr = 1
def dummy_return(self):
return self.attr
def dummy_return():
return 2
def bad_comparisons():
"""this is not ok"""
instance = MyClass()
for i in range(10):
if 5 <= i: # [misplaced-comparison-constant]
pass
if 1 == i: # [misplaced-comparison-constant]
pass
if 3 < dummy_return(): # [misplaced-comparison-constant]
pass
if 4 != instance.dummy_return(): # [misplaced-comparison-constant]
pass
if 1 == instance.attr: # [misplaced-comparison-constant]
pass
if "aaa" == instance.attr: # [misplaced-comparison-constant]
pass
def good_comparison():
"""this is ok"""
for i in range(10):
if i == 5:
pass
def double_comparison():
"""Check that we return early for non-binary comparison"""
for i in range(10):
if i == 1 == 2:
pass
if 2 <= i <= 8:
print("Between 2 and 8 inclusive")
def const_comparison():
"""Check that we return early for comparison of two constants"""
if 1 == 2:
pass

View File

@@ -0,0 +1,103 @@
"""Check for else branches on loops with break and return only."""
def test_return_for():
"""else + return is not acceptable."""
for i in range(10):
if i % 2:
return i
else: # [useless-else-on-loop]
print("math is broken")
return None
def test_return_while():
"""else + return is not acceptable."""
while True:
return 1
else: # [useless-else-on-loop]
print("math is broken")
return None
while True:
def short_fun():
"""A function with a loop."""
for _ in range(10):
break
else: # [useless-else-on-loop]
print("or else!")
while True:
while False:
break
else: # [useless-else-on-loop]
print("or else!")
for j in range(10):
pass
else: # [useless-else-on-loop]
print("fat chance")
for j in range(10):
break
def test_return_for2():
"""no false positive for break in else
https://bitbucket.org/logilab/pylint/issue/117/useless-else-on-loop-false-positives
"""
for i in range(10):
for _ in range(i):
if i % 2:
break
else:
break
else:
print("great math")
def test_break_in_orelse_deep():
"""no false positive for break in else deeply nested"""
for _ in range(10):
if 1 < 2: # pylint: disable=comparison-of-constants
for _ in range(3):
if 3 < 2: # pylint: disable=comparison-of-constants
break
else:
break
else:
return True
return False
def test_break_in_orelse_deep2():
"""should rise a useless-else-on-loop message, as the break statement is only
for the inner for loop
"""
for _ in range(10):
if 1 < 2: # pylint: disable=comparison-of-constants
for _ in range(3):
if 3 < 2: # pylint: disable=comparison-of-constants
break
else:
print("all right")
else: # [useless-else-on-loop]
return True
return False
def test_break_in_orelse_deep3():
"""no false positive for break deeply nested in else"""
for _ in range(10):
for _ in range(3):
pass
else:
if 1 < 2: # pylint: disable=comparison-of-constants
break
else:
return True
return False

View File

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

View File

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

View File

@@ -0,0 +1,9 @@
from typing import Optional
def f(x: Optional[str]) -> None: # noqa: U007
...
def f(x: Optional[str]) -> None: # noqa: UP007
...

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff_dev"
version = "0.0.153"
version = "0.0.159"
edition = "2021"
[dependencies]
@@ -9,9 +9,10 @@ clap = { version = "4.0.1", features = ["derive"] }
codegen = { version = "0.2.0" }
itertools = { version = "0.10.5" }
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "f2f0b7a487a8725d161fe8b3ed73a6758b21e177" }
once_cell = { version = "1.16.0" }
ruff = { path = ".." }
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "f885db8c61514f069979861f6b3bd83292086231" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "f885db8c61514f069979861f6b3bd83292086231" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "f885db8c61514f069979861f6b3bd83292086231" }
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "28f9f65ccc625f00835d84bbb5fba274dce5aa89" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "28f9f65ccc625f00835d84bbb5fba274dce5aa89" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "28f9f65ccc625f00835d84bbb5fba274dce5aa89" }
strum = { version = "0.24.1", features = ["strum_macros"] }
strum_macros = { version = "0.24.3" }

View File

@@ -8,7 +8,7 @@ use anyhow::Result;
use clap::Parser;
use codegen::{Scope, Type, Variant};
use itertools::Itertools;
use ruff::checks::CheckCode;
use ruff::checks::{CheckCode, REDIRECTS};
use strum::IntoEnumIterator;
const FILE: &str = "src/checks_gen.rs";
@@ -26,18 +26,49 @@ pub fn main(cli: &Cli) -> Result<()> {
// Build up a map from prefix to matching CheckCodes.
let mut prefix_to_codes: BTreeMap<String, BTreeSet<CheckCode>> = BTreeMap::default();
for check_code in CheckCode::iter() {
let as_ref: String = check_code.as_ref().to_string();
let prefix_len = as_ref
let code_str: String = check_code.as_ref().to_string();
let code_prefix_len = code_str
.chars()
.take_while(|char| char.is_alphabetic())
.count();
for i in prefix_len..=as_ref.len() {
let prefix = as_ref[..i].to_string();
let code_suffix_len = code_str.len() - code_prefix_len;
for i in 0..=code_suffix_len {
let prefix = code_str[..code_prefix_len + i].to_string();
let entry = prefix_to_codes.entry(prefix).or_default();
entry.insert(check_code.clone());
}
}
// Add any aliases (e.g., "U001" to "UP001").
for (alias, check_code) in REDIRECTS.iter() {
// Compute the length of the prefix and suffix for both codes.
let code_str: String = check_code.as_ref().to_string();
let code_prefix_len = code_str
.chars()
.take_while(|char| char.is_alphabetic())
.count();
let code_suffix_len = code_str.len() - code_prefix_len;
let alias_prefix_len = alias
.chars()
.take_while(|char| char.is_alphabetic())
.count();
let alias_suffix_len = alias.len() - alias_prefix_len;
assert_eq!(code_suffix_len, alias_suffix_len);
for i in 0..=code_suffix_len {
let source = code_str[..code_prefix_len + i].to_string();
let destination = alias[..alias_prefix_len + i].to_string();
if source != destination {
prefix_to_codes.insert(
destination,
prefix_to_codes
.get(&source)
.unwrap_or_else(|| panic!("Unknown CheckCode: {source:?}"))
.clone(),
);
}
}
}
let mut scope = Scope::new();
// Create the `CheckCodePrefix` definition.
@@ -81,13 +112,27 @@ pub fn main(cli: &Cli) -> Result<()> {
.line("#[allow(clippy::match_same_arms)]")
.line("match self {");
for (prefix, codes) in &prefix_to_codes {
gen = gen.line(format!(
"CheckCodePrefix::{prefix} => vec![{}],",
codes
.iter()
.map(|code| format!("CheckCode::{}", code.as_ref()))
.join(", ")
));
if let Some(target) = REDIRECTS.get(&prefix.as_str()) {
gen = gen.line(format!(
"CheckCodePrefix::{prefix} => {{ eprintln!(\"{{}}{{}} {{}}\", \
\"warning\".yellow().bold(), \":\".bold(), \"`{}` has been renamed to \
`{}`\".bold()); \n vec![{}] }}",
prefix,
target.as_ref(),
codes
.iter()
.map(|code| format!("CheckCode::{}", code.as_ref()))
.join(", ")
));
} else {
gen = gen.line(format!(
"CheckCodePrefix::{prefix} => vec![{}],",
codes
.iter()
.map(|code| format!("CheckCode::{}", code.as_ref()))
.join(", ")
));
}
}
gen.line("}");
@@ -123,6 +168,8 @@ pub fn main(cli: &Cli) -> Result<()> {
.push_str("//! File automatically generated by `examples/generate_check_code_prefix.rs`.");
output.push('\n');
output.push('\n');
output.push_str("use colored::Colorize;");
output.push('\n');
output.push_str("use serde::{{Serialize, Deserialize}};");
output.push('\n');
output.push_str("use strum_macros::EnumString;");

View File

@@ -61,15 +61,6 @@ pub fn dealias_call_path<'a>(
}
}
/// Return `true` if the `Expr` is a name or attribute reference to `${target}`.
pub fn match_name_or_attr(expr: &Expr, target: &str) -> bool {
match &expr.node {
ExprKind::Attribute { attr, .. } => target == attr,
ExprKind::Name { id, .. } => target == id,
_ => false,
}
}
/// Return `true` if the `Expr` is a reference to `${module}.${target}`.
///
/// Useful for, e.g., ensuring that a `Union` reference represents

View File

@@ -1,4 +1,7 @@
use rustpython_ast::{Cmpop, Located};
use rustpython_parser::ast::{Constant, Expr, ExprKind, Stmt, StmtKind};
use rustpython_parser::lexer;
use rustpython_parser::lexer::Tok;
use crate::ast::types::{BindingKind, Scope};
@@ -93,20 +96,164 @@ pub fn in_nested_block<'a>(parents: &mut impl Iterator<Item = &'a Stmt>) -> bool
/// Check if a node represents an unpacking assignment.
pub fn is_unpacking_assignment(stmt: &Stmt) -> bool {
if let StmtKind::Assign { targets, value, .. } = &stmt.node {
if !targets.iter().any(|child| {
matches!(
child.node,
ExprKind::Set { .. } | ExprKind::List { .. } | ExprKind::Tuple { .. }
)
}) {
return false;
}
match &value.node {
ExprKind::Set { .. } | ExprKind::List { .. } | ExprKind::Tuple { .. } => return false,
_ => {}
}
return true;
let StmtKind::Assign { targets, value, .. } = &stmt.node else {
return false;
};
if !targets.iter().any(|child| {
matches!(
child.node,
ExprKind::Set { .. } | ExprKind::List { .. } | ExprKind::Tuple { .. }
)
}) {
return false;
}
!matches!(
&value.node,
ExprKind::Set { .. } | ExprKind::List { .. } | ExprKind::Tuple { .. }
)
}
pub type LocatedCmpop<U = ()> = Located<Cmpop, U>;
/// Extract all `Cmpop` operators from a source code snippet, with appropriate
/// ranges.
///
/// RustPython doesn't include line and column information on `Cmpop` nodes.
/// (CPython doesn't either.) This method iterates over the token stream and
/// re-identifies `Cmpop` nodes, annotating them with valid arnges.
pub fn locate_cmpops(contents: &str) -> Vec<LocatedCmpop> {
let mut tok_iter = lexer::make_tokenizer(contents)
.flatten()
.into_iter()
.peekable();
let mut ops: Vec<LocatedCmpop> = vec![];
let mut count: usize = 0;
loop {
let Some((start, tok, end)) = tok_iter.next() else {
break;
};
if matches!(tok, Tok::Lpar) {
count += 1;
continue;
} else if matches!(tok, Tok::Rpar) {
count -= 1;
continue;
}
if count == 0 {
match tok {
Tok::Not => {
if let Some((_, _, end)) =
tok_iter.next_if(|(_, tok, _)| matches!(tok, Tok::In))
{
ops.push(LocatedCmpop::new(start, end, Cmpop::NotIn));
}
}
Tok::In => {
ops.push(LocatedCmpop::new(start, end, Cmpop::In));
}
Tok::Is => {
if let Some((_, _, end)) =
tok_iter.next_if(|(_, tok, _)| matches!(tok, Tok::Not))
{
ops.push(LocatedCmpop::new(start, end, Cmpop::IsNot));
} else {
ops.push(LocatedCmpop::new(start, end, Cmpop::Is));
}
}
Tok::NotEqual => {
ops.push(LocatedCmpop::new(start, end, Cmpop::NotEq));
}
Tok::EqEqual => {
ops.push(LocatedCmpop::new(start, end, Cmpop::Eq));
}
Tok::GreaterEqual => {
ops.push(LocatedCmpop::new(start, end, Cmpop::GtE));
}
Tok::Greater => {
ops.push(LocatedCmpop::new(start, end, Cmpop::Gt));
}
Tok::LessEqual => {
ops.push(LocatedCmpop::new(start, end, Cmpop::LtE));
}
Tok::Less => {
ops.push(LocatedCmpop::new(start, end, Cmpop::Lt));
}
_ => {}
}
}
}
ops
}
#[cfg(test)]
mod tests {
use rustpython_ast::{Cmpop, Location};
use crate::ast::operations::{locate_cmpops, LocatedCmpop};
#[test]
fn locates_cmpops() {
assert_eq!(
locate_cmpops("x == 1"),
vec![LocatedCmpop::new(
Location::new(1, 2),
Location::new(1, 4),
Cmpop::Eq
)]
);
assert_eq!(
locate_cmpops("x != 1"),
vec![LocatedCmpop::new(
Location::new(1, 2),
Location::new(1, 4),
Cmpop::NotEq
)]
);
assert_eq!(
locate_cmpops("x is 1"),
vec![LocatedCmpop::new(
Location::new(1, 2),
Location::new(1, 4),
Cmpop::Is
)]
);
assert_eq!(
locate_cmpops("x is not 1"),
vec![LocatedCmpop::new(
Location::new(1, 2),
Location::new(1, 8),
Cmpop::IsNot
)]
);
assert_eq!(
locate_cmpops("x in 1"),
vec![LocatedCmpop::new(
Location::new(1, 2),
Location::new(1, 4),
Cmpop::In
)]
);
assert_eq!(
locate_cmpops("x not in 1"),
vec![LocatedCmpop::new(
Location::new(1, 2),
Location::new(1, 8),
Cmpop::NotIn
)]
);
assert_eq!(
locate_cmpops("x != (1 is not 2)"),
vec![LocatedCmpop::new(
Location::new(1, 2),
Location::new(1, 4),
Cmpop::NotEq
)]
);
}
false
}

View File

@@ -36,8 +36,8 @@ 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_debugger,
flake8_print, flake8_tidy_imports, mccabe, pep8_naming, pycodestyle, pydocstyle, pyflakes,
pygrep_hooks, pylint, pyupgrade, rules,
flake8_print, flake8_return, flake8_tidy_imports, mccabe, pep8_naming, pycodestyle, pydocstyle,
pyflakes, pygrep_hooks, pylint, pyupgrade,
};
const GLOBAL_SCOPE_INDEX: usize = 0;
@@ -412,7 +412,7 @@ where
}
}
if self.settings.enabled.contains(&CheckCode::U011)
if self.settings.enabled.contains(&CheckCode::UP011)
&& self.settings.target_version >= PythonVersion::Py38
{
pyupgrade::plugins::unnecessary_lru_cache_params(self, decorator_list);
@@ -421,9 +421,23 @@ where
if self.settings.enabled.contains(&CheckCode::B018) {
flake8_bugbear::plugins::useless_expression(self, body);
}
if self.settings.enabled.contains(&CheckCode::B019) {
flake8_bugbear::plugins::cached_instance_method(self, decorator_list);
}
if self.settings.enabled.contains(&CheckCode::RET501)
|| self.settings.enabled.contains(&CheckCode::RET502)
|| self.settings.enabled.contains(&CheckCode::RET503)
|| self.settings.enabled.contains(&CheckCode::RET504)
|| self.settings.enabled.contains(&CheckCode::RET505)
|| self.settings.enabled.contains(&CheckCode::RET506)
|| self.settings.enabled.contains(&CheckCode::RET507)
|| self.settings.enabled.contains(&CheckCode::RET508)
{
flake8_return::plugins::function(self, body);
}
if self.settings.enabled.contains(&CheckCode::C901) {
if let Some(check) = mccabe::checks::function_is_too_complex(
stmt,
@@ -517,7 +531,7 @@ where
decorator_list,
body,
} => {
if self.settings.enabled.contains(&CheckCode::U004) {
if self.settings.enabled.contains(&CheckCode::UP004) {
pyupgrade::plugins::useless_object_inheritance(
self, stmt, name, bases, keywords,
);
@@ -654,6 +668,14 @@ where
}
}
// pylint
if self.settings.enabled.contains(&CheckCode::PLC0414) {
pylint::plugins::useless_import_alias(self, alias);
}
if self.settings.enabled.contains(&CheckCode::PLR0402) {
pylint::plugins::consider_using_from_import(self, alias);
}
if let Some(asname) = &alias.node.asname {
for alias in names {
if let Some(asname) = &alias.node.asname {
@@ -741,7 +763,7 @@ where
}
if let Some("__future__") = module.as_deref() {
if self.settings.enabled.contains(&CheckCode::U010) {
if self.settings.enabled.contains(&CheckCode::UP010) {
pyupgrade::plugins::unnecessary_future_import(self, stmt, names);
}
}
@@ -946,6 +968,11 @@ where
self.add_check(check);
}
}
// pylint
if self.settings.enabled.contains(&CheckCode::PLC0414) {
pylint::plugins::useless_import_alias(self, alias);
}
}
}
}
@@ -990,16 +1017,27 @@ where
flake8_bugbear::plugins::assert_raises_exception(self, stmt, items);
}
}
StmtKind::While { .. } => {
StmtKind::While { body, orelse, .. } => {
if self.settings.enabled.contains(&CheckCode::B023) {
flake8_bugbear::plugins::function_uses_loop_variable(self, &Node::Stmt(stmt));
}
if self.settings.enabled.contains(&CheckCode::PLW0120) {
pylint::plugins::useless_else_on_loop(self, stmt, body, orelse);
}
}
StmtKind::For {
target, body, iter, ..
target,
body,
iter,
orelse,
..
}
| StmtKind::AsyncFor {
target, body, iter, ..
target,
body,
iter,
orelse,
..
} => {
if self.settings.enabled.contains(&CheckCode::B007) {
flake8_bugbear::plugins::unused_loop_control_variable(self, target, body);
@@ -1010,6 +1048,9 @@ where
if self.settings.enabled.contains(&CheckCode::B023) {
flake8_bugbear::plugins::function_uses_loop_variable(self, &Node::Stmt(stmt));
}
if self.settings.enabled.contains(&CheckCode::PLW0120) {
pylint::plugins::useless_else_on_loop(self, stmt, body, orelse);
}
}
StmtKind::Try { handlers, .. } => {
if self.settings.enabled.contains(&CheckCode::F707) {
@@ -1035,7 +1076,7 @@ where
pycodestyle::plugins::do_not_assign_lambda(self, target, value, stmt);
}
}
if self.settings.enabled.contains(&CheckCode::U001) {
if self.settings.enabled.contains(&CheckCode::UP001) {
pyupgrade::plugins::useless_metaclass_type(self, stmt, value, targets);
}
if self.settings.enabled.contains(&CheckCode::B003) {
@@ -1048,12 +1089,12 @@ where
self.add_check(check);
}
}
if self.settings.enabled.contains(&CheckCode::U013) {
if self.settings.enabled.contains(&CheckCode::UP013) {
pyupgrade::plugins::convert_typed_dict_functional_to_class(
self, stmt, targets, value,
);
}
if self.settings.enabled.contains(&CheckCode::U014) {
if self.settings.enabled.contains(&CheckCode::UP014) {
pyupgrade::plugins::convert_named_tuple_functional_to_class(
self, stmt, targets, value,
);
@@ -1225,7 +1266,7 @@ where
ExprKind::Subscript { value, slice, .. } => {
// Ex) Optional[...]
if !self.in_deferred_string_type_definition
&& self.settings.enabled.contains(&CheckCode::U007)
&& self.settings.enabled.contains(&CheckCode::UP007)
&& (self.settings.target_version >= PythonVersion::Py310
|| (self.settings.target_version >= PythonVersion::Py37
&& !self.settings.pyupgrade.keep_runtime_typing
@@ -1268,7 +1309,7 @@ where
ExprContext::Load => {
// Ex) List[...]
if !self.in_deferred_string_type_definition
&& self.settings.enabled.contains(&CheckCode::U006)
&& self.settings.enabled.contains(&CheckCode::UP006)
&& (self.settings.target_version >= PythonVersion::Py39
|| (self.settings.target_version >= PythonVersion::Py37
&& !self.settings.pyupgrade.keep_runtime_typing
@@ -1309,7 +1350,7 @@ where
ExprKind::Attribute { attr, .. } => {
// Ex) typing.List[...]
if !self.in_deferred_string_type_definition
&& self.settings.enabled.contains(&CheckCode::U006)
&& self.settings.enabled.contains(&CheckCode::UP006)
&& (self.settings.target_version >= PythonVersion::Py39
|| (self.settings.target_version >= PythonVersion::Py37
&& self.annotations_future_enabled
@@ -1403,16 +1444,16 @@ where
}
// pyupgrade
if self.settings.enabled.contains(&CheckCode::U005) {
if self.settings.enabled.contains(&CheckCode::UP005) {
pyupgrade::plugins::deprecated_unittest_alias(self, func);
}
// flake8-super
if self.settings.enabled.contains(&CheckCode::U008) {
if self.settings.enabled.contains(&CheckCode::UP008) {
pyupgrade::plugins::super_call_with_parameters(self, expr, func, args);
}
if self.settings.enabled.contains(&CheckCode::U012) {
if self.settings.enabled.contains(&CheckCode::UP012) {
pyupgrade::plugins::unnecessary_encode_utf8(self, expr, func, args, keywords);
}
@@ -1671,11 +1712,11 @@ where
}
// pyupgrade
if self.settings.enabled.contains(&CheckCode::U003) {
if self.settings.enabled.contains(&CheckCode::UP003) {
pyupgrade::plugins::type_of_primitive(self, expr, func, args);
}
if self.settings.enabled.contains(&CheckCode::U015) {
if self.settings.enabled.contains(&CheckCode::UP015) {
pyupgrade::plugins::redundant_open_modes(self, expr);
}
@@ -1716,10 +1757,8 @@ where
if self.settings.enabled.contains(&CheckCode::PLC3002) {
pylint::plugins::unnecessary_direct_lambda_call(self, expr, func);
}
// Ruff
if self.settings.enabled.contains(&CheckCode::RUF101) {
rules::plugins::convert_exit_to_sys_exit(self, func);
if self.settings.enabled.contains(&CheckCode::PLR1722) {
pylint::plugins::consider_using_sys_exit(self, func);
}
}
ExprKind::Dict { keys, .. } => {
@@ -1982,6 +2021,16 @@ where
.into_iter(),
);
}
if self.settings.enabled.contains(&CheckCode::PLC2201) {
pylint::plugins::misplaced_comparison_constant(
self,
expr,
left,
ops,
comparators,
);
}
}
ExprKind::Constant {
value: Constant::Str(value),
@@ -3046,8 +3095,9 @@ impl<'a> Checker<'a> {
let child = self.parents[defined_by];
let parent = defined_in.map(|defined_in| self.parents[defined_in]);
let in_init_py = self.path.ends_with("__init__.py");
let fix = if !in_init_py && self.patch(&CheckCode::F401) {
let ignore_init = self.settings.ignore_init_module_imports
&& self.path.ends_with("__init__.py");
let fix = if !ignore_init && self.patch(&CheckCode::F401) {
let deleted: Vec<&Stmt> = self
.deletions
.iter()
@@ -3067,7 +3117,7 @@ impl<'a> Checker<'a> {
Some(fix)
}
Err(e) => {
error!("Failed to remove unused imports: {}", e);
error!("Failed to remove unused imports: {e}");
None
}
}
@@ -3077,7 +3127,7 @@ impl<'a> Checker<'a> {
for (full_name, range) in unused_imports {
let mut check = Check::new(
CheckKind::UnusedImport(full_name.clone(), in_init_py),
CheckKind::UnusedImport(full_name.clone(), ignore_init),
*range,
);
if let Some(fix) = fix.as_ref() {

View File

@@ -7,7 +7,7 @@ use rustpython_parser::ast::Location;
use crate::ast::types::Range;
use crate::autofix::Fix;
use crate::checks::{Check, CheckCode, CheckKind};
use crate::checks::{Check, CheckCode, CheckKind, REDIRECTS};
use crate::noqa;
use crate::noqa::{is_file_exempt, Directive};
use crate::settings::Settings;
@@ -43,9 +43,9 @@ pub fn check_lines(
autofix: bool,
ignore_noqa: bool,
) {
let enforce_unnecessary_coding_comment = settings.enabled.contains(&CheckCode::U009);
let enforce_unnecessary_coding_comment = settings.enabled.contains(&CheckCode::UP009);
let enforce_line_too_long = settings.enabled.contains(&CheckCode::E501);
let enforce_noqa = settings.enabled.contains(&CheckCode::M001);
let enforce_noqa = settings.enabled.contains(&CheckCode::RUF100);
let mut noqa_directives: IntMap<usize, (Directive, Vec<&str>)> = IntMap::default();
let mut line_checks = vec![];
@@ -70,7 +70,7 @@ pub fn check_lines(
}
}
(Directive::Codes(.., codes), matches) => {
if codes.contains(&$check.kind.code().as_ref()) {
if noqa::includes($check.kind.code(), codes) {
matches.push($check.kind.code().as_ref());
if ignore_noqa {
line_checks.push($check);
@@ -97,7 +97,7 @@ pub fn check_lines(
// `noqa_line_for`, so fallback to the current line.
let noqa_lineno = noqa_line_for.get(&(lineno + 1)).unwrap_or(&(lineno + 1)) - 1;
// Enforce unnecessary coding comments (U009).
// Enforce unnecessary coding comments (UP009).
if enforce_unnecessary_coding_comment {
if lineno < 2 {
// PEP3120 makes utf-8 the default encoding.
@@ -140,7 +140,7 @@ pub fn check_lines(
ignored.push(index);
}
(Directive::Codes(.., codes), matches) => {
if codes.contains(&check.kind.code().as_ref()) {
if noqa::includes(check.kind.code(), codes) {
matches.push(check.kind.code().as_ref());
ignored.push(index);
}
@@ -184,7 +184,7 @@ pub fn check_lines(
}
}
// Enforce that the noqa directive was actually used (M001).
// Enforce that the noqa directive was actually used (RUF100).
if enforce_noqa {
for (row, (directive, matches)) in noqa_directives {
match directive {
@@ -210,6 +210,7 @@ pub fn check_lines(
let mut invalid_codes = vec![];
let mut valid_codes = vec![];
for code in codes {
let code = REDIRECTS.get(code).map_or(code, AsRef::as_ref);
if matches.contains(&code) || settings.external.contains(code) {
valid_codes.push(code.to_string());
} else {

View File

@@ -2,6 +2,8 @@ use std::fmt;
use std::str::FromStr;
use itertools::Itertools;
use once_cell::sync::Lazy;
use rustc_hash::FxHashMap;
use rustpython_parser::ast::Location;
use serde::{Deserialize, Serialize};
use strum_macros::{AsRefStr, Display, EnumIter, EnumString};
@@ -91,10 +93,15 @@ pub enum CheckCode {
F841,
F901,
// pylint
PLR1701,
PLC0414,
PLC2201,
PLC3002,
PLR0206,
PLE1142,
PLR0206,
PLR0402,
PLR1701,
PLR1722,
PLW0120,
// flake8-builtins
A001,
A002,
@@ -152,6 +159,15 @@ pub enum CheckCode {
C901,
// flake8-tidy-imports
I252,
// flake8-return
RET501,
RET502,
RET503,
RET504,
RET505,
RET506,
RET507,
RET508,
// flake8-print
T201,
T203,
@@ -184,20 +200,20 @@ pub enum CheckCode {
YTT302,
YTT303,
// pyupgrade
U001,
U003,
U004,
U005,
U006,
U007,
U008,
U009,
U010,
U011,
U012,
U013,
U014,
U015,
UP001,
UP003,
UP004,
UP005,
UP006,
UP007,
UP008,
UP009,
UP010,
UP011,
UP012,
UP013,
UP014,
UP015,
// pydocstyle
D100,
D101,
@@ -278,9 +294,7 @@ pub enum CheckCode {
RUF001,
RUF002,
RUF003,
RUF101,
// Meta
M001,
RUF100,
// pygrep-hooks
PGH001,
}
@@ -289,28 +303,28 @@ pub enum CheckCode {
pub enum CheckCategory {
Pyflakes,
Pycodestyle,
McCabe,
Isort,
Pydocstyle,
Pyupgrade,
PEP8Naming,
Eradicate,
Flake82020,
Flake8Annotations,
Flake8Bandit,
Flake8Comprehensions,
Flake8Debugger,
Flake8BlindExcept,
Flake8BooleanTrap,
Flake8Bugbear,
Flake8Builtins,
Flake8TidyImports,
Flake8Comprehensions,
Flake8Debugger,
Flake8Print,
Flake8Quotes,
Flake8Annotations,
Flake82020,
Flake8BlindExcept,
McCabe,
Flake8Return,
Flake8TidyImports,
Eradicate,
PygrepHooks,
Pylint,
Ruff,
Meta,
}
pub enum Platform {
@@ -342,16 +356,16 @@ impl CheckCategory {
CheckCategory::Flake8Debugger => "flake8-debugger",
CheckCategory::Flake8Print => "flake8-print",
CheckCategory::Flake8Quotes => "flake8-quotes",
CheckCategory::Flake8Return => "flake8-return",
CheckCategory::Flake8TidyImports => "flake8-tidy-imports",
CheckCategory::Isort => "isort",
CheckCategory::McCabe => "mccabe",
CheckCategory::Meta => "Meta rules",
CheckCategory::PEP8Naming => "pep8-naming",
CheckCategory::Pycodestyle => "pycodestyle",
CheckCategory::Pydocstyle => "pydocstyle",
CheckCategory::Pyflakes => "Pyflakes",
CheckCategory::Pylint => "Pylint",
CheckCategory::PygrepHooks => "pygrep-hooks",
CheckCategory::Pylint => "Pylint",
CheckCategory::Pyupgrade => "pyupgrade",
CheckCategory::Ruff => "Ruff-specific rules",
}
@@ -406,6 +420,10 @@ impl CheckCategory {
"https://pypi.org/project/flake8-quotes/3.3.1/",
&Platform::PyPI,
)),
CheckCategory::Flake8Return => Some((
"https://pypi.org/project/flake8-return/1.2.0/",
&Platform::PyPI,
)),
CheckCategory::Flake8TidyImports => Some((
"https://pypi.org/project/flake8-tidy-imports/4.8.0/",
&Platform::PyPI,
@@ -416,7 +434,6 @@ impl CheckCategory {
CheckCategory::McCabe => {
Some(("https://pypi.org/project/mccabe/0.7.0/", &Platform::PyPI))
}
CheckCategory::Meta => None,
CheckCategory::PEP8Naming => Some((
"https://pypi.org/project/pep8-naming/0.13.2/",
&Platform::PyPI,
@@ -479,6 +496,21 @@ impl fmt::Display for DeferralKeyword {
}
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Branch {
Elif,
Else,
}
impl fmt::Display for Branch {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self {
Branch::Elif => fmt.write_str("elif"),
Branch::Else => fmt.write_str("else"),
}
}
}
#[derive(AsRefStr, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum CheckKind {
// pycodestyle errors
@@ -544,9 +576,14 @@ pub enum CheckKind {
YieldOutsideFunction(DeferralKeyword),
// pylint
ConsiderMergingIsinstance(String, Vec<String>),
UselessImportAlias,
MisplacedComparisonConstant(String),
UnnecessaryDirectLambdaCall,
PropertyWithParameters,
ConsiderUsingFromImport(String, String),
AwaitOutsideAsync,
UselessElseOnLoop,
ConsiderUsingSysExit,
// flake8-builtins
BuiltinVariableShadowing(String),
BuiltinArgumentShadowing(String),
@@ -602,6 +639,15 @@ pub enum CheckKind {
Debugger(DebuggerUsingType),
// flake8-tidy-imports
BannedRelativeImport(Strictness),
// flake8-return
UnnecessaryReturnNone,
ImplicitReturnValue,
ImplicitReturn,
UnnecessaryAssign,
SuperfluousElseReturn(Branch),
SuperfluousElseRaise(Branch),
SuperfluousElseContinue(Branch),
SuperfluousElseBreak(Branch),
// flake8-print
PrintFound,
PPrintFound,
@@ -732,8 +778,6 @@ pub enum CheckKind {
AmbiguousUnicodeCharacterString(char, char),
AmbiguousUnicodeCharacterDocstring(char, char),
AmbiguousUnicodeCharacterComment(char, char),
ConvertExitToSysExit,
// Meta
UnusedNOQA(Option<Vec<String>>),
}
@@ -742,7 +786,7 @@ impl CheckCode {
/// physical lines).
pub fn lint_source(&self) -> &'static LintSource {
match self {
CheckCode::E501 | CheckCode::W292 | CheckCode::M001 | CheckCode::U009 => {
CheckCode::E501 | CheckCode::W292 | CheckCode::RUF100 | CheckCode::UP009 => {
&LintSource::Lines
}
CheckCode::ERA001
@@ -831,12 +875,19 @@ impl CheckCode {
CheckCode::F841 => CheckKind::UnusedVariable("...".to_string()),
CheckCode::F901 => CheckKind::RaiseNotImplemented,
// pylint
CheckCode::PLC0414 => CheckKind::UselessImportAlias,
CheckCode::PLC2201 => CheckKind::MisplacedComparisonConstant("...".to_string()),
CheckCode::PLC3002 => CheckKind::UnnecessaryDirectLambdaCall,
CheckCode::PLE1142 => CheckKind::AwaitOutsideAsync,
CheckCode::PLR0402 => {
CheckKind::ConsiderUsingFromImport("...".to_string(), "...".to_string())
}
CheckCode::PLR0206 => CheckKind::PropertyWithParameters,
CheckCode::PLR1701 => {
CheckKind::ConsiderMergingIsinstance("...".to_string(), vec!["...".to_string()])
}
CheckCode::PLR1722 => CheckKind::ConsiderUsingSysExit,
CheckCode::PLW0120 => CheckKind::UselessElseOnLoop,
// flake8-builtins
CheckCode::A001 => CheckKind::BuiltinVariableShadowing("...".to_string()),
CheckCode::A002 => CheckKind::BuiltinArgumentShadowing("...".to_string()),
@@ -907,6 +958,15 @@ impl CheckCode {
CheckCode::T100 => CheckKind::Debugger(DebuggerUsingType::Import("...".to_string())),
// flake8-tidy-imports
CheckCode::I252 => CheckKind::BannedRelativeImport(Strictness::All),
// flake8-return
CheckCode::RET501 => CheckKind::UnnecessaryReturnNone,
CheckCode::RET502 => CheckKind::ImplicitReturnValue,
CheckCode::RET503 => CheckKind::ImplicitReturn,
CheckCode::RET504 => CheckKind::UnnecessaryAssign,
CheckCode::RET505 => CheckKind::SuperfluousElseReturn(Branch::Else),
CheckCode::RET506 => CheckKind::SuperfluousElseRaise(Branch::Else),
CheckCode::RET507 => CheckKind::SuperfluousElseContinue(Branch::Else),
CheckCode::RET508 => CheckKind::SuperfluousElseBreak(Branch::Else),
// flake8-print
CheckCode::T201 => CheckKind::PrintFound,
CheckCode::T203 => CheckKind::PPrintFound,
@@ -941,23 +1001,23 @@ impl CheckCode {
// flake8-blind-except
CheckCode::BLE001 => CheckKind::BlindExcept,
// pyupgrade
CheckCode::U001 => CheckKind::UselessMetaclassType,
CheckCode::U003 => CheckKind::TypeOfPrimitive(Primitive::Str),
CheckCode::U004 => CheckKind::UselessObjectInheritance("...".to_string()),
CheckCode::U005 => CheckKind::DeprecatedUnittestAlias(
CheckCode::UP001 => CheckKind::UselessMetaclassType,
CheckCode::UP003 => CheckKind::TypeOfPrimitive(Primitive::Str),
CheckCode::UP004 => CheckKind::UselessObjectInheritance("...".to_string()),
CheckCode::UP005 => CheckKind::DeprecatedUnittestAlias(
"assertEquals".to_string(),
"assertEqual".to_string(),
),
CheckCode::U006 => CheckKind::UsePEP585Annotation("List".to_string()),
CheckCode::U007 => CheckKind::UsePEP604Annotation,
CheckCode::U008 => CheckKind::SuperCallWithParameters,
CheckCode::U009 => CheckKind::PEP3120UnnecessaryCodingComment,
CheckCode::U010 => CheckKind::UnnecessaryFutureImport(vec!["...".to_string()]),
CheckCode::U011 => CheckKind::UnnecessaryLRUCacheParams,
CheckCode::U012 => CheckKind::UnnecessaryEncodeUTF8,
CheckCode::U013 => CheckKind::ConvertTypedDictFunctionalToClass("...".to_string()),
CheckCode::U014 => CheckKind::ConvertNamedTupleFunctionalToClass("...".to_string()),
CheckCode::U015 => CheckKind::RedundantOpenModes,
CheckCode::UP006 => CheckKind::UsePEP585Annotation("List".to_string()),
CheckCode::UP007 => CheckKind::UsePEP604Annotation,
CheckCode::UP008 => CheckKind::SuperCallWithParameters,
CheckCode::UP009 => CheckKind::PEP3120UnnecessaryCodingComment,
CheckCode::UP010 => CheckKind::UnnecessaryFutureImport(vec!["...".to_string()]),
CheckCode::UP011 => CheckKind::UnnecessaryLRUCacheParams,
CheckCode::UP012 => CheckKind::UnnecessaryEncodeUTF8,
CheckCode::UP013 => CheckKind::ConvertTypedDictFunctionalToClass("...".to_string()),
CheckCode::UP014 => CheckKind::ConvertNamedTupleFunctionalToClass("...".to_string()),
CheckCode::UP015 => CheckKind::RedundantOpenModes,
// pydocstyle
CheckCode::D100 => CheckKind::PublicModule,
CheckCode::D101 => CheckKind::PublicClass,
@@ -1057,9 +1117,7 @@ impl CheckCode {
CheckCode::RUF001 => CheckKind::AmbiguousUnicodeCharacterString('𝐁', 'B'),
CheckCode::RUF002 => CheckKind::AmbiguousUnicodeCharacterDocstring('𝐁', 'B'),
CheckCode::RUF003 => CheckKind::AmbiguousUnicodeCharacterComment('𝐁', 'B'),
CheckCode::RUF101 => CheckKind::ConvertExitToSysExit,
// Meta
CheckCode::M001 => CheckKind::UnusedNOQA(None),
CheckCode::RUF100 => CheckKind::UnusedNOQA(None),
}
}
@@ -1231,7 +1289,6 @@ impl CheckCode {
CheckCode::FBT003 => CheckCategory::Flake8BooleanTrap,
CheckCode::I001 => CheckCategory::Isort,
CheckCode::I252 => CheckCategory::Flake8TidyImports,
CheckCode::M001 => CheckCategory::Meta,
CheckCode::N801 => CheckCategory::PEP8Naming,
CheckCode::N802 => CheckCategory::PEP8Naming,
CheckCode::N803 => CheckCategory::PEP8Naming,
@@ -1248,18 +1305,31 @@ impl CheckCode {
CheckCode::N817 => CheckCategory::PEP8Naming,
CheckCode::N818 => CheckCategory::PEP8Naming,
CheckCode::PGH001 => CheckCategory::PygrepHooks,
CheckCode::PLC0414 => CheckCategory::Pylint,
CheckCode::PLC2201 => CheckCategory::Pylint,
CheckCode::PLC3002 => CheckCategory::Pylint,
CheckCode::PLE1142 => CheckCategory::Pylint,
CheckCode::PLR0206 => CheckCategory::Pylint,
CheckCode::PLR0402 => CheckCategory::Pylint,
CheckCode::PLR1701 => CheckCategory::Pylint,
CheckCode::PLR1722 => CheckCategory::Pylint,
CheckCode::PLW0120 => CheckCategory::Pylint,
CheckCode::Q000 => CheckCategory::Flake8Quotes,
CheckCode::Q001 => CheckCategory::Flake8Quotes,
CheckCode::Q002 => CheckCategory::Flake8Quotes,
CheckCode::Q003 => CheckCategory::Flake8Quotes,
CheckCode::RET501 => CheckCategory::Flake8Return,
CheckCode::RET502 => CheckCategory::Flake8Return,
CheckCode::RET503 => CheckCategory::Flake8Return,
CheckCode::RET504 => CheckCategory::Flake8Return,
CheckCode::RET505 => CheckCategory::Flake8Return,
CheckCode::RET506 => CheckCategory::Flake8Return,
CheckCode::RET507 => CheckCategory::Flake8Return,
CheckCode::RET508 => CheckCategory::Flake8Return,
CheckCode::RUF001 => CheckCategory::Ruff,
CheckCode::RUF002 => CheckCategory::Ruff,
CheckCode::RUF003 => CheckCategory::Ruff,
CheckCode::RUF101 => CheckCategory::Ruff,
CheckCode::RUF100 => CheckCategory::Ruff,
CheckCode::S101 => CheckCategory::Flake8Bandit,
CheckCode::S102 => CheckCategory::Flake8Bandit,
CheckCode::S104 => CheckCategory::Flake8Bandit,
@@ -1269,20 +1339,20 @@ impl CheckCode {
CheckCode::T100 => CheckCategory::Flake8Debugger,
CheckCode::T201 => CheckCategory::Flake8Print,
CheckCode::T203 => CheckCategory::Flake8Print,
CheckCode::U001 => CheckCategory::Pyupgrade,
CheckCode::U003 => CheckCategory::Pyupgrade,
CheckCode::U004 => CheckCategory::Pyupgrade,
CheckCode::U005 => CheckCategory::Pyupgrade,
CheckCode::U006 => CheckCategory::Pyupgrade,
CheckCode::U007 => CheckCategory::Pyupgrade,
CheckCode::U008 => CheckCategory::Pyupgrade,
CheckCode::U009 => CheckCategory::Pyupgrade,
CheckCode::U010 => CheckCategory::Pyupgrade,
CheckCode::U011 => CheckCategory::Pyupgrade,
CheckCode::U012 => CheckCategory::Pyupgrade,
CheckCode::U013 => CheckCategory::Pyupgrade,
CheckCode::U014 => CheckCategory::Pyupgrade,
CheckCode::U015 => CheckCategory::Pyupgrade,
CheckCode::UP001 => CheckCategory::Pyupgrade,
CheckCode::UP003 => CheckCategory::Pyupgrade,
CheckCode::UP004 => CheckCategory::Pyupgrade,
CheckCode::UP005 => CheckCategory::Pyupgrade,
CheckCode::UP006 => CheckCategory::Pyupgrade,
CheckCode::UP007 => CheckCategory::Pyupgrade,
CheckCode::UP008 => CheckCategory::Pyupgrade,
CheckCode::UP009 => CheckCategory::Pyupgrade,
CheckCode::UP010 => CheckCategory::Pyupgrade,
CheckCode::UP011 => CheckCategory::Pyupgrade,
CheckCode::UP012 => CheckCategory::Pyupgrade,
CheckCode::UP013 => CheckCategory::Pyupgrade,
CheckCode::UP014 => CheckCategory::Pyupgrade,
CheckCode::UP015 => CheckCategory::Pyupgrade,
CheckCode::W292 => CheckCategory::Pycodestyle,
CheckCode::W605 => CheckCategory::Pycodestyle,
CheckCode::YTT101 => CheckCategory::Flake82020,
@@ -1364,10 +1434,15 @@ impl CheckKind {
CheckKind::NoNewLineAtEndOfFile => &CheckCode::W292,
CheckKind::InvalidEscapeSequence(_) => &CheckCode::W605,
// pylint
CheckKind::UselessImportAlias => &CheckCode::PLC0414,
CheckKind::MisplacedComparisonConstant(..) => &CheckCode::PLC2201,
CheckKind::UnnecessaryDirectLambdaCall => &CheckCode::PLC3002,
CheckKind::AwaitOutsideAsync => &CheckCode::PLE1142,
CheckKind::ConsiderMergingIsinstance(..) => &CheckCode::PLR1701,
CheckKind::PropertyWithParameters => &CheckCode::PLR0206,
CheckKind::UnnecessaryDirectLambdaCall => &CheckCode::PLC3002,
CheckKind::ConsiderUsingFromImport(..) => &CheckCode::PLR0402,
CheckKind::ConsiderUsingSysExit => &CheckCode::PLR1722,
CheckKind::UselessElseOnLoop => &CheckCode::PLW0120,
// flake8-builtins
CheckKind::BuiltinVariableShadowing(_) => &CheckCode::A001,
CheckKind::BuiltinArgumentShadowing(_) => &CheckCode::A002,
@@ -1423,6 +1498,15 @@ impl CheckKind {
CheckKind::Debugger(_) => &CheckCode::T100,
// flake8-tidy-imports
CheckKind::BannedRelativeImport(_) => &CheckCode::I252,
// flake8-return
CheckKind::UnnecessaryReturnNone => &CheckCode::RET501,
CheckKind::ImplicitReturnValue => &CheckCode::RET502,
CheckKind::ImplicitReturn => &CheckCode::RET503,
CheckKind::UnnecessaryAssign => &CheckCode::RET504,
CheckKind::SuperfluousElseReturn(_) => &CheckCode::RET505,
CheckKind::SuperfluousElseRaise(_) => &CheckCode::RET506,
CheckKind::SuperfluousElseContinue(_) => &CheckCode::RET507,
CheckKind::SuperfluousElseBreak(_) => &CheckCode::RET508,
// flake8-print
CheckKind::PrintFound => &CheckCode::T201,
CheckKind::PPrintFound => &CheckCode::T203,
@@ -1455,20 +1539,20 @@ impl CheckKind {
CheckKind::SysVersionCmpStr10 => &CheckCode::YTT302,
CheckKind::SysVersionSlice1Referenced => &CheckCode::YTT303,
// pyupgrade
CheckKind::TypeOfPrimitive(_) => &CheckCode::U003,
CheckKind::UselessMetaclassType => &CheckCode::U001,
CheckKind::DeprecatedUnittestAlias(..) => &CheckCode::U005,
CheckKind::UsePEP585Annotation(_) => &CheckCode::U006,
CheckKind::UsePEP604Annotation => &CheckCode::U007,
CheckKind::UselessObjectInheritance(_) => &CheckCode::U004,
CheckKind::SuperCallWithParameters => &CheckCode::U008,
CheckKind::PEP3120UnnecessaryCodingComment => &CheckCode::U009,
CheckKind::UnnecessaryFutureImport(_) => &CheckCode::U010,
CheckKind::UnnecessaryLRUCacheParams => &CheckCode::U011,
CheckKind::UnnecessaryEncodeUTF8 => &CheckCode::U012,
CheckKind::ConvertTypedDictFunctionalToClass(_) => &CheckCode::U013,
CheckKind::ConvertNamedTupleFunctionalToClass(_) => &CheckCode::U014,
CheckKind::RedundantOpenModes => &CheckCode::U015,
CheckKind::TypeOfPrimitive(_) => &CheckCode::UP003,
CheckKind::UselessMetaclassType => &CheckCode::UP001,
CheckKind::DeprecatedUnittestAlias(..) => &CheckCode::UP005,
CheckKind::UsePEP585Annotation(_) => &CheckCode::UP006,
CheckKind::UsePEP604Annotation => &CheckCode::UP007,
CheckKind::UselessObjectInheritance(_) => &CheckCode::UP004,
CheckKind::SuperCallWithParameters => &CheckCode::UP008,
CheckKind::PEP3120UnnecessaryCodingComment => &CheckCode::UP009,
CheckKind::UnnecessaryFutureImport(_) => &CheckCode::UP010,
CheckKind::UnnecessaryLRUCacheParams => &CheckCode::UP011,
CheckKind::UnnecessaryEncodeUTF8 => &CheckCode::UP012,
CheckKind::ConvertTypedDictFunctionalToClass(_) => &CheckCode::UP013,
CheckKind::ConvertNamedTupleFunctionalToClass(_) => &CheckCode::UP014,
CheckKind::RedundantOpenModes => &CheckCode::UP015,
// pydocstyle
CheckKind::BlankLineAfterLastSection(_) => &CheckCode::D413,
CheckKind::BlankLineAfterSection(_) => &CheckCode::D410,
@@ -1553,9 +1637,7 @@ impl CheckKind {
CheckKind::AmbiguousUnicodeCharacterString(..) => &CheckCode::RUF001,
CheckKind::AmbiguousUnicodeCharacterDocstring(..) => &CheckCode::RUF002,
CheckKind::AmbiguousUnicodeCharacterComment(..) => &CheckCode::RUF003,
CheckKind::ConvertExitToSysExit => &CheckCode::RUF101,
// Meta
CheckKind::UnusedNOQA(_) => &CheckCode::M001,
CheckKind::UnusedNOQA(_) => &CheckCode::RUF100,
}
}
@@ -1721,9 +1803,12 @@ impl CheckKind {
CheckKind::UndefinedName(name) => {
format!("Undefined name `{name}`")
}
CheckKind::UnusedImport(name, in_init_py) => {
if *in_init_py {
format!("`{name}` imported but unused and missing from `__all__`")
CheckKind::UnusedImport(name, ignore_init) => {
if *ignore_init {
format!(
"`{name}` imported but unused; consider adding to `__all__` or using a \
redundant alias"
)
} else {
format!("`{name}` imported but unused")
}
@@ -1740,19 +1825,32 @@ impl CheckKind {
format!("Invalid escape sequence: '\\{char}'")
}
// pylint
CheckKind::UselessImportAlias => {
"Import alias does not rename original package".to_string()
}
CheckKind::ConsiderMergingIsinstance(obj, types) => {
let types = types.join(", ");
format!("Consider merging these isinstance calls: `isinstance({obj}, ({types}))`")
}
CheckKind::MisplacedComparisonConstant(comprison) => {
format!("Comparison should be {comprison}")
}
CheckKind::UnnecessaryDirectLambdaCall => "Lambda expression called directly. Execute \
the expression inline instead."
.to_string(),
CheckKind::PropertyWithParameters => {
"Cannot have defined parameters for properties".to_string()
}
CheckKind::ConsiderUsingFromImport(module, name) => {
format!("Consider using `from {module} import {name}`")
}
CheckKind::AwaitOutsideAsync => {
"`await` should be used within an async function".to_string()
}
CheckKind::UselessElseOnLoop => "Else clause on loop without a break statement, \
remove the else and de-indent all the code inside it"
.to_string(),
CheckKind::ConsiderUsingSysExit => "Consider using `sys.exit()`".to_string(),
// flake8-builtins
CheckKind::BuiltinVariableShadowing(name) => {
format!("Variable `{name}` is shadowing a python builtin")
@@ -1963,6 +2061,31 @@ impl CheckKind {
}
Strictness::All => "Relative imports are banned".to_string(),
},
// flake8-return
CheckKind::UnnecessaryReturnNone => "Do not explicitly `return None` in function if \
it is the only possible return value"
.to_string(),
CheckKind::ImplicitReturnValue => "Do not implicitly `return None` in function able \
to return non-`None` value"
.to_string(),
CheckKind::ImplicitReturn => "Missing explicit `return` at the end of function able \
to return non-`None` value"
.to_string(),
CheckKind::UnnecessaryAssign => {
"Unnecessary variable assignment before `return` statement".to_string()
}
CheckKind::SuperfluousElseReturn(branch) => {
format!("Unnecessary `{branch}` after `return` statement")
}
CheckKind::SuperfluousElseRaise(branch) => {
format!("Unnecessary `{branch}` after `raise` statement")
}
CheckKind::SuperfluousElseContinue(branch) => {
format!("Unnecessary `{branch}` after `continue` statement")
}
CheckKind::SuperfluousElseBreak(branch) => {
format!("Unnecessary `{branch}` after `break` statement")
}
// flake8-print
CheckKind::PrintFound => "`print` found".to_string(),
CheckKind::PPrintFound => "`pprint` found".to_string(),
@@ -2315,10 +2438,6 @@ 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(),
Some(codes) => {
@@ -2370,7 +2489,7 @@ impl CheckKind {
| CheckKind::BlankLineBeforeSection(..)
| CheckKind::CapitalizeSectionName(..)
| CheckKind::CommentedOutCode
| CheckKind::ConvertExitToSysExit
| CheckKind::ConsiderUsingSysExit
| CheckKind::ConvertNamedTupleFunctionalToClass(..)
| CheckKind::ConvertTypedDictFunctionalToClass(..)
| CheckKind::DashedUnderlineAfterSection(..)
@@ -2379,7 +2498,10 @@ impl CheckKind {
| CheckKind::DoNotAssignLambda
| CheckKind::DuplicateHandlerException(..)
| CheckKind::GetAttrWithConstant
| CheckKind::ImplicitReturn
| CheckKind::ImplicitReturnValue
| CheckKind::IsLiteral
| CheckKind::MisplacedComparisonConstant(..)
| CheckKind::NewLineAfterLastParagraph
| CheckKind::NewLineAfterSectionName(..)
| CheckKind::NoBlankLineAfterFunction(..)
@@ -2398,6 +2520,7 @@ impl CheckKind {
| CheckKind::PPrintFound
| CheckKind::PrintFound
| CheckKind::RaiseNotImplemented
| CheckKind::RedundantOpenModes
| CheckKind::RedundantTupleInExceptionHandler(..)
| CheckKind::SectionNameEndsInColon(..)
| CheckKind::SectionNotOverIndented(..)
@@ -2412,7 +2535,6 @@ impl CheckKind {
| CheckKind::UnnecessaryComprehension(..)
| CheckKind::UnnecessaryEncodeUTF8
| CheckKind::UnnecessaryFutureImport(..)
| CheckKind::RedundantOpenModes
| CheckKind::UnnecessaryGeneratorDict
| CheckKind::UnnecessaryGeneratorList
| CheckKind::UnnecessaryGeneratorSet
@@ -2424,12 +2546,14 @@ impl CheckKind {
| CheckKind::UnnecessaryLiteralSet(..)
| CheckKind::UnnecessaryLiteralWithinListCall(..)
| CheckKind::UnnecessaryLiteralWithinTupleCall(..)
| CheckKind::UnnecessaryReturnNone
| CheckKind::UnsortedImports
| CheckKind::UnusedImport(_, false)
| CheckKind::UnusedLoopControlVariable(..)
| CheckKind::UnusedNOQA(..)
| CheckKind::UsePEP585Annotation(..)
| CheckKind::UsePEP604Annotation
| CheckKind::UselessImportAlias
| CheckKind::UselessMetaclassType
| CheckKind::UselessObjectInheritance(..)
)
@@ -2459,6 +2583,27 @@ impl Check {
}
}
/// A hash map from deprecated to latest `CheckCode`.
pub static REDIRECTS: Lazy<FxHashMap<&'static str, CheckCode>> = Lazy::new(|| {
FxHashMap::from_iter([
// TODO(charlie): Remove by 2023-01-01.
("U001", CheckCode::UP001),
("U003", CheckCode::UP003),
("U004", CheckCode::UP004),
("U005", CheckCode::UP005),
("U006", CheckCode::UP006),
("U007", CheckCode::UP007),
("U008", CheckCode::UP008),
("U009", CheckCode::UP009),
("U010", CheckCode::UP010),
("U011", CheckCode::UP011),
("U012", CheckCode::UP012),
("U013", CheckCode::UP013),
("U014", CheckCode::UP014),
("U015", CheckCode::UP015),
])
});
#[cfg(test)]
mod tests {
use std::str::FromStr;
@@ -2472,8 +2617,7 @@ mod tests {
for check_code in CheckCode::iter() {
assert!(
CheckCode::from_str(check_code.as_ref()).is_ok(),
"{:?} could not be round-trip serialized.",
check_code
"{check_code:?} could not be round-trip serialized."
);
}
}

View File

@@ -1,5 +1,6 @@
//! File automatically generated by `examples/generate_check_code_prefix.rs`.
use colored::Colorize;
use serde::{Deserialize, Serialize};
use strum_macros::EnumString;
@@ -255,10 +256,6 @@ pub enum CheckCodePrefix {
I2,
I25,
I252,
M,
M0,
M00,
M001,
N,
N8,
N80,
@@ -283,6 +280,14 @@ pub enum CheckCodePrefix {
PGH00,
PGH001,
PLC,
PLC0,
PLC04,
PLC041,
PLC0414,
PLC2,
PLC22,
PLC220,
PLC2201,
PLC3,
PLC30,
PLC300,
@@ -297,10 +302,20 @@ pub enum CheckCodePrefix {
PLR02,
PLR020,
PLR0206,
PLR04,
PLR040,
PLR0402,
PLR1,
PLR17,
PLR170,
PLR1701,
PLR172,
PLR1722,
PLW,
PLW0,
PLW01,
PLW012,
PLW0120,
Q,
Q0,
Q00,
@@ -308,6 +323,17 @@ pub enum CheckCodePrefix {
Q001,
Q002,
Q003,
RET,
RET5,
RET50,
RET501,
RET502,
RET503,
RET504,
RET505,
RET506,
RET507,
RET508,
RUF,
RUF0,
RUF00,
@@ -316,7 +342,7 @@ pub enum CheckCodePrefix {
RUF003,
RUF1,
RUF10,
RUF101,
RUF100,
S,
S1,
S10,
@@ -352,6 +378,24 @@ pub enum CheckCodePrefix {
U013,
U014,
U015,
UP,
UP0,
UP00,
UP001,
UP003,
UP004,
UP005,
UP006,
UP007,
UP008,
UP009,
UP01,
UP010,
UP011,
UP012,
UP013,
UP014,
UP015,
W,
W2,
W29,
@@ -1094,10 +1138,6 @@ impl CheckCodePrefix {
CheckCodePrefix::I2 => vec![CheckCode::I252],
CheckCodePrefix::I25 => vec![CheckCode::I252],
CheckCodePrefix::I252 => vec![CheckCode::I252],
CheckCodePrefix::M => vec![CheckCode::M001],
CheckCodePrefix::M0 => vec![CheckCode::M001],
CheckCodePrefix::M00 => vec![CheckCode::M001],
CheckCodePrefix::M001 => vec![CheckCode::M001],
CheckCodePrefix::N => vec![
CheckCode::N801,
CheckCode::N802,
@@ -1170,7 +1210,17 @@ impl CheckCodePrefix {
CheckCodePrefix::PGH0 => vec![CheckCode::PGH001],
CheckCodePrefix::PGH00 => vec![CheckCode::PGH001],
CheckCodePrefix::PGH001 => vec![CheckCode::PGH001],
CheckCodePrefix::PLC => vec![CheckCode::PLC3002],
CheckCodePrefix::PLC => {
vec![CheckCode::PLC0414, CheckCode::PLC2201, CheckCode::PLC3002]
}
CheckCodePrefix::PLC0 => vec![CheckCode::PLC0414],
CheckCodePrefix::PLC04 => vec![CheckCode::PLC0414],
CheckCodePrefix::PLC041 => vec![CheckCode::PLC0414],
CheckCodePrefix::PLC0414 => vec![CheckCode::PLC0414],
CheckCodePrefix::PLC2 => vec![CheckCode::PLC2201],
CheckCodePrefix::PLC22 => vec![CheckCode::PLC2201],
CheckCodePrefix::PLC220 => vec![CheckCode::PLC2201],
CheckCodePrefix::PLC2201 => vec![CheckCode::PLC2201],
CheckCodePrefix::PLC3 => vec![CheckCode::PLC3002],
CheckCodePrefix::PLC30 => vec![CheckCode::PLC3002],
CheckCodePrefix::PLC300 => vec![CheckCode::PLC3002],
@@ -1180,15 +1230,30 @@ impl CheckCodePrefix {
CheckCodePrefix::PLE11 => vec![CheckCode::PLE1142],
CheckCodePrefix::PLE114 => vec![CheckCode::PLE1142],
CheckCodePrefix::PLE1142 => vec![CheckCode::PLE1142],
CheckCodePrefix::PLR => vec![CheckCode::PLR1701, CheckCode::PLR0206],
CheckCodePrefix::PLR0 => vec![CheckCode::PLR0206],
CheckCodePrefix::PLR => vec![
CheckCode::PLR0206,
CheckCode::PLR0402,
CheckCode::PLR1701,
CheckCode::PLR1722,
],
CheckCodePrefix::PLR0 => vec![CheckCode::PLR0206, CheckCode::PLR0402],
CheckCodePrefix::PLR02 => vec![CheckCode::PLR0206],
CheckCodePrefix::PLR020 => vec![CheckCode::PLR0206],
CheckCodePrefix::PLR0206 => vec![CheckCode::PLR0206],
CheckCodePrefix::PLR1 => vec![CheckCode::PLR1701],
CheckCodePrefix::PLR17 => vec![CheckCode::PLR1701],
CheckCodePrefix::PLR04 => vec![CheckCode::PLR0402],
CheckCodePrefix::PLR040 => vec![CheckCode::PLR0402],
CheckCodePrefix::PLR0402 => vec![CheckCode::PLR0402],
CheckCodePrefix::PLR1 => vec![CheckCode::PLR1701, CheckCode::PLR1722],
CheckCodePrefix::PLR17 => vec![CheckCode::PLR1701, CheckCode::PLR1722],
CheckCodePrefix::PLR170 => vec![CheckCode::PLR1701],
CheckCodePrefix::PLR1701 => vec![CheckCode::PLR1701],
CheckCodePrefix::PLR172 => vec![CheckCode::PLR1722],
CheckCodePrefix::PLR1722 => vec![CheckCode::PLR1722],
CheckCodePrefix::PLW => vec![CheckCode::PLW0120],
CheckCodePrefix::PLW0 => vec![CheckCode::PLW0120],
CheckCodePrefix::PLW01 => vec![CheckCode::PLW0120],
CheckCodePrefix::PLW012 => vec![CheckCode::PLW0120],
CheckCodePrefix::PLW0120 => vec![CheckCode::PLW0120],
CheckCodePrefix::Q => vec![
CheckCode::Q000,
CheckCode::Q001,
@@ -1211,20 +1276,58 @@ impl CheckCodePrefix {
CheckCodePrefix::Q001 => vec![CheckCode::Q001],
CheckCodePrefix::Q002 => vec![CheckCode::Q002],
CheckCodePrefix::Q003 => vec![CheckCode::Q003],
CheckCodePrefix::RET => vec![
CheckCode::RET501,
CheckCode::RET502,
CheckCode::RET503,
CheckCode::RET504,
CheckCode::RET505,
CheckCode::RET506,
CheckCode::RET507,
CheckCode::RET508,
],
CheckCodePrefix::RET5 => vec![
CheckCode::RET501,
CheckCode::RET502,
CheckCode::RET503,
CheckCode::RET504,
CheckCode::RET505,
CheckCode::RET506,
CheckCode::RET507,
CheckCode::RET508,
],
CheckCodePrefix::RET50 => vec![
CheckCode::RET501,
CheckCode::RET502,
CheckCode::RET503,
CheckCode::RET504,
CheckCode::RET505,
CheckCode::RET506,
CheckCode::RET507,
CheckCode::RET508,
],
CheckCodePrefix::RET501 => vec![CheckCode::RET501],
CheckCodePrefix::RET502 => vec![CheckCode::RET502],
CheckCodePrefix::RET503 => vec![CheckCode::RET503],
CheckCodePrefix::RET504 => vec![CheckCode::RET504],
CheckCodePrefix::RET505 => vec![CheckCode::RET505],
CheckCodePrefix::RET506 => vec![CheckCode::RET506],
CheckCodePrefix::RET507 => vec![CheckCode::RET507],
CheckCodePrefix::RET508 => vec![CheckCode::RET508],
CheckCodePrefix::RUF => vec![
CheckCode::RUF001,
CheckCode::RUF002,
CheckCode::RUF003,
CheckCode::RUF101,
CheckCode::RUF100,
],
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::RUF1 => vec![CheckCode::RUF100],
CheckCodePrefix::RUF10 => vec![CheckCode::RUF100],
CheckCodePrefix::RUF100 => vec![CheckCode::RUF100],
CheckCodePrefix::S => vec![
CheckCode::S101,
CheckCode::S102,
@@ -1264,69 +1367,245 @@ impl CheckCodePrefix {
CheckCodePrefix::T201 => vec![CheckCode::T201],
CheckCodePrefix::T203 => vec![CheckCode::T203],
CheckCodePrefix::U => vec![
CheckCode::U001,
CheckCode::U003,
CheckCode::U004,
CheckCode::U005,
CheckCode::U006,
CheckCode::U007,
CheckCode::U008,
CheckCode::U009,
CheckCode::U010,
CheckCode::U011,
CheckCode::U012,
CheckCode::U013,
CheckCode::U014,
CheckCode::U015,
CheckCode::UP001,
CheckCode::UP003,
CheckCode::UP004,
CheckCode::UP005,
CheckCode::UP006,
CheckCode::UP007,
CheckCode::UP008,
CheckCode::UP009,
CheckCode::UP010,
CheckCode::UP011,
CheckCode::UP012,
CheckCode::UP013,
CheckCode::UP014,
CheckCode::UP015,
],
CheckCodePrefix::U0 => vec![
CheckCode::U001,
CheckCode::U003,
CheckCode::U004,
CheckCode::U005,
CheckCode::U006,
CheckCode::U007,
CheckCode::U008,
CheckCode::U009,
CheckCode::U010,
CheckCode::U011,
CheckCode::U012,
CheckCode::U013,
CheckCode::U014,
CheckCode::U015,
CheckCode::UP001,
CheckCode::UP003,
CheckCode::UP004,
CheckCode::UP005,
CheckCode::UP006,
CheckCode::UP007,
CheckCode::UP008,
CheckCode::UP009,
CheckCode::UP010,
CheckCode::UP011,
CheckCode::UP012,
CheckCode::UP013,
CheckCode::UP014,
CheckCode::UP015,
],
CheckCodePrefix::U00 => vec![
CheckCode::U001,
CheckCode::U003,
CheckCode::U004,
CheckCode::U005,
CheckCode::U006,
CheckCode::U007,
CheckCode::U008,
CheckCode::U009,
CheckCode::UP001,
CheckCode::UP003,
CheckCode::UP004,
CheckCode::UP005,
CheckCode::UP006,
CheckCode::UP007,
CheckCode::UP008,
CheckCode::UP009,
],
CheckCodePrefix::U001 => vec![CheckCode::U001],
CheckCodePrefix::U003 => vec![CheckCode::U003],
CheckCodePrefix::U004 => vec![CheckCode::U004],
CheckCodePrefix::U005 => vec![CheckCode::U005],
CheckCodePrefix::U006 => vec![CheckCode::U006],
CheckCodePrefix::U007 => vec![CheckCode::U007],
CheckCodePrefix::U008 => vec![CheckCode::U008],
CheckCodePrefix::U009 => vec![CheckCode::U009],
CheckCodePrefix::U001 => {
eprintln!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`U001` has been renamed to `UP001`".bold()
);
vec![CheckCode::UP001]
}
CheckCodePrefix::U003 => {
eprintln!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`U003` has been renamed to `UP003`".bold()
);
vec![CheckCode::UP003]
}
CheckCodePrefix::U004 => {
eprintln!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`U004` has been renamed to `UP004`".bold()
);
vec![CheckCode::UP004]
}
CheckCodePrefix::U005 => {
eprintln!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`U005` has been renamed to `UP005`".bold()
);
vec![CheckCode::UP005]
}
CheckCodePrefix::U006 => {
eprintln!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`U006` has been renamed to `UP006`".bold()
);
vec![CheckCode::UP006]
}
CheckCodePrefix::U007 => {
eprintln!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`U007` has been renamed to `UP007`".bold()
);
vec![CheckCode::UP007]
}
CheckCodePrefix::U008 => {
eprintln!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`U008` has been renamed to `UP008`".bold()
);
vec![CheckCode::UP008]
}
CheckCodePrefix::U009 => {
eprintln!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`U009` has been renamed to `UP009`".bold()
);
vec![CheckCode::UP009]
}
CheckCodePrefix::U01 => vec![
CheckCode::U010,
CheckCode::U011,
CheckCode::U012,
CheckCode::U013,
CheckCode::U014,
CheckCode::U015,
CheckCode::UP010,
CheckCode::UP011,
CheckCode::UP012,
CheckCode::UP013,
CheckCode::UP014,
CheckCode::UP015,
],
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::U010 => {
eprintln!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`U010` has been renamed to `UP010`".bold()
);
vec![CheckCode::UP010]
}
CheckCodePrefix::U011 => {
eprintln!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`U011` has been renamed to `UP011`".bold()
);
vec![CheckCode::UP011]
}
CheckCodePrefix::U012 => {
eprintln!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`U012` has been renamed to `UP012`".bold()
);
vec![CheckCode::UP012]
}
CheckCodePrefix::U013 => {
eprintln!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`U013` has been renamed to `UP013`".bold()
);
vec![CheckCode::UP013]
}
CheckCodePrefix::U014 => {
eprintln!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`U014` has been renamed to `UP014`".bold()
);
vec![CheckCode::UP014]
}
CheckCodePrefix::U015 => {
eprintln!(
"{}{} {}",
"warning".yellow().bold(),
":".bold(),
"`U015` has been renamed to `UP015`".bold()
);
vec![CheckCode::UP015]
}
CheckCodePrefix::UP => vec![
CheckCode::UP001,
CheckCode::UP003,
CheckCode::UP004,
CheckCode::UP005,
CheckCode::UP006,
CheckCode::UP007,
CheckCode::UP008,
CheckCode::UP009,
CheckCode::UP010,
CheckCode::UP011,
CheckCode::UP012,
CheckCode::UP013,
CheckCode::UP014,
CheckCode::UP015,
],
CheckCodePrefix::UP0 => vec![
CheckCode::UP001,
CheckCode::UP003,
CheckCode::UP004,
CheckCode::UP005,
CheckCode::UP006,
CheckCode::UP007,
CheckCode::UP008,
CheckCode::UP009,
CheckCode::UP010,
CheckCode::UP011,
CheckCode::UP012,
CheckCode::UP013,
CheckCode::UP014,
CheckCode::UP015,
],
CheckCodePrefix::UP00 => vec![
CheckCode::UP001,
CheckCode::UP003,
CheckCode::UP004,
CheckCode::UP005,
CheckCode::UP006,
CheckCode::UP007,
CheckCode::UP008,
CheckCode::UP009,
],
CheckCodePrefix::UP001 => vec![CheckCode::UP001],
CheckCodePrefix::UP003 => vec![CheckCode::UP003],
CheckCodePrefix::UP004 => vec![CheckCode::UP004],
CheckCodePrefix::UP005 => vec![CheckCode::UP005],
CheckCodePrefix::UP006 => vec![CheckCode::UP006],
CheckCodePrefix::UP007 => vec![CheckCode::UP007],
CheckCodePrefix::UP008 => vec![CheckCode::UP008],
CheckCodePrefix::UP009 => vec![CheckCode::UP009],
CheckCodePrefix::UP01 => vec![
CheckCode::UP010,
CheckCode::UP011,
CheckCode::UP012,
CheckCode::UP013,
CheckCode::UP014,
CheckCode::UP015,
],
CheckCodePrefix::UP010 => vec![CheckCode::UP010],
CheckCodePrefix::UP011 => vec![CheckCode::UP011],
CheckCodePrefix::UP012 => vec![CheckCode::UP012],
CheckCodePrefix::UP013 => vec![CheckCode::UP013],
CheckCodePrefix::UP014 => vec![CheckCode::UP014],
CheckCodePrefix::UP015 => vec![CheckCode::UP015],
CheckCodePrefix::W => vec![CheckCode::W292, CheckCode::W605],
CheckCodePrefix::W2 => vec![CheckCode::W292],
CheckCodePrefix::W29 => vec![CheckCode::W292],
@@ -1628,10 +1907,6 @@ impl CheckCodePrefix {
CheckCodePrefix::I2 => SuffixLength::One,
CheckCodePrefix::I25 => SuffixLength::Two,
CheckCodePrefix::I252 => SuffixLength::Three,
CheckCodePrefix::M => SuffixLength::Zero,
CheckCodePrefix::M0 => SuffixLength::One,
CheckCodePrefix::M00 => SuffixLength::Two,
CheckCodePrefix::M001 => SuffixLength::Three,
CheckCodePrefix::N => SuffixLength::Zero,
CheckCodePrefix::N8 => SuffixLength::One,
CheckCodePrefix::N80 => SuffixLength::Two,
@@ -1656,6 +1931,14 @@ impl CheckCodePrefix {
CheckCodePrefix::PGH00 => SuffixLength::Two,
CheckCodePrefix::PGH001 => SuffixLength::Three,
CheckCodePrefix::PLC => SuffixLength::Zero,
CheckCodePrefix::PLC0 => SuffixLength::One,
CheckCodePrefix::PLC04 => SuffixLength::Two,
CheckCodePrefix::PLC041 => SuffixLength::Three,
CheckCodePrefix::PLC0414 => SuffixLength::Four,
CheckCodePrefix::PLC2 => SuffixLength::One,
CheckCodePrefix::PLC22 => SuffixLength::Two,
CheckCodePrefix::PLC220 => SuffixLength::Three,
CheckCodePrefix::PLC2201 => SuffixLength::Four,
CheckCodePrefix::PLC3 => SuffixLength::One,
CheckCodePrefix::PLC30 => SuffixLength::Two,
CheckCodePrefix::PLC300 => SuffixLength::Three,
@@ -1670,10 +1953,20 @@ impl CheckCodePrefix {
CheckCodePrefix::PLR02 => SuffixLength::Two,
CheckCodePrefix::PLR020 => SuffixLength::Three,
CheckCodePrefix::PLR0206 => SuffixLength::Four,
CheckCodePrefix::PLR04 => SuffixLength::Two,
CheckCodePrefix::PLR040 => SuffixLength::Three,
CheckCodePrefix::PLR0402 => SuffixLength::Four,
CheckCodePrefix::PLR1 => SuffixLength::One,
CheckCodePrefix::PLR17 => SuffixLength::Two,
CheckCodePrefix::PLR170 => SuffixLength::Three,
CheckCodePrefix::PLR1701 => SuffixLength::Four,
CheckCodePrefix::PLR172 => SuffixLength::Three,
CheckCodePrefix::PLR1722 => SuffixLength::Four,
CheckCodePrefix::PLW => SuffixLength::Zero,
CheckCodePrefix::PLW0 => SuffixLength::One,
CheckCodePrefix::PLW01 => SuffixLength::Two,
CheckCodePrefix::PLW012 => SuffixLength::Three,
CheckCodePrefix::PLW0120 => SuffixLength::Four,
CheckCodePrefix::Q => SuffixLength::Zero,
CheckCodePrefix::Q0 => SuffixLength::One,
CheckCodePrefix::Q00 => SuffixLength::Two,
@@ -1681,6 +1974,17 @@ impl CheckCodePrefix {
CheckCodePrefix::Q001 => SuffixLength::Three,
CheckCodePrefix::Q002 => SuffixLength::Three,
CheckCodePrefix::Q003 => SuffixLength::Three,
CheckCodePrefix::RET => SuffixLength::Zero,
CheckCodePrefix::RET5 => SuffixLength::One,
CheckCodePrefix::RET50 => SuffixLength::Two,
CheckCodePrefix::RET501 => SuffixLength::Three,
CheckCodePrefix::RET502 => SuffixLength::Three,
CheckCodePrefix::RET503 => SuffixLength::Three,
CheckCodePrefix::RET504 => SuffixLength::Three,
CheckCodePrefix::RET505 => SuffixLength::Three,
CheckCodePrefix::RET506 => SuffixLength::Three,
CheckCodePrefix::RET507 => SuffixLength::Three,
CheckCodePrefix::RET508 => SuffixLength::Three,
CheckCodePrefix::RUF => SuffixLength::Zero,
CheckCodePrefix::RUF0 => SuffixLength::One,
CheckCodePrefix::RUF00 => SuffixLength::Two,
@@ -1689,7 +1993,7 @@ impl CheckCodePrefix {
CheckCodePrefix::RUF003 => SuffixLength::Three,
CheckCodePrefix::RUF1 => SuffixLength::One,
CheckCodePrefix::RUF10 => SuffixLength::Two,
CheckCodePrefix::RUF101 => SuffixLength::Three,
CheckCodePrefix::RUF100 => SuffixLength::Three,
CheckCodePrefix::S => SuffixLength::Zero,
CheckCodePrefix::S1 => SuffixLength::One,
CheckCodePrefix::S10 => SuffixLength::Two,
@@ -1725,6 +2029,24 @@ impl CheckCodePrefix {
CheckCodePrefix::U013 => SuffixLength::Three,
CheckCodePrefix::U014 => SuffixLength::Three,
CheckCodePrefix::U015 => SuffixLength::Three,
CheckCodePrefix::UP => SuffixLength::Zero,
CheckCodePrefix::UP0 => SuffixLength::One,
CheckCodePrefix::UP00 => SuffixLength::Two,
CheckCodePrefix::UP001 => SuffixLength::Three,
CheckCodePrefix::UP003 => SuffixLength::Three,
CheckCodePrefix::UP004 => SuffixLength::Three,
CheckCodePrefix::UP005 => SuffixLength::Three,
CheckCodePrefix::UP006 => SuffixLength::Three,
CheckCodePrefix::UP007 => SuffixLength::Three,
CheckCodePrefix::UP008 => SuffixLength::Three,
CheckCodePrefix::UP009 => SuffixLength::Three,
CheckCodePrefix::UP01 => SuffixLength::Two,
CheckCodePrefix::UP010 => SuffixLength::Three,
CheckCodePrefix::UP011 => SuffixLength::Three,
CheckCodePrefix::UP012 => SuffixLength::Three,
CheckCodePrefix::UP013 => SuffixLength::Three,
CheckCodePrefix::UP014 => SuffixLength::Three,
CheckCodePrefix::UP015 => SuffixLength::Three,
CheckCodePrefix::W => SuffixLength::Zero,
CheckCodePrefix::W2 => SuffixLength::One,
CheckCodePrefix::W29 => SuffixLength::Two,
@@ -1765,17 +2087,19 @@ pub const CATEGORIES: &[CheckCodePrefix] = &[
CheckCodePrefix::F,
CheckCodePrefix::FBT,
CheckCodePrefix::I,
CheckCodePrefix::M,
CheckCodePrefix::N,
CheckCodePrefix::PGH,
CheckCodePrefix::PLC,
CheckCodePrefix::PLE,
CheckCodePrefix::PLR,
CheckCodePrefix::PLW,
CheckCodePrefix::Q,
CheckCodePrefix::RET,
CheckCodePrefix::RUF,
CheckCodePrefix::S,
CheckCodePrefix::T,
CheckCodePrefix::U,
CheckCodePrefix::UP,
CheckCodePrefix::W,
CheckCodePrefix::YTT,
];

View File

@@ -7,20 +7,20 @@ use crate::visibility::{Modifier, VisibleScope};
/// Extract a docstring from a function or class body.
pub fn docstring_from(suite: &[Stmt]) -> Option<&Expr> {
if let Some(stmt) = suite.first() {
if let StmtKind::Expr { value } = &stmt.node {
if matches!(
&value.node,
ExprKind::Constant {
value: Constant::Str(_),
..
}
) {
return Some(value);
}
let stmt = suite.first()?;
let StmtKind::Expr { value } = &stmt.node else {
return None;
};
if !matches!(
&value.node,
ExprKind::Constant {
value: Constant::Str(_),
..
}
) {
return None;
}
None
Some(value)
}
/// Extract a `Definition` from the AST node defined by a `Stmt`.

View File

@@ -211,7 +211,7 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
.chain(args.kwonlyargs.iter())
.skip(
// If this is a non-static method, skip `cls` or `self`.
usize::from(!visibility::is_staticmethod(stmt)),
usize::from(!visibility::is_staticmethod(checker, stmt)),
)
{
// ANN401 for dynamically typed arguments
@@ -283,10 +283,10 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
}
// ANN101, ANN102
if !visibility::is_staticmethod(stmt) {
if !visibility::is_staticmethod(checker, stmt) {
if let Some(arg) = args.args.first() {
if arg.node.annotation.is_none() {
if visibility::is_classmethod(stmt) {
if visibility::is_classmethod(checker, stmt) {
if checker.settings.enabled.contains(&CheckCode::ANN102) {
checker.add_check(Check::new(
CheckKind::MissingTypeCls(arg.node.arg.to_string()),
@@ -319,14 +319,14 @@ pub fn definition(checker: &mut Checker, definition: &Definition, visibility: &V
return;
}
if visibility::is_classmethod(stmt) {
if visibility::is_classmethod(checker, stmt) {
if checker.settings.enabled.contains(&CheckCode::ANN206) {
checker.add_check(Check::new(
CheckKind::MissingReturnTypeClassMethod(name.to_string()),
Range::from_located(stmt),
));
}
} else if visibility::is_staticmethod(stmt) {
} else if visibility::is_staticmethod(checker, stmt) {
if checker.settings.enabled.contains(&CheckCode::ANN205) {
checker.add_check(Check::new(
CheckKind::MissingReturnTypeStaticMethod(name.to_string()),

View File

@@ -5,10 +5,11 @@ use crate::checks::{Check, CheckKind};
/// S102
pub fn exec_used(expr: &Expr, func: &Expr) -> Option<Check> {
if let ExprKind::Name { id, .. } = &func.node {
if id == "exec" {
return Some(Check::new(CheckKind::ExecUsed, Range::from_located(expr)));
}
let ExprKind::Name { id, .. } = &func.node else {
return None;
};
if id != "exec" {
return None;
}
None
Some(Check::new(CheckKind::ExecUsed, Range::from_located(expr)))
}

View File

@@ -5,16 +5,15 @@ use crate::checks::{Check, CheckKind};
use crate::flake8_bandit::helpers::{matches_password_name, string_literal};
fn check_password_kwarg(arg: &Located<ArgData>, default: &Expr) -> Option<Check> {
if let Some(string) = string_literal(default) {
let kwarg_name = &arg.node.arg;
if matches_password_name(kwarg_name) {
return Some(Check::new(
CheckKind::HardcodedPasswordDefault(string.to_string()),
Range::from_located(default),
));
}
let string = string_literal(default)?;
let kwarg_name = &arg.node.arg;
if !matches_password_name(kwarg_name) {
return None;
}
None
Some(Check::new(
CheckKind::HardcodedPasswordDefault(string.to_string()),
Range::from_located(default),
))
}
/// S107

View File

@@ -9,17 +9,15 @@ pub fn hardcoded_password_func_arg(keywords: &[Keyword]) -> Vec<Check> {
keywords
.iter()
.filter_map(|keyword| {
if let Some(string) = string_literal(&keyword.node.value) {
if let Some(arg) = &keyword.node.arg {
if matches_password_name(arg) {
return Some(Check::new(
CheckKind::HardcodedPasswordFuncArg(string.to_string()),
Range::from_located(keyword),
));
}
}
let string = string_literal(&keyword.node.value)?;
let arg = keyword.node.arg.as_ref()?;
if !matches_password_name(arg) {
return None;
}
None
Some(Check::new(
CheckKind::HardcodedPasswordFuncArg(string.to_string()),
Range::from_located(keyword),
))
})
.collect()
}

View File

@@ -29,15 +29,14 @@ pub fn compare_to_hardcoded_password_string(left: &Expr, comparators: &[Expr]) -
comparators
.iter()
.filter_map(|comp| {
if let Some(string) = string_literal(comp) {
if is_password_target(left) {
return Some(Check::new(
CheckKind::HardcodedPasswordString(string.to_string()),
Range::from_located(comp),
));
}
let string = string_literal(comp)?;
if !is_password_target(left) {
return None;
}
None
Some(Check::new(
CheckKind::HardcodedPasswordString(string.to_string()),
Range::from_located(comp),
))
})
.collect()
}

View File

@@ -6,17 +6,18 @@ use crate::checks::{Check, CheckKind};
pub fn blind_except(checker: &mut Checker, handlers: &[Excepthandler]) {
for handler in handlers {
let ExcepthandlerKind::ExceptHandler { type_, .. } = &handler.node;
if let Some(type_) = type_ {
if let ExprKind::Name { id, .. } = &type_.node {
for exception in ["BaseException", "Exception"] {
if id == exception {
checker.add_check(Check::new(
CheckKind::BlindExcept,
Range::from_located(type_),
));
}
}
let ExcepthandlerKind::ExceptHandler { type_: Some(type_), .. } = &handler.node else {
continue;
};
let ExprKind::Name { id, .. } = &type_.node else {
continue;
};
for exception in ["BaseException", "Exception"] {
if id == exception {
checker.add_check(Check::new(
CheckKind::BlindExcept,
Range::from_located(type_),
));
}
}
}

View File

@@ -11,11 +11,10 @@ const FUNC_NAME_ALLOWLIST: &[&str] = &["get", "setdefault", "pop", "fromkeys"];
/// `true`, the function name must be explicitly allowed, and the argument must
/// be either the first or second argument in the call.
fn allow_boolean_trap(func: &Expr) -> bool {
if let ExprKind::Attribute { attr, .. } = &func.node {
FUNC_NAME_ALLOWLIST.contains(&attr.as_ref())
} else {
false
}
let ExprKind::Attribute { attr, .. } = &func.node else {
return false;
};
FUNC_NAME_ALLOWLIST.contains(&attr.as_ref())
}
fn is_boolean_arg(arg: &Expr) -> bool {
@@ -39,24 +38,26 @@ pub fn check_positional_boolean_in_def(checker: &mut Checker, arguments: &Argume
if arg.node.annotation.is_none() {
continue;
}
let Some(expr) = &arg.node.annotation else {
continue;
};
if let Some(expr) = &arg.node.annotation {
// check for both bool (python class) and 'bool' (string annotation)
let hint = match &expr.node {
ExprKind::Name { id, .. } => id == "bool",
ExprKind::Constant {
value: Constant::Str(value),
..
} => value == "bool",
_ => false,
};
if hint {
checker.add_check(Check::new(
CheckKind::BooleanPositionalArgInFunctionDefinition,
Range::from_located(arg),
));
}
// check for both bool (python class) and 'bool' (string annotation)
let hint = match &expr.node {
ExprKind::Name { id, .. } => id == "bool",
ExprKind::Constant {
value: Constant::Str(value),
..
} => value == "bool",
_ => false,
};
if !hint {
continue;
}
checker.add_check(Check::new(
CheckKind::BooleanPositionalArgInFunctionDefinition,
Range::from_located(arg),
));
}
}

View File

@@ -67,24 +67,28 @@ pub fn abstract_base_class(
keywords: &[Keyword],
body: &[Stmt],
) {
if bases.len() + keywords.len() == 1
&& is_abc_class(
bases,
keywords,
&checker.from_imports,
&checker.import_aliases,
)
{
let mut has_abstract_method = false;
for stmt in body {
// https://github.com/PyCQA/flake8-bugbear/issues/293
// Ignore abc's that declares a class attribute that must be set
if let StmtKind::AnnAssign { .. } | StmtKind::Assign { .. } = &stmt.node {
has_abstract_method = true;
continue;
}
if bases.len() + keywords.len() != 1 {
return;
}
if !is_abc_class(
bases,
keywords,
&checker.from_imports,
&checker.import_aliases,
) {
return;
}
if let StmtKind::FunctionDef {
let mut has_abstract_method = false;
for stmt in body {
// https://github.com/PyCQA/flake8-bugbear/issues/293
// Ignore abc's that declares a class attribute that must be set
if let StmtKind::AnnAssign { .. } | StmtKind::Assign { .. } = &stmt.node {
has_abstract_method = true;
continue;
}
let (StmtKind::FunctionDef {
decorator_list,
body,
..
@@ -93,36 +97,38 @@ pub fn abstract_base_class(
decorator_list,
body,
..
} = &stmt.node
{
let has_abstract_decorator = decorator_list
.iter()
.any(|d| is_abstractmethod(d, &checker.from_imports, &checker.import_aliases));
}) = &stmt.node else {
continue;
};
has_abstract_method |= has_abstract_decorator;
let has_abstract_decorator = decorator_list
.iter()
.any(|d| is_abstractmethod(d, &checker.from_imports, &checker.import_aliases));
if checker.settings.enabled.contains(&CheckCode::B027) {
if !has_abstract_decorator
&& is_empty_body(body)
&& !decorator_list
.iter()
.any(|d| is_overload(d, &checker.from_imports, &checker.import_aliases))
{
checker.add_check(Check::new(
CheckKind::EmptyMethodWithoutAbstractDecorator(name.to_string()),
Range::from_located(stmt),
));
}
}
}
has_abstract_method |= has_abstract_decorator;
if !checker.settings.enabled.contains(&CheckCode::B027) {
continue;
}
if checker.settings.enabled.contains(&CheckCode::B024) {
if !has_abstract_method {
checker.add_check(Check::new(
CheckKind::AbstractBaseClassWithoutAbstractMethod(name.to_string()),
Range::from_located(stmt),
));
}
if !has_abstract_decorator
&& is_empty_body(body)
&& !decorator_list
.iter()
.any(|d| is_overload(d, &checker.from_imports, &checker.import_aliases))
{
checker.add_check(Check::new(
CheckKind::EmptyMethodWithoutAbstractDecorator(name.to_string()),
Range::from_located(stmt),
));
}
}
if checker.settings.enabled.contains(&CheckCode::B024) {
if !has_abstract_method {
checker.add_check(Check::new(
CheckKind::AbstractBaseClassWithoutAbstractMethod(name.to_string()),
Range::from_located(stmt),
));
}
}
}

View File

@@ -38,23 +38,24 @@ 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 {
let ExprKind::Constant {
value: Constant::Bool(false),
..
} = &test.node
{
let mut check = Check::new(CheckKind::DoNotAssertFalse, Range::from_located(test));
if checker.patch(check.kind.code()) {
let mut generator = SourceGenerator::new();
generator.unparse_stmt(&assertion_error(msg));
if let Ok(content) = generator.generate() {
check.amend(Fix::replacement(
content,
stmt.location,
stmt.end_location.unwrap(),
));
}
} = &test.node else {
return;
};
let mut check = Check::new(CheckKind::DoNotAssertFalse, Range::from_located(test));
if checker.patch(check.kind.code()) {
let mut generator = SourceGenerator::new();
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);
}
checker.add_check(check);
}

View File

@@ -1,25 +1,40 @@
use rustpython_ast::{ExprKind, Stmt, Withitem};
use crate::ast::helpers::match_name_or_attr;
use crate::ast::helpers::match_module_member;
use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::checks::{Check, CheckKind};
/// B017
pub fn assert_raises_exception(checker: &mut Checker, stmt: &Stmt, items: &[Withitem]) {
if let Some(item) = items.first() {
let item_context = &item.context_expr;
if let ExprKind::Call { func, args, .. } = &item_context.node {
if args.len() == 1
&& item.optional_vars.is_none()
&& match_name_or_attr(func, "assertRaises")
&& match_name_or_attr(args.first().unwrap(), "Exception")
{
checker.add_check(Check::new(
CheckKind::NoAssertRaisesException,
Range::from_located(stmt),
));
}
}
let Some(item) = items.first() else {
return;
};
let item_context = &item.context_expr;
let ExprKind::Call { func, args, .. } = &item_context.node else {
return;
};
if args.len() != 1 {
return;
}
if item.optional_vars.is_some() {
return;
}
if !matches!(&func.node, ExprKind::Attribute { attr, .. } if attr == "assertRaises") {
return;
}
if !match_module_member(
args.first().unwrap(),
"",
"Exception",
&checker.from_imports,
&checker.import_aliases,
) {
return;
}
checker.add_check(Check::new(
CheckKind::NoAssertRaisesException,
Range::from_located(stmt),
));
}

View File

@@ -6,19 +6,24 @@ use crate::checks::{Check, CheckKind};
/// B003
pub fn assignment_to_os_environ(checker: &mut Checker, targets: &[Expr]) {
if targets.len() == 1 {
let target = &targets[0];
if let ExprKind::Attribute { value, attr, .. } = &target.node {
if attr == "environ" {
if let ExprKind::Name { id, .. } = &value.node {
if id == "os" {
checker.add_check(Check::new(
CheckKind::AssignmentToOsEnviron,
Range::from_located(target),
));
}
}
}
}
if targets.len() != 1 {
return;
}
let target = &targets[0];
let ExprKind::Attribute { value, attr, .. } = &target.node else {
return;
};
if attr != "environ" {
return;
}
let ExprKind::Name { id, .. } = &value.node else {
return;
};
if id != "os" {
return;
}
checker.add_check(Check::new(
CheckKind::AssignmentToOsEnviron,
Range::from_located(target),
));
}

View File

@@ -13,29 +13,30 @@ fn is_cache_func(checker: &Checker, expr: &Expr) -> bool {
/// B019
pub fn cached_instance_method(checker: &mut Checker, decorator_list: &[Expr]) {
if matches!(checker.current_scope().kind, ScopeKind::Class(_)) {
for decorator in decorator_list {
// TODO(charlie): This should take into account `classmethod-decorators` and
// `staticmethod-decorators`.
if let ExprKind::Name { id, .. } = &decorator.node {
if id == "classmethod" || id == "staticmethod" {
return;
}
}
}
for decorator in decorator_list {
if is_cache_func(
checker,
match &decorator.node {
ExprKind::Call { func, .. } => func,
_ => decorator,
},
) {
checker.add_check(Check::new(
CheckKind::CachedInstanceMethod,
Range::from_located(decorator),
));
if !matches!(checker.current_scope().kind, ScopeKind::Class(_)) {
return;
}
for decorator in decorator_list {
// TODO(charlie): This should take into account `classmethod-decorators` and
// `staticmethod-decorators`.
if let ExprKind::Name { id, .. } = &decorator.node {
if id == "classmethod" || id == "staticmethod" {
return;
}
}
}
for decorator in decorator_list {
if is_cache_func(
checker,
match &decorator.node {
ExprKind::Call { func, .. } => func,
_ => decorator,
},
) {
checker.add_check(Check::new(
CheckKind::CachedInstanceMethod,
Range::from_located(decorator),
));
}
}
}

View File

@@ -6,10 +6,11 @@ use crate::checks::{Check, CheckKind};
/// B016
pub fn cannot_raise_literal(checker: &mut Checker, expr: &Expr) {
if let ExprKind::Constant { .. } = &expr.node {
checker.add_check(Check::new(
CheckKind::CannotRaiseLiteral,
Range::from_located(expr),
));
}
let ExprKind::Constant { .. } = &expr.node else {
return;
};
checker.add_check(Check::new(
CheckKind::CannotRaiseLiteral,
Range::from_located(expr),
));
}

View File

@@ -79,33 +79,30 @@ pub fn duplicate_exceptions(checker: &mut Checker, stmt: &Stmt, handlers: &[Exce
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_, .. } => {
if let Some(type_) = type_ {
match &type_.node {
ExprKind::Attribute { .. } | ExprKind::Name { .. } => {
let call_path = helpers::collect_call_paths(type_);
if !call_path.is_empty() {
if seen.contains(&call_path) {
duplicates.insert(call_path);
} else {
seen.insert(call_path);
}
}
}
ExprKind::Tuple { elts, .. } => {
for name in duplicate_handler_exceptions(checker, type_, elts) {
if seen.contains(&name) {
duplicates.insert(name);
} else {
seen.insert(name);
}
}
}
_ => {}
let ExcepthandlerKind::ExceptHandler { type_: Some(type_), .. } = &handler.node else {
continue;
};
match &type_.node {
ExprKind::Attribute { .. } | ExprKind::Name { .. } => {
let call_path = helpers::collect_call_paths(type_);
if !call_path.is_empty() {
if seen.contains(&call_path) {
duplicates.insert(call_path);
} else {
seen.insert(call_path);
}
}
}
ExprKind::Tuple { elts, .. } => {
for name in duplicate_handler_exceptions(checker, type_, elts) {
if seen.contains(&name) {
duplicates.insert(name);
} else {
seen.insert(name);
}
}
}
_ => {}
}
}

View File

@@ -6,14 +6,17 @@ use crate::checks::{Check, CheckKind};
/// B021
pub fn f_string_docstring(checker: &mut Checker, body: &[Stmt]) {
if let Some(stmt) = body.first() {
if let StmtKind::Expr { value } = &stmt.node {
if let ExprKind::JoinedStr { .. } = value.node {
checker.add_check(Check::new(
CheckKind::FStringDocstring,
Range::from_located(stmt),
));
}
}
}
let Some(stmt) = body.first() else {
return;
};
let StmtKind::Expr { value } = &stmt.node else {
return;
};
let ExprKind::JoinedStr { .. } = value.node else {
return;
};
checker.add_check(Check::new(
CheckKind::FStringDocstring,
Range::from_located(stmt),
));
}

View File

@@ -71,29 +71,26 @@ where
}
fn is_nan_or_infinity(expr: &Expr, args: &[Expr]) -> bool {
if let ExprKind::Name { id, .. } = &expr.node {
if id == "float" {
if let Some(arg) = args.first() {
if let ExprKind::Constant {
value: Constant::Str(value),
..
} = &arg.node
{
let lowercased = value.to_lowercase();
return lowercased == "nan"
|| lowercased == "+nan"
|| lowercased == "-nan"
|| lowercased == "inf"
|| lowercased == "+inf"
|| lowercased == "-inf"
|| lowercased == "infinity"
|| lowercased == "+infinity"
|| lowercased == "-infinity";
}
}
}
let ExprKind::Name { id, .. } = &expr.node else {
return false;
};
if id != "float" {
return false;
}
false
let Some(arg) = args.first() else {
return false;
};
let ExprKind::Constant {
value: Constant::Str(value),
..
} = &arg.node else {
return false;
};
let lowercased = value.to_lowercase();
matches!(
lowercased.as_str(),
"nan" | "+nan" | "-nan" | "inf" | "+inf" | "-inf" | "infinity" | "+infinity" | "-infinity"
)
}
/// B008

View File

@@ -22,32 +22,39 @@ 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" {
if let [obj, arg] = args {
if let ExprKind::Constant {
value: Constant::Str(value),
..
} = &arg.node
{
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(check.kind.code()) {
let mut generator = SourceGenerator::new();
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);
}
}
}
let ExprKind::Name { id, .. } = &func.node else {
return;
};
if id != "getattr" {
return;
}
let [obj, arg] = args else {
return;
};
let ExprKind::Constant {
value: Constant::Str(value),
..
} = &arg.node else {
return;
};
if !IDENTIFIER_REGEX.is_match(value) {
return;
}
if KWLIST.contains(&value.as_str()) {
return;
}
let mut check = Check::new(CheckKind::GetAttrWithConstant, Range::from_located(expr));
if checker.patch(check.kind.code()) {
let mut generator = SourceGenerator::new();
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

@@ -13,21 +13,18 @@ struct RaiseVisitor {
impl<'a> Visitor<'a> for RaiseVisitor {
fn visit_stmt(&mut self, stmt: &'a Stmt) {
match &stmt.node {
StmtKind::Raise { exc, cause } => {
if cause.is_none() {
if let Some(exc) = exc {
match &exc.node {
ExprKind::Name { id, .. } if is_lower(id) => {}
_ => {
self.checks.push(Check::new(
CheckKind::RaiseWithoutFromInsideExcept,
Range::from_located(stmt),
));
}
}
}
StmtKind::Raise {
exc: Some(exc),
cause: None,
} => match &exc.node {
ExprKind::Name { id, .. } if is_lower(id) => {}
_ => {
self.checks.push(Check::new(
CheckKind::RaiseWithoutFromInsideExcept,
Range::from_located(stmt),
));
}
}
},
StmtKind::ClassDef { .. }
| StmtKind::FunctionDef { .. }
| StmtKind::AsyncFunctionDef { .. }

View File

@@ -39,46 +39,47 @@ fn match_tuple_range<T>(located: &Located<T>, locator: &SourceCodeLocator) -> Re
}
}
}
if let (Some(location), Some(end_location)) = (location, end_location) {
Ok(Range {
location,
end_location,
})
} else {
let (Some(location), Some(end_location)) = (location, end_location) else {
bail!("Unable to find left and right parentheses");
}
};
Ok(Range {
location,
end_location,
})
}
/// B013
pub fn redundant_tuple_in_exception_handler(checker: &mut Checker, handlers: &[Excepthandler]) {
for handler in handlers {
let ExcepthandlerKind::ExceptHandler { type_, .. } = &handler.node;
if let Some(type_) = type_ {
if let ExprKind::Tuple { elts, .. } = &type_.node {
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),
}
}
let ExcepthandlerKind::ExceptHandler { type_: Some(type_), .. } = &handler.node else {
continue;
};
let ExprKind::Tuple { elts, .. } = &type_.node else {
continue;
};
let [elt] = &elts[..] else {
continue;
};
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,
));
}
checker.add_check(check);
Err(e) => error!("Failed to locate parentheses: {e}"),
}
}
}
checker.add_check(check);
}
}

View File

@@ -35,31 +35,37 @@ fn assignment(obj: &Expr, name: &str, value: &Expr) -> Result<String> {
/// 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 [obj, name, value] = args {
if let ExprKind::Constant {
value: Constant::Str(name),
..
} = &name.node
{
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);
}
}
}
}
let ExprKind::Name { id, .. } = &func.node else {
return;
};
if id != "setattr" {
return;
}
let [obj, name, value] = args else {
return;
};
let ExprKind::Constant {
value: Constant::Str(name),
..
} = &name.node else {
return;
};
if !IDENTIFIER_REGEX.is_match(name) {
return;
}
if KWLIST.contains(&name.as_str()) {
return;
}
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

@@ -10,16 +10,19 @@ pub fn star_arg_unpacking_after_keyword_arg(
args: &[Expr],
keywords: &[Keyword],
) {
if let Some(keyword) = keywords.first() {
for arg in args {
if let ExprKind::Starred { .. } = arg.node {
if arg.location > keyword.location {
checker.add_check(Check::new(
CheckKind::StarArgUnpackingAfterKeywordArg,
Range::from_located(arg),
));
}
}
let Some(keyword) = keywords.first() else {
return;
};
for arg in args {
let ExprKind::Starred { .. } = arg.node else {
continue;
};
if arg.location <= keyword.location {
continue;
}
checker.add_check(Check::new(
CheckKind::StarArgUnpackingAfterKeywordArg,
Range::from_located(arg),
));
}
}

View File

@@ -7,22 +7,27 @@ use crate::checks::{Check, CheckKind};
/// B005
pub fn strip_with_multi_characters(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) {
if let ExprKind::Attribute { attr, .. } = &func.node {
if attr == "strip" || attr == "lstrip" || attr == "rstrip" {
if args.len() == 1 {
if let ExprKind::Constant {
value: Constant::Str(value),
..
} = &args[0].node
{
if value.len() > 1 && value.chars().unique().count() != value.len() {
checker.add_check(Check::new(
CheckKind::StripWithMultiCharacters,
Range::from_located(expr),
));
}
}
}
}
let ExprKind::Attribute { attr, .. } = &func.node else {
return;
};
if !matches!(attr.as_str(), "strip" | "lstrip" | "rstrip") {
return;
}
if args.len() != 1 {
return;
}
let ExprKind::Constant {
value: Constant::Str(value),
..
} = &args[0].node else {
return;
};
if value.len() > 1 && value.chars().unique().count() != value.len() {
checker.add_check(Check::new(
CheckKind::StripWithMultiCharacters,
Range::from_located(expr),
));
}
}

View File

@@ -6,14 +6,17 @@ use crate::checks::{Check, CheckKind};
/// B002
pub fn unary_prefix_increment(checker: &mut Checker, expr: &Expr, op: &Unaryop, operand: &Expr) {
if matches!(op, Unaryop::UAdd) {
if let ExprKind::UnaryOp { op, .. } = &operand.node {
if matches!(op, Unaryop::UAdd) {
checker.add_check(Check::new(
CheckKind::UnaryPrefixIncrement,
Range::from_located(expr),
));
}
}
if !matches!(op, Unaryop::UAdd) {
return;
}
let ExprKind::UnaryOp { op, .. } = &operand.node else {
return;
};
if !matches!(op, Unaryop::UAdd) {
return;
}
checker.add_check(Check::new(
CheckKind::UnaryPrefixIncrement,
Range::from_located(expr),
));
}

View File

@@ -6,22 +6,27 @@ use crate::checks::{Check, CheckKind};
/// B004
pub fn unreliable_callable_check(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) {
if let ExprKind::Name { id, .. } = &func.node {
if id == "getattr" || id == "hasattr" {
if args.len() >= 2 {
if let ExprKind::Constant {
value: Constant::Str(s),
..
} = &args[1].node
{
if s == "__call__" {
checker.add_check(Check::new(
CheckKind::UnreliableCallableCheck,
Range::from_located(expr),
));
}
}
}
}
let ExprKind::Name { id, .. } = &func.node else {
return;
};
if id != "getattr" && id != "hasattr" {
return;
}
if args.len() < 2 {
return;
};
let ExprKind::Constant {
value: Constant::Str(s),
..
} = &args[1].node else
{
return;
};
if s != "__call__" {
return;
}
checker.add_check(Check::new(
CheckKind::UnreliableCallableCheck,
Range::from_located(expr),
));
}

View File

@@ -62,7 +62,7 @@ pub fn unnecessary_generator_list(
if fix {
match fixes::fix_unnecessary_generator_list(locator, expr) {
Ok(fix) => check.amend(fix),
Err(e) => error!("Failed to generate fix: {}", e),
Err(e) => error!("Failed to generate fix: {e}"),
}
}
return Some(check);
@@ -86,7 +86,7 @@ pub fn unnecessary_generator_set(
if fix {
match fixes::fix_unnecessary_generator_set(locator, expr) {
Ok(fix) => check.amend(fix),
Err(e) => error!("Failed to generate fix: {}", e),
Err(e) => error!("Failed to generate fix: {e}"),
}
}
return Some(check);
@@ -112,7 +112,7 @@ pub fn unnecessary_generator_dict(
if fix {
match fixes::fix_unnecessary_generator_dict(locator, expr) {
Ok(fix) => check.amend(fix),
Err(e) => error!("Failed to generate fix: {}", e),
Err(e) => error!("Failed to generate fix: {e}"),
}
}
return Some(check);
@@ -139,7 +139,7 @@ pub fn unnecessary_list_comprehension_set(
if fix {
match fixes::fix_unnecessary_list_comprehension_set(locator, expr) {
Ok(fix) => check.amend(fix),
Err(e) => error!("Failed to generate fix: {}", e),
Err(e) => error!("Failed to generate fix: {e}"),
}
}
return Some(check);
@@ -158,22 +158,23 @@ pub fn unnecessary_list_comprehension_dict(
location: Range,
) -> Option<Check> {
let argument = exactly_one_argument_with_matching_function("dict", func, args, keywords)?;
if let ExprKind::ListComp { elt, .. } = &argument {
match &elt.node {
ExprKind::Tuple { elts, .. } if elts.len() == 2 => {
let mut check = Check::new(CheckKind::UnnecessaryListComprehensionDict, location);
if fix {
match fixes::fix_unnecessary_list_comprehension_dict(locator, expr) {
Ok(fix) => check.amend(fix),
Err(e) => error!("Failed to generate fix: {}", e),
}
}
return Some(check);
}
_ => {}
let ExprKind::ListComp { elt, .. } = &argument else {
return None;
};
let ExprKind::Tuple { elts, .. } = &elt.node else {
return None;
};
if elts.len() != 2 {
return None;
}
let mut check = Check::new(CheckKind::UnnecessaryListComprehensionDict, location);
if fix {
match fixes::fix_unnecessary_list_comprehension_dict(locator, expr) {
Ok(fix) => check.amend(fix),
Err(e) => error!("Failed to generate fix: {e}"),
}
}
None
Some(check)
}
/// C405 (`set([1, 2])`)
@@ -196,7 +197,7 @@ pub fn unnecessary_literal_set(
if fix {
match fixes::fix_unnecessary_literal_set(locator, expr) {
Ok(fix) => check.amend(fix),
Err(e) => error!("Failed to generate fix: {}", e),
Err(e) => error!("Failed to generate fix: {e}"),
}
}
Some(check)
@@ -232,7 +233,7 @@ pub fn unnecessary_literal_dict(
if fix {
match fixes::fix_unnecessary_literal_dict(locator, expr) {
Ok(fix) => check.amend(fix),
Err(e) => error!("Failed to generate fix: {}", e),
Err(e) => error!("Failed to generate fix: {e}"),
}
}
Some(check)
@@ -268,7 +269,7 @@ pub fn unnecessary_collection_call(
if fix {
match fixes::fix_unnecessary_collection_call(locator, expr) {
Ok(fix) => check.amend(fix),
Err(e) => error!("Failed to generate fix: {}", e),
Err(e) => error!("Failed to generate fix: {e}"),
}
}
Some(check)
@@ -296,7 +297,7 @@ pub fn unnecessary_literal_within_tuple_call(
if fix {
match fixes::fix_unnecessary_literal_within_tuple_call(locator, expr) {
Ok(fix) => check.amend(fix),
Err(e) => error!("Failed to generate fix: {}", e),
Err(e) => error!("Failed to generate fix: {e}"),
}
}
Some(check)
@@ -324,7 +325,7 @@ pub fn unnecessary_literal_within_list_call(
if fix {
match fixes::fix_unnecessary_literal_within_list_call(locator, expr) {
Ok(fix) => check.amend(fix),
Err(e) => error!("Failed to generate fix: {}", e),
Err(e) => error!("Failed to generate fix: {e}"),
}
}
Some(check)
@@ -340,17 +341,17 @@ pub fn unnecessary_list_call(
location: Range,
) -> Option<Check> {
let argument = first_argument_with_matching_function("list", func, args)?;
if let ExprKind::ListComp { .. } = argument {
let mut check = Check::new(CheckKind::UnnecessaryListCall, location);
if fix {
match fixes::fix_unnecessary_list_call(locator, expr) {
Ok(fix) => check.amend(fix),
Err(e) => error!("Failed to generate fix: {}", e),
}
}
return Some(check);
if !matches!(argument, ExprKind::ListComp { .. }) {
return None;
}
None
let mut check = Check::new(CheckKind::UnnecessaryListCall, location);
if fix {
match fixes::fix_unnecessary_list_call(locator, expr) {
Ok(fix) => check.amend(fix),
Err(e) => error!("Failed to generate fix: {e}"),
}
}
Some(check)
}
/// C413
@@ -366,22 +367,24 @@ pub fn unnecessary_call_around_sorted(
if !(outer == "list" || outer == "reversed") {
return None;
}
if let ExprKind::Call { func, .. } = &args.first()?.node {
if function_name(func)? == "sorted" {
let mut check = Check::new(
CheckKind::UnnecessaryCallAroundSorted(outer.to_string()),
location,
);
if fix {
match fixes::fix_unnecessary_call_around_sorted(locator, expr) {
Ok(fix) => check.amend(fix),
Err(e) => error!("Failed to generate fix: {}", e),
}
}
return Some(check);
let ExprKind::Call { func, .. } = &args.first()?.node else {
return None;
};
if function_name(func)? != "sorted" {
return None;
}
let mut check = Check::new(
CheckKind::UnnecessaryCallAroundSorted(outer.to_string()),
location,
);
if fix {
match fixes::fix_unnecessary_call_around_sorted(locator, expr) {
Ok(fix) => check.amend(fix),
Err(e) => error!("Failed to generate fix: {e}"),
}
}
None
Some(check)
}
/// C414
@@ -402,25 +405,28 @@ pub fn unnecessary_double_cast_or_process(
return None;
}
if let ExprKind::Call { func, .. } = &args.first()?.node {
let inner = function_name(func)?;
// Ex) set(tuple(...))
if (outer == "set" || outer == "sorted")
&& (inner == "list" || inner == "tuple" || inner == "reversed" || inner == "sorted")
{
return Some(new_check(inner, outer, location));
}
let ExprKind::Call { func, .. } = &args.first()?.node else {
return None;
};
// Ex) list(tuple(...))
if (outer == "list" || outer == "tuple") && (inner == "list" || inner == "tuple") {
return Some(new_check(inner, outer, location));
}
// Ex) set(set(...))
if outer == "set" && inner == "set" {
return Some(new_check(inner, outer, location));
}
let inner = function_name(func)?;
// Ex) set(tuple(...))
if (outer == "set" || outer == "sorted")
&& (inner == "list" || inner == "tuple" || inner == "reversed" || inner == "sorted")
{
return Some(new_check(inner, outer, location));
}
// Ex) list(tuple(...))
if (outer == "list" || outer == "tuple") && (inner == "list" || inner == "tuple") {
return Some(new_check(inner, outer, location));
}
// Ex) set(set(...))
if outer == "set" && inner == "set" {
return Some(new_check(inner, outer, location));
}
None
}
@@ -435,33 +441,34 @@ pub fn unnecessary_subscript_reversal(
if !["set", "sorted", "reversed"].contains(&id) {
return None;
}
if let ExprKind::Subscript { slice, .. } = &first_arg.node {
if let ExprKind::Slice { lower, upper, step } = &slice.node {
if lower.is_none() && upper.is_none() {
if let Some(step) = step {
if let ExprKind::UnaryOp {
op: Unaryop::USub,
operand,
} = &step.node
{
if let ExprKind::Constant {
value: Constant::Int(val),
..
} = &operand.node
{
if *val == BigInt::from(1) {
return Some(Check::new(
CheckKind::UnnecessarySubscriptReversal(id.to_string()),
location,
));
}
}
}
}
}
}
let ExprKind::Subscript { slice, .. } = &first_arg.node else {
return None;
};
let ExprKind::Slice { lower, upper, step } = &slice.node else {
return None;
};
if lower.is_some() || upper.is_some() {
return None;
}
None
let ExprKind::UnaryOp {
op: Unaryop::USub,
operand,
} = &step.as_ref()?.node else {
return None;
};
let ExprKind::Constant {
value: Constant::Int(val),
..
} = &operand.node else {
return None;
};
if *val != BigInt::from(1) {
return None;
};
Some(Check::new(
CheckKind::UnnecessarySubscriptReversal(id.to_string()),
location,
))
}
/// C416
@@ -497,7 +504,7 @@ pub fn unnecessary_comprehension(
if fix {
match fixes::fix_unnecessary_comprehension(locator, expr) {
Ok(fix) => check.amend(fix),
Err(e) => error!("Failed to generate fix: {}", e),
Err(e) => error!("Failed to generate fix: {e}"),
}
}
Some(check)

View File

@@ -41,6 +41,12 @@ pub fn debugger_call(
/// Checks for the presence of a debugger import.
pub fn debugger_import(stmt: &Stmt, module: Option<&str>, name: &str) -> Option<Check> {
// Special-case: allow `import builtins`, which is far more general than (e.g.)
// `import celery.contrib.rdb`).
if module.is_none() && name == "builtins" {
return None;
}
if let Some(module) = module {
if let Some((module_name, member)) = DEBUGGERS
.iter()

View File

@@ -9,39 +9,41 @@ 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(
let Some(mut check) = checks::print_call(
func,
checker.settings.enabled.contains(&CheckCode::T201),
checker.settings.enabled.contains(&CheckCode::T203),
Range::from_located(expr),
) {
if checker.patch(check.kind.code()) {
let context = checker.binding_context();
if matches!(
checker.parents[context.defined_by].node,
StmtKind::Expr { .. }
) else {
return;
};
if checker.patch(check.kind.code()) {
let context = checker.binding_context();
if matches!(
checker.parents[context.defined_by].node,
StmtKind::Expr { .. }
) {
let deleted: Vec<&Stmt> = checker
.deletions
.iter()
.map(|index| checker.parents[*index])
.collect();
match helpers::remove_stmt(
checker.parents[context.defined_by],
context.defined_in.map(|index| checker.parents[index]),
&deleted,
) {
let deleted: Vec<&Stmt> = checker
.deletions
.iter()
.map(|index| checker.parents[*index])
.collect();
match helpers::remove_stmt(
checker.parents[context.defined_by],
context.defined_in.map(|index| checker.parents[index]),
&deleted,
) {
Ok(fix) => {
if fix.content.is_empty() || fix.content == "pass" {
checker.deletions.insert(context.defined_by);
}
check.amend(fix);
Ok(fix) => {
if fix.content.is_empty() || fix.content == "pass" {
checker.deletions.insert(context.defined_by);
}
Err(e) => error!("Failed to remove print call: {}", e),
check.amend(fix);
}
Err(e) => error!("Failed to remove print call: {e}"),
}
}
checker.add_check(check);
}
checker.add_check(check);
}

View File

@@ -26,70 +26,70 @@ expression: checks
Debugger:
Import: builtins.breakpoint
location:
row: 5
row: 6
column: 0
end_location:
row: 5
row: 6
column: 31
fix: ~
- kind:
Debugger:
Import: pdb.set_trace
location:
row: 6
row: 7
column: 0
end_location:
row: 6
row: 7
column: 31
fix: ~
- kind:
Debugger:
Import: celery.contrib.rdb.set_trace
location:
row: 7
row: 8
column: 0
end_location:
row: 7
row: 8
column: 40
fix: ~
- kind:
Debugger:
Import: celery.contrib.rdb
location:
row: 9
row: 10
column: 0
end_location:
row: 9
row: 10
column: 25
fix: ~
- kind:
Debugger:
Call: breakpoint
location:
row: 12
row: 13
column: 0
end_location:
row: 12
row: 13
column: 12
fix: ~
- kind:
Debugger:
Call: set_trace
location:
row: 13
row: 14
column: 0
end_location:
row: 13
row: 14
column: 4
fix: ~
- kind:
Debugger:
Call: set_trace
location:
row: 14
row: 15
column: 0
end_location:
row: 14
row: 15
column: 11
fix: ~

View File

@@ -0,0 +1,18 @@
use rustpython_ast::{Constant, Expr, ExprKind, Stmt};
/// Return `true` if a function's return statement include at least one
/// non-`None` value.
pub fn result_exists(returns: &[(&Stmt, Option<&Expr>)]) -> bool {
returns.iter().any(|(_, expr)| {
expr.map(|expr| {
!matches!(
expr.node,
ExprKind::Constant {
value: Constant::None,
..
}
)
})
.unwrap_or(false)
})
}

37
src/flake8_return/mod.rs Normal file
View File

@@ -0,0 +1,37 @@
mod helpers;
pub mod plugins;
mod visitor;
#[cfg(test)]
mod tests {
use std::path::Path;
use anyhow::Result;
use test_case::test_case;
use crate::checks::CheckCode;
use crate::linter::test_path;
use crate::Settings;
#[test_case(CheckCode::RET501, Path::new("RET501.py"); "RET501")]
#[test_case(CheckCode::RET502, Path::new("RET502.py"); "RET502")]
#[test_case(CheckCode::RET503, Path::new("RET503.py"); "RET503")]
#[test_case(CheckCode::RET504, Path::new("RET504.py"); "RET504")]
#[test_case(CheckCode::RET505, Path::new("RET505.py"); "RET505")]
#[test_case(CheckCode::RET506, Path::new("RET506.py"); "RET506")]
#[test_case(CheckCode::RET507, Path::new("RET507.py"); "RET507")]
#[test_case(CheckCode::RET508, Path::new("RET508.py"); "RET508")]
fn checks(check_code: CheckCode, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", check_code.as_ref(), path.to_string_lossy());
let mut checks = test_path(
Path::new("./resources/test/fixtures/flake8_return")
.join(path)
.as_path(),
&Settings::for_rule(check_code),
true,
)?;
checks.sort_by_key(|check| check.location);
insta::assert_yaml_snapshot!(snapshot, checks);
Ok(())
}
}

View File

@@ -0,0 +1,348 @@
use itertools::Itertools;
use rustpython_ast::{Constant, Expr, ExprKind, Location, Stmt, StmtKind};
use crate::ast::types::Range;
use crate::ast::visitor::Visitor;
use crate::ast::whitespace::indentation;
use crate::autofix::Fix;
use crate::check_ast::Checker;
use crate::checks::{Branch, CheckCode, CheckKind};
use crate::flake8_return::helpers::result_exists;
use crate::flake8_return::visitor::{ReturnVisitor, Stack};
use crate::Check;
/// RET501
fn unnecessary_return_none(checker: &mut Checker, stack: &Stack) {
for (stmt, expr) in &stack.returns {
let Some(expr) = expr else {
continue;
};
if !matches!(
expr.node,
ExprKind::Constant {
value: Constant::None,
..
}
) {
continue;
}
let mut check = Check::new(CheckKind::UnnecessaryReturnNone, Range::from_located(stmt));
if checker.patch(&CheckCode::RET501) {
check.amend(Fix::replacement(
"return".to_string(),
stmt.location,
stmt.end_location.unwrap(),
));
}
checker.add_check(check);
}
}
/// RET502
fn implicit_return_value(checker: &mut Checker, stack: &Stack) {
for (stmt, expr) in &stack.returns {
if expr.is_some() {
continue;
}
let mut check = Check::new(CheckKind::ImplicitReturnValue, Range::from_located(stmt));
if checker.patch(&CheckCode::RET502) {
check.amend(Fix::replacement(
"return None".to_string(),
stmt.location,
stmt.end_location.unwrap(),
));
}
checker.add_check(check);
}
}
/// RET503
fn implicit_return(checker: &mut Checker, last_stmt: &Stmt) {
match &last_stmt.node {
StmtKind::If { body, orelse, .. } => {
if body.is_empty() || orelse.is_empty() {
checker.add_check(Check::new(
CheckKind::ImplicitReturn,
Range::from_located(last_stmt),
));
return;
}
if let Some(last_stmt) = body.last() {
implicit_return(checker, last_stmt);
}
if let Some(last_stmt) = orelse.last() {
implicit_return(checker, last_stmt);
}
}
StmtKind::For { body, orelse, .. } | StmtKind::AsyncFor { body, orelse, .. } => {
if let Some(last_stmt) = orelse.last() {
implicit_return(checker, last_stmt);
} else if let Some(last_stmt) = body.last() {
implicit_return(checker, last_stmt);
}
}
StmtKind::With { body, .. } | StmtKind::AsyncWith { body, .. } => {
if let Some(last_stmt) = body.last() {
implicit_return(checker, last_stmt);
}
}
StmtKind::Assert { test, .. }
if matches!(
test.node,
ExprKind::Constant {
value: Constant::Bool(false),
..
}
) => {}
StmtKind::Return { .. }
| StmtKind::While { .. }
| StmtKind::Raise { .. }
| StmtKind::Try { .. } => {}
_ => {
let mut check = Check::new(CheckKind::ImplicitReturn, Range::from_located(last_stmt));
if checker.patch(&CheckCode::RET503) {
let mut content = String::new();
content.push_str(&indentation(checker, last_stmt));
content.push_str("return None");
content.push('\n');
check.amend(Fix::insertion(
content,
Location::new(last_stmt.end_location.unwrap().row() + 1, 0),
));
}
checker.add_check(check);
}
}
}
fn has_refs_before_next_assign(id: &str, return_location: Location, stack: &Stack) -> bool {
let mut before_assign: &Location = &Location::default();
let mut after_assign: Option<&Location> = None;
if let Some(assigns) = stack.assigns.get(&id) {
for location in assigns.iter().sorted() {
if location.row() > return_location.row() {
after_assign = Some(location);
break;
}
if location.row() <= return_location.row() {
before_assign = location;
}
}
}
if let Some(refs) = stack.refs.get(&id) {
for location in refs {
if location.row() == return_location.row() {
continue;
}
if let Some(after_assign) = after_assign {
if before_assign.row() < location.row() && location.row() <= after_assign.row() {
return true;
}
} else if before_assign.row() < location.row() {
return true;
}
}
}
false
}
fn has_refs_or_assigns_within_try_or_loop(id: &str, stack: &Stack) -> bool {
if let Some(refs) = stack.refs.get(&id) {
for location in refs {
for (try_location, try_end_location) in &stack.tries {
if try_location.row() < location.row() && location.row() <= try_end_location.row() {
return true;
}
}
for (loop_location, loop_end_location) in &stack.loops {
if loop_location.row() < location.row() && location.row() <= loop_end_location.row()
{
return true;
}
}
}
}
if let Some(refs) = stack.assigns.get(&id) {
for location in refs {
for (try_location, try_end_location) in &stack.tries {
if try_location.row() < location.row() && location.row() <= try_end_location.row() {
return true;
}
}
for (loop_location, loop_end_location) in &stack.loops {
if loop_location.row() < location.row() && location.row() <= loop_end_location.row()
{
return true;
}
}
}
}
false
}
/// RET504
fn unnecessary_assign(checker: &mut Checker, stack: &Stack, expr: &Expr) {
if let ExprKind::Name { id, .. } = &expr.node {
if !stack.assigns.contains_key(id.as_str()) {
return;
}
if !stack.refs.contains_key(id.as_str()) {
checker.add_check(Check::new(
CheckKind::UnnecessaryAssign,
Range::from_located(expr),
));
return;
}
if has_refs_before_next_assign(id, expr.location, stack)
|| has_refs_or_assigns_within_try_or_loop(id, stack)
{
return;
}
checker.add_check(Check::new(
CheckKind::UnnecessaryAssign,
Range::from_located(expr),
));
}
}
/// RET505, RET506, RET507, RET508
fn superfluous_else_node(checker: &mut Checker, stmt: &Stmt, branch: Branch) -> bool {
let StmtKind::If { body, .. } = &stmt.node else {
return false;
};
for child in body {
if matches!(child.node, StmtKind::Return { .. }) {
if checker.settings.enabled.contains(&CheckCode::RET505) {
checker.add_check(Check::new(
CheckKind::SuperfluousElseReturn(branch),
Range::from_located(stmt),
));
}
return true;
}
if matches!(child.node, StmtKind::Break) {
if checker.settings.enabled.contains(&CheckCode::RET508) {
checker.add_check(Check::new(
CheckKind::SuperfluousElseBreak(branch),
Range::from_located(stmt),
));
}
return true;
}
if matches!(child.node, StmtKind::Raise { .. }) {
if checker.settings.enabled.contains(&CheckCode::RET506) {
checker.add_check(Check::new(
CheckKind::SuperfluousElseRaise(branch),
Range::from_located(stmt),
));
}
return true;
}
if matches!(child.node, StmtKind::Continue) {
if checker.settings.enabled.contains(&CheckCode::RET507) {
checker.add_check(Check::new(
CheckKind::SuperfluousElseContinue(branch),
Range::from_located(stmt),
));
}
return true;
}
}
false
}
/// RET505, RET506, RET507, RET508
fn superfluous_elif(checker: &mut Checker, stack: &Stack) -> bool {
for stmt in &stack.elifs {
if superfluous_else_node(checker, stmt, Branch::Elif) {
return true;
}
}
false
}
/// RET505, RET506, RET507, RET508
fn superfluous_else(checker: &mut Checker, stack: &Stack) -> bool {
for stmt in &stack.ifs {
let StmtKind::If { orelse, .. } = &stmt.node else {
continue;
};
if orelse.is_empty() {
continue;
}
if superfluous_else_node(checker, stmt, Branch::Else) {
return true;
}
}
false
}
/// Run all checks from the `flake8-return` plugin.
pub fn function(checker: &mut Checker, body: &[Stmt]) {
// Skip empty functions.
if body.is_empty() {
return;
}
// Find the last statement in the function.
let last_stmt = body.last().unwrap();
// Skip functions that consist of a single return statement.
if body.len() == 1 && matches!(last_stmt.node, StmtKind::Return { .. }) {
return;
}
// Traverse the function body, to collect the stack.
let stack = {
let mut visitor = ReturnVisitor::default();
for stmt in body {
visitor.visit_stmt(stmt);
}
visitor.stack
};
if checker.settings.enabled.contains(&CheckCode::RET505)
|| checker.settings.enabled.contains(&CheckCode::RET506)
|| checker.settings.enabled.contains(&CheckCode::RET507)
|| checker.settings.enabled.contains(&CheckCode::RET508)
{
if superfluous_elif(checker, &stack) {
return;
}
if superfluous_else(checker, &stack) {
return;
}
}
// Skip any functions without return statements.
if stack.returns.is_empty() {
return;
}
if !result_exists(&stack.returns) {
if checker.settings.enabled.contains(&CheckCode::RET501) {
unnecessary_return_none(checker, &stack);
}
return;
}
if checker.settings.enabled.contains(&CheckCode::RET502) {
implicit_return_value(checker, &stack);
}
if checker.settings.enabled.contains(&CheckCode::RET503) {
implicit_return(checker, last_stmt);
}
if checker.settings.enabled.contains(&CheckCode::RET504) {
for (_, expr) in &stack.returns {
if let Some(expr) = expr {
unnecessary_assign(checker, &stack, expr);
}
}
}
}

View File

@@ -0,0 +1,20 @@
---
source: src/flake8_return/mod.rs
expression: checks
---
- kind: UnnecessaryReturnNone
location:
row: 4
column: 4
end_location:
row: 4
column: 15
fix:
content: return
location:
row: 4
column: 4
end_location:
row: 4
column: 15

View File

@@ -0,0 +1,20 @@
---
source: src/flake8_return/mod.rs
expression: checks
---
- kind: ImplicitReturnValue
location:
row: 3
column: 8
end_location:
row: 3
column: 14
fix:
content: return None
location:
row: 3
column: 8
end_location:
row: 3
column: 14

View File

@@ -0,0 +1,66 @@
---
source: src/flake8_return/mod.rs
expression: checks
---
- kind: ImplicitReturn
location:
row: 7
column: 4
end_location:
row: 12
column: 0
fix: ~
- kind: ImplicitReturn
location:
row: 14
column: 8
end_location:
row: 14
column: 15
fix:
content: " return None\n"
location:
row: 15
column: 0
end_location:
row: 15
column: 0
- kind: ImplicitReturn
location:
row: 23
column: 4
end_location:
row: 23
column: 11
fix:
content: " return None\n"
location:
row: 24
column: 0
end_location:
row: 24
column: 0
- kind: ImplicitReturn
location:
row: 29
column: 8
end_location:
row: 34
column: 0
fix: ~
- kind: ImplicitReturn
location:
row: 39
column: 8
end_location:
row: 39
column: 15
fix:
content: " return None\n"
location:
row: 40
column: 0
end_location:
row: 40
column: 0

View File

@@ -0,0 +1,61 @@
---
source: src/flake8_return/mod.rs
expression: checks
---
- kind: UnnecessaryAssign
location:
row: 6
column: 11
end_location:
row: 6
column: 12
fix: ~
- kind: UnnecessaryAssign
location:
row: 12
column: 11
end_location:
row: 12
column: 12
fix: ~
- kind: UnnecessaryAssign
location:
row: 18
column: 11
end_location:
row: 18
column: 12
fix: ~
- kind: UnnecessaryAssign
location:
row: 25
column: 11
end_location:
row: 25
column: 12
fix: ~
- kind: UnnecessaryAssign
location:
row: 31
column: 15
end_location:
row: 31
column: 16
fix: ~
- kind: UnnecessaryAssign
location:
row: 43
column: 11
end_location:
row: 43
column: 17
fix: ~
- kind: UnnecessaryAssign
location:
row: 51
column: 11
end_location:
row: 51
column: 20
fix: ~

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