Merge branch 'main' into sasanjac/2195-Implement-configuration-options-from-`flake8-type-checking`

This commit is contained in:
Charlie Marsh 2023-03-05 18:05:53 -05:00
commit b307200e42
263 changed files with 4900 additions and 2261 deletions

View File

@ -109,21 +109,6 @@ jobs:
./scripts/add_rule.py --name FirstRule --code TST001 --linter test
- run: cargo check
maturin-build:
name: "maturin build"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: "Install Rust toolchain"
run: rustup show
- uses: Swatinem/rust-cache@v1
- uses: actions/setup-python@v4
with:
python-version: "3.11"
- run: pip install maturin
- run: maturin build -b bin
- run: python scripts/transform_readme.py --target pypi
typos:
name: "spell check"
runs-on: ubuntu-latest

188
Cargo.lock generated
View File

@ -289,9 +289,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.1.6"
version = "4.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0b0588d44d4d63a87dbd75c136c166bbfd9a86a31cb89e09906521c7d3f5e3"
checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5"
dependencies = [
"bitflags",
"clap_derive",
@ -308,7 +308,7 @@ version = "4.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd125be87bf4c255ebc50de0b7f4d2a6201e8ac3dc86e39c0ad081dc5e7236fe"
dependencies = [
"clap 4.1.6",
"clap 4.1.8",
]
[[package]]
@ -317,7 +317,7 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4160b4a4f72ef58bd766bad27c09e6ef1cc9d82a22f6a0f55d152985a4a48e31"
dependencies = [
"clap 4.1.6",
"clap 4.1.8",
"clap_complete",
"clap_complete_fig",
]
@ -328,15 +328,15 @@ version = "4.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63a06158a72dbb088f864887b4409fd22600ba379f3cee3040f842234cc5c2d0"
dependencies = [
"clap 4.1.6",
"clap 4.1.8",
"clap_complete",
]
[[package]]
name = "clap_derive"
version = "4.1.0"
version = "4.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8"
checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0"
dependencies = [
"heck",
"proc-macro-error",
@ -427,9 +427,9 @@ dependencies = [
[[package]]
name = "console_log"
version = "0.2.0"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "501a375961cef1a0d44767200e66e4a559283097e91d0730b1d75dfb2f8a1494"
checksum = "878ad067d4089144a36ee412d665916c665430eb84c0057b9987f424a5d15c03"
dependencies = [
"log",
"web-sys",
@ -770,10 +770,10 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8-to-ruff"
version = "0.0.253"
version = "0.0.254"
dependencies = [
"anyhow",
"clap 4.1.6",
"clap 4.1.8",
"colored",
"configparser",
"once_cell",
@ -1080,12 +1080,6 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
[[package]]
name = "joinery"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72167d68f5fce3b8655487b8038691a3c9984ee769590f93f2a631f4ad64e4f5"
[[package]]
name = "js-sys"
version = "0.3.61"
@ -1369,15 +1363,6 @@ dependencies = [
"version_check",
]
[[package]]
name = "nom8"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8"
dependencies = [
"memchr",
]
[[package]]
name = "notify"
version = "5.1.0"
@ -1405,7 +1390,6 @@ dependencies = [
"autocfg",
"num-integer",
"num-traits",
"serde",
]
[[package]]
@ -1415,7 +1399,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d"
dependencies = [
"num-traits",
"serde",
]
[[package]]
@ -1447,27 +1430,6 @@ dependencies = [
"libc",
]
[[package]]
name = "num_enum"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e0072973714303aa6e3631c7e8e777970cf4bdd25dc4932e41031027b8bcc4e"
dependencies = [
"num_enum_derive",
]
[[package]]
name = "num_enum_derive"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0629cbd6b897944899b1f10496d9c4a7ac5878d45fd61bc22e9e79bfbbc29597"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "once_cell"
version = "1.17.1"
@ -1757,16 +1719,6 @@ dependencies = [
"termtree",
]
[[package]]
name = "proc-macro-crate"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66618389e4ec1c7afe67d51a9bf34ff9236480f8d51e7489b7d5ab0303c13f34"
dependencies = [
"once_cell",
"toml_edit",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
@ -1870,9 +1822,9 @@ dependencies = [
[[package]]
name = "rayon"
version = "1.6.1"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7"
checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
dependencies = [
"either",
"rayon-core",
@ -1880,9 +1832,9 @@ dependencies = [
[[package]]
name = "rayon-core"
version = "1.10.2"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b"
checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
@ -1981,14 +1933,14 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.253"
version = "0.0.254"
dependencies = [
"anyhow",
"bisection",
"bitflags",
"cfg-if",
"chrono",
"clap 4.1.6",
"clap 4.1.8",
"colored",
"console_error_panic_hook",
"console_log",
@ -2015,6 +1967,7 @@ dependencies = [
"path-absolutize",
"regex",
"result-like",
"ruff_cache",
"ruff_macros",
"ruff_python",
"ruff_rustpython",
@ -2032,15 +1985,25 @@ dependencies = [
"test-case",
"textwrap",
"thiserror",
"titlecase",
"toml",
"wasm-bindgen",
"wasm-bindgen-test",
]
[[package]]
name = "ruff_cache"
version = "0.0.0"
dependencies = [
"filetime",
"globset",
"itertools",
"regex",
"ruff_macros",
]
[[package]]
name = "ruff_cli"
version = "0.0.253"
version = "0.0.254"
dependencies = [
"annotate-snippets 0.9.1",
"anyhow",
@ -2050,7 +2013,7 @@ dependencies = [
"bitflags",
"cachedir",
"chrono",
"clap 4.1.6",
"clap 4.1.8",
"clap_complete_command",
"clearscreen",
"colored",
@ -2066,6 +2029,7 @@ dependencies = [
"rayon",
"regex",
"ruff",
"ruff_cache",
"rustc-hash",
"serde",
"serde_json",
@ -2083,7 +2047,7 @@ name = "ruff_dev"
version = "0.0.0"
dependencies = [
"anyhow",
"clap 4.1.6",
"clap 4.1.8",
"itertools",
"libcst",
"once_cell",
@ -2138,7 +2102,7 @@ name = "ruff_python_formatter"
version = "0.0.0"
dependencies = [
"anyhow",
"clap 4.1.6",
"clap 4.1.8",
"insta",
"is-macro",
"itertools",
@ -2231,7 +2195,7 @@ dependencies = [
[[package]]
name = "rustpython-ast"
version = "0.2.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=aa8336ee94492b52458ed8e1517238e5c6c2914c#aa8336ee94492b52458ed8e1517238e5c6c2914c"
source = "git+https://github.com/RustPython/RustPython.git?rev=1871a1632e310985414211222f5bf8069678892f#1871a1632e310985414211222f5bf8069678892f"
dependencies = [
"num-bigint",
"rustpython-compiler-core",
@ -2240,7 +2204,7 @@ dependencies = [
[[package]]
name = "rustpython-common"
version = "0.2.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=aa8336ee94492b52458ed8e1517238e5c6c2914c#aa8336ee94492b52458ed8e1517238e5c6c2914c"
source = "git+https://github.com/RustPython/RustPython.git?rev=1871a1632e310985414211222f5bf8069678892f#1871a1632e310985414211222f5bf8069678892f"
dependencies = [
"ascii",
"bitflags",
@ -2265,24 +2229,21 @@ dependencies = [
[[package]]
name = "rustpython-compiler-core"
version = "0.2.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=aa8336ee94492b52458ed8e1517238e5c6c2914c#aa8336ee94492b52458ed8e1517238e5c6c2914c"
source = "git+https://github.com/RustPython/RustPython.git?rev=1871a1632e310985414211222f5bf8069678892f#1871a1632e310985414211222f5bf8069678892f"
dependencies = [
"bincode",
"bitflags",
"bstr 0.2.17",
"itertools",
"lz4_flex",
"num-bigint",
"num-complex",
"num_enum",
"serde",
"thiserror",
]
[[package]]
name = "rustpython-parser"
version = "0.2.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=aa8336ee94492b52458ed8e1517238e5c6c2914c#aa8336ee94492b52458ed8e1517238e5c6c2914c"
source = "git+https://github.com/RustPython/RustPython.git?rev=1871a1632e310985414211222f5bf8069678892f#1871a1632e310985414211222f5bf8069678892f"
dependencies = [
"ahash",
"anyhow",
@ -2297,7 +2258,7 @@ dependencies = [
"rustc-hash",
"rustpython-ast",
"rustpython-compiler-core",
"thiserror",
"serde",
"tiny-keccak",
"unic-emoji-char",
"unic-ucd-ident",
@ -2327,9 +2288,9 @@ dependencies = [
[[package]]
name = "schemars"
version = "0.8.11"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307"
checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f"
dependencies = [
"dyn-clone",
"schemars_derive",
@ -2339,9 +2300,9 @@ dependencies = [
[[package]]
name = "schemars_derive"
version = "0.8.11"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9"
checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c"
dependencies = [
"proc-macro2",
"quote",
@ -2553,9 +2514,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.108"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d56e159d99e6c2b93995d171050271edb50ecc5288fbc7cc17de8fdce4e58c14"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
@ -2626,18 +2587,18 @@ checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8"
[[package]]
name = "test-case"
version = "2.2.2"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21d6cf5a7dffb3f9dceec8e6b8ca528d9bd71d36c9f074defb548ce161f598c0"
checksum = "679b019fb241da62cc449b33b224d19ebe1c6767b495569765115dd7f7f9fba4"
dependencies = [
"test-case-macros",
]
[[package]]
name = "test-case-macros"
version = "2.2.2"
name = "test-case-core"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e45b7bf6e19353ddd832745c8fcf77a17a93171df7151187f26623f2b75b5b26"
checksum = "72dc21b5887f4032c4656502d085dc28f2afbb686f25f216472bb0526f4b1b88"
dependencies = [
"cfg-if",
"proc-macro-error",
@ -2646,6 +2607,19 @@ dependencies = [
"syn",
]
[[package]]
name = "test-case-macros"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3786898e0be151a96f730fd529b0e8a10f5990fa2a7ea14e37ca27613c05190"
dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
"test-case-core",
]
[[package]]
name = "textwrap"
version = "0.16.0"
@ -2752,22 +2726,11 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "titlecase"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38397a8cdb017cfeb48bf6c154d6de975ac69ffeed35980fde199d2ee0842042"
dependencies = [
"joinery",
"lazy_static",
"regex",
]
[[package]]
name = "toml"
version = "0.6.0"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb9d890e4dc9298b70f740f615f2e05b9db37dce531f6b24fb77ac993f9f217"
checksum = "f7afcae9e3f0fe2c370fd4657108972cbb2fa9db1b9f84849cefd80741b01cb6"
dependencies = [
"serde",
"serde_spanned",
@ -2777,24 +2740,24 @@ dependencies = [
[[package]]
name = "toml_datetime"
version = "0.5.1"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5"
checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.18.1"
version = "0.19.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b"
checksum = "9a1eb0622d28f4b9c90adc4ea4b2b46b47663fde9ac5fafcb14a1369d5508825"
dependencies = [
"indexmap",
"nom8",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
]
[[package]]
@ -2949,7 +2912,7 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "unicode_names2"
version = "0.6.0"
source = "git+https://github.com/youknowone/unicode_names2.git?tag=v0.6.0+character-alias#4ce16aa85cbcdd9cc830410f1a72ef9a235f2fde"
source = "git+https://github.com/youknowone/unicode_names2.git?rev=4ce16aa85cbcdd9cc830410f1a72ef9a235f2fde#4ce16aa85cbcdd9cc830410f1a72ef9a235f2fde"
dependencies = [
"phf",
]
@ -3285,6 +3248,15 @@ version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
[[package]]
name = "winnow"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "faf09497b8f8b5ac5d3bb4d05c0a99be20f26fd3d5f2db7b0716e946d5103658"
dependencies = [
"memchr",
]
[[package]]
name = "yaml-rust"
version = "0.4.5"

View File

@ -6,22 +6,42 @@ edition = "2021"
rust-version = "1.67.0"
[workspace.dependencies]
anyhow = { version = "1.0.66" }
clap = { version = "4.0.1", features = ["derive"] }
itertools = { version = "0.10.5" }
anyhow = { version = "1.0.69" }
bitflags = { version = "1.3.2" }
chrono = { version = "0.4.23", default-features = false, features = ["clock"] }
clap = { version = "4.1.8", features = ["derive"] }
colored = { version = "2.0.0" }
filetime = { version = "0.2.20" }
glob = { version = "0.3.1" }
globset = { version = "0.4.10" }
ignore = { version = "0.4.20" }
insta = { version = "1.28.0" }
is-macro = { version = "0.2.2" }
itertools = { version = "0.10.5" }
libcst = { git = "https://github.com/charliermarsh/LibCST", rev = "80e4c1399f95e5beb532fdd1e209ad2dbb470438" }
once_cell = { version = "1.16.0" }
regex = { version = "1.6.0" }
log = { version = "0.4.17" }
once_cell = { version = "1.17.1" }
path-absolutize = { version = "3.0.14" }
proc-macro2 = { version = "1.0.51" }
quote = { version = "1.0.23" }
regex = { version = "1.7.1" }
rustc-hash = { version = "1.1.0" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "aa8336ee94492b52458ed8e1517238e5c6c2914c" }
rustpython-parser = { features = ["lalrpop"], git = "https://github.com/RustPython/RustPython.git", rev = "aa8336ee94492b52458ed8e1517238e5c6c2914c" }
schemars = { version = "0.8.11" }
serde = { version = "1.0.147", features = ["derive"] }
serde_json = { version = "1.0.87" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "1871a1632e310985414211222f5bf8069678892f" }
rustpython-parser = { features = [
"lalrpop",
"serde",
], git = "https://github.com/RustPython/RustPython.git", rev = "1871a1632e310985414211222f5bf8069678892f" }
schemars = { version = "0.8.12" }
serde = { version = "1.0.152", features = ["derive"] }
serde_json = { version = "1.0.93" }
shellexpand = { version = "3.0.0" }
similar = { version = "2.2.1" }
strum = { version = "0.24.1", features = ["strum_macros"] }
strum_macros = { version = "0.24.3" }
toml = { version = "0.6.0" }
syn = { version = "1.0.109" }
test-case = { version = "3.0.0" }
textwrap = { version = "0.16.0" }
toml = { version = "0.7.2" }
[profile.release]
panic = "abort"

View File

@ -137,7 +137,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com) hook:
```yaml
- repo: https://github.com/charliermarsh/ruff-pre-commit
# Ruff version.
rev: 'v0.0.253'
rev: 'v0.0.254'
hooks:
- id: ruff
```

View File

@ -1,13 +1,13 @@
[package]
name = "flake8-to-ruff"
version = "0.0.253"
version = "0.0.254"
edition = { workspace = true }
rust-version = { workspace = true }
[dependencies]
anyhow = { workspace = true }
clap = { workspace = true }
colored = { version = "2.0.0" }
colored = { workspace = true }
configparser = { version = "3.0.2" }
once_cell = { workspace = true }
regex = { workspace = true }

View File

@ -1,6 +1,6 @@
[package]
name = "ruff"
version = "0.0.253"
version = "0.0.254"
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
edition = { workspace = true }
rust-version = { workspace = true }
@ -19,61 +19,64 @@ doctest = false
ruff_macros = { path = "../ruff_macros" }
ruff_python = { path = "../ruff_python" }
ruff_rustpython = { path = "../ruff_rustpython" }
ruff_cache = { path = "../ruff_cache" }
anyhow = { workspace = true }
bisection = { version = "0.1.0" }
bitflags = { version = "1.3.2" }
bitflags = { workspace = true }
cfg-if = { version = "1.0.0" }
chrono = { version = "0.4.21", default-features = false, features = ["clock"] }
chrono = { workspace = true }
clap = { workspace = true, features = ["derive", "env", "string"] }
colored = { version = "2.0.0" }
colored = { workspace = true }
derivative = { version = "2.2.0" }
dirs = { version = "4.0.0" }
fern = { version = "0.6.1" }
glob = { version = "0.3.0" }
globset = { version = "0.4.9" }
ignore = { version = "0.4.18" }
imperative = { version = "1.0.3" }
glob = { workspace = true }
globset = { workspace = true }
ignore = { workspace = true }
imperative = { version = "1.0.4" }
is-macro = { workspace = true }
itertools = { workspace = true }
libcst = { workspace = true }
log = { version = "0.4.17" }
log = { workspace = true }
natord = { version = "1.0.9" }
nohash-hasher = { version = "0.2.0" }
num-bigint = { version = "0.4.3" }
num-traits = "0.2.15"
num-traits = { version = "0.2.15" }
once_cell = { workspace = true }
path-absolutize = { version = "3.0.14", features = ["once_cell_cache", "use_unix_paths_on_wasm"] }
path-absolutize = { workspace = true, features = [
"once_cell_cache",
"use_unix_paths_on_wasm",
] }
regex = { workspace = true }
result-like = "0.4.6"
result-like = { version = "0.4.6" }
rustc-hash = { workspace = true }
rustpython-common = { workspace = true }
rustpython-parser = { workspace = true }
schemars = { workspace = true }
semver = { version = "1.0.16" }
serde = { workspace = true }
shellexpand = { version = "3.0.0" }
shellexpand = { workspace = true }
smallvec = { version = "1.10.0" }
strum = { workspace = true }
strum_macros = { workspace = true }
textwrap = { version = "0.16.0" }
thiserror = { version = "1.0" }
titlecase = { version = "2.2.1" }
textwrap = { workspace = true }
thiserror = { version = "1.0.38" }
toml = { workspace = true }
# https://docs.rs/getrandom/0.2.7/getrandom/#webassembly-support
[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies]
getrandom = { version = "0.2.7", features = ["js"] }
getrandom = { version = "0.2.8", features = ["js"] }
console_error_panic_hook = { version = "0.1.7" }
console_log = { version = "0.2.0" }
serde-wasm-bindgen = { version = "0.4" }
js-sys = { version = "0.3.60" }
wasm-bindgen = { version = "0.2.83" }
console_log = { version = "0.2.1" }
serde-wasm-bindgen = { version = "0.4.5" }
js-sys = { version = "0.3.61" }
wasm-bindgen = { version = "0.2.84" }
[dev-dependencies]
insta = { version = "1.19.0", features = ["yaml", "redactions"] }
test-case = { version = "2.2.2" }
wasm-bindgen-test = { version = "0.3.33" }
insta = { workspace = true, features = ["yaml", "redactions"] }
test-case = { workspace = true }
wasm-bindgen-test = { version = "0.3.34" }
[target.'cfg(not(target_family = "wasm"))'.dev-dependencies]
criterion = { version = "0.4.0" }

View File

@ -0,0 +1,33 @@
# From https://github.com/PyCQA/flake8-pyi/blob/4212bec43dbc4020a59b90e2957c9488575e57ba/tests/type_comments.pyi
from collections.abc import Sequence
from typing import TypeAlias
A: TypeAlias = None # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
B: TypeAlias = None # type: str # And here's an extra comment about why it's that type # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
C: TypeAlias = None #type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
D: TypeAlias = None # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
E: TypeAlias = None# type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
F: TypeAlias = None#type:int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
def func(
arg1, # type: dict[str, int] # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
arg2 # type: Sequence[bytes] # And here's some more info about this arg # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
): ...
class Foo:
Attr: TypeAlias = None # type: set[str] # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
G: TypeAlias = None # type: ignore
H: TypeAlias = None # type: ignore[attr-defined]
I: TypeAlias = None #type: ignore
J: TypeAlias = None # type: ignore
K: TypeAlias = None# type: ignore
L: TypeAlias = None#type:ignore
# Whole line commented out # type: int
M: TypeAlias = None # type: can't parse me!
class Bar:
N: TypeAlias = None # type: can't parse me either!
# This whole line is commented out and indented # type: str

View File

@ -0,0 +1,33 @@
# From https://github.com/PyCQA/flake8-pyi/blob/4212bec43dbc4020a59b90e2957c9488575e57ba/tests/type_comments.pyi
from collections.abc import Sequence
from typing import TypeAlias
A: TypeAlias = None # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
B: TypeAlias = None # type: str # And here's an extra comment about why it's that type # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
C: TypeAlias = None #type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
D: TypeAlias = None # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
E: TypeAlias = None# type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
F: TypeAlias = None#type:int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
def func(
arg1, # type: dict[str, int] # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
arg2 # type: Sequence[bytes] # And here's some more info about this arg # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
): ...
class Foo:
Attr: TypeAlias = None # type: set[str] # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
G: TypeAlias = None # type: ignore
H: TypeAlias = None # type: ignore[attr-defined]
I: TypeAlias = None #type: ignore
J: TypeAlias = None # type: ignore
K: TypeAlias = None# type: ignore
L: TypeAlias = None#type:ignore
# Whole line commented out # type: int
M: TypeAlias = None # type: can't parse me!
class Bar:
N: TypeAlias = None # type: can't parse me either!
# This whole line is commented out and indented # type: str

View File

@ -293,3 +293,30 @@ def x(y):
def foo(baz: str) -> str:
return baz
def end_of_statement():
def example():
if True:
return ""
def example():
if True:
return ""
def example():
if True:
return "" # type: ignore
def example():
if True:
return "" ;
def example():
if True:
return "" \
; # type: ignore

View File

@ -6,6 +6,8 @@
"yoda" <= compare # SIM300
"yoda" < compare # SIM300
42 > age # SIM300
-42 > age # SIM300
+42 > age # SIM300
YODA == age # SIM300
YODA > age # SIM300
YODA >= age # SIM300

View File

@ -1,7 +1,8 @@
from typing import TYPE_CHECKING, Any, ClassVar
import attrs
from ....import unknown
from ..protocol import commands, definitions, responses
from ..server import example
from .. import server

View File

@ -0,0 +1,14 @@
#: E211
spam (1)
#: E211 E211
dict ['key'] = list [index]
#: E211
dict['key'] ['subkey'] = list[index]
#: Okay
spam(1)
dict['key'] = list[index]
# This is not prohibited by PEP8, but avoid it.
class Foo (Bar, Baz):
pass

View File

@ -0,0 +1,15 @@
#: E231
a = (1,2)
#: E231
a[b1,:]
#: E231
a = [{'a':''}]
#: Okay
a = (4,)
b = (5, )
c = {'text': text[5:]}
result = {
'key1': 'value',
'key2': 'value',
}

View File

@ -2,6 +2,8 @@
from functools import cached_property
from gi.repository import GObject
# Bad examples
def bad_liouiwnlkjl():
@ -76,6 +78,11 @@ class Thingy:
"""This property method docstring does not need to be written in imperative mood."""
return self._beep
@GObject.Property
def good_custom_property(self):
"""This property method docstring does not need to be written in imperative mood."""
return self._beep
@cached_property
def good_cached_property(self):
"""This property method docstring does not need to be written in imperative mood."""

View File

@ -0,0 +1,23 @@
"""Test case: strings used within calls within type annotations."""
from typing import Callable
import bpy
from mypy_extensions import VarArg
from foo import Bar
class LightShow(bpy.types.Operator):
label = "Create Character"
name = "lightshow.letter_creation"
filepath: bpy.props.StringProperty(subtype="FILE_PATH") # OK
def f(x: Callable[[VarArg("os")], None]): # F821
pass
f(Callable[["Bar"], None])
f(Callable[["Baz"], None])

View File

@ -0,0 +1,25 @@
"""Test case: strings used within calls within type annotations."""
from __future__ import annotations
from typing import Callable
import bpy
from mypy_extensions import VarArg
from foo import Bar
class LightShow(bpy.types.Operator):
label = "Create Character"
name = "lightshow.letter_creation"
filepath: bpy.props.StringProperty(subtype="FILE_PATH") # OK
def f(x: Callable[[VarArg("os")], None]): # F821
pass
f(Callable[["Bar"], None])
f(Callable[["Baz"], None])

View File

@ -11,6 +11,9 @@ if 10 == 100: # [comparison-of-constants] R0133
if 1 == 3: # [comparison-of-constants] R0133
pass
if 1 == -3: # [comparison-of-constants] R0133
pass
x = 0
if 4 == 3 == x: # [comparison-of-constants] R0133
pass
@ -35,6 +38,12 @@ if argc != 1: # correct
if argc != 2: # [magic-value-comparison]
pass
if argc != -2: # [magic-value-comparison]
pass
if argc != +2: # [magic-value-comparison]
pass
if __name__ == "__main__": # correct
pass

View File

@ -25,22 +25,7 @@ avoid-escape = true
max-complexity = 10
[tool.ruff.pep8-naming]
ignore-names = [
"setUp",
"tearDown",
"setUpClass",
"tearDownClass",
"setUpModule",
"tearDownModule",
"asyncSetUp",
"asyncTearDown",
"setUpTestData",
"failureException",
"longMessage",
"maxDiff",
]
classmethod-decorators = ["classmethod", "pydantic.validator"]
staticmethod-decorators = ["staticmethod"]
classmethod-decorators = ["pydantic.validator"]
[tool.ruff.flake8-tidy-imports]
ban-relative-imports = "parents"

View File

@ -0,0 +1,292 @@
use std::path::Path;
use nohash_hasher::IntMap;
use rustc_hash::FxHashMap;
use rustpython_parser::ast::{Expr, Stmt};
use smallvec::smallvec;
use ruff_python::typing::TYPING_EXTENSIONS;
use crate::ast::helpers::{collect_call_path, from_relative_import, Exceptions};
use crate::ast::types::{Binding, BindingKind, CallPath, ExecutionContext, RefEquality, Scope};
use crate::resolver::is_interface_definition_path;
use crate::visibility::{module_visibility, Modifier, VisibleScope};
#[allow(clippy::struct_excessive_bools)]
pub struct Context<'a> {
pub typing_modules: &'a [String],
pub module_path: Option<Vec<String>>,
// Retain all scopes and parent nodes, along with a stack of indexes to track which are active
// at various points in time.
pub parents: Vec<RefEquality<'a, Stmt>>,
pub depths: FxHashMap<RefEquality<'a, Stmt>, usize>,
pub child_to_parent: FxHashMap<RefEquality<'a, Stmt>, RefEquality<'a, Stmt>>,
// A stack of all bindings created in any scope, at any point in execution.
pub bindings: Vec<Binding<'a>>,
// Map from binding index to indexes of bindings that redefine it in other scopes.
pub redefinitions: IntMap<usize, Vec<usize>>,
pub exprs: Vec<RefEquality<'a, Expr>>,
pub scopes: Vec<Scope<'a>>,
pub scope_stack: Vec<usize>,
pub dead_scopes: Vec<(usize, Vec<usize>)>,
// Body iteration; used to peek at siblings.
pub body: &'a [Stmt],
pub body_index: usize,
// Internal, derivative state.
pub visible_scope: VisibleScope,
pub in_annotation: bool,
pub in_type_definition: bool,
pub in_deferred_string_type_definition: bool,
pub in_deferred_type_definition: bool,
pub in_exception_handler: bool,
pub in_literal: bool,
pub in_subscript: bool,
pub in_type_checking_block: bool,
pub seen_import_boundary: bool,
pub futures_allowed: bool,
pub annotations_future_enabled: bool,
pub handled_exceptions: Vec<Exceptions>,
}
impl<'a> Context<'a> {
pub fn new(
typing_modules: &'a [String],
path: &'a Path,
module_path: Option<Vec<String>>,
) -> Self {
Self {
typing_modules,
module_path,
parents: Vec::default(),
depths: FxHashMap::default(),
child_to_parent: FxHashMap::default(),
bindings: Vec::default(),
redefinitions: IntMap::default(),
exprs: Vec::default(),
scopes: Vec::default(),
scope_stack: Vec::default(),
dead_scopes: Vec::default(),
body: &[],
body_index: 0,
visible_scope: VisibleScope {
modifier: Modifier::Module,
visibility: module_visibility(path),
},
in_annotation: false,
in_type_definition: false,
in_deferred_string_type_definition: false,
in_deferred_type_definition: false,
in_exception_handler: false,
in_literal: false,
in_subscript: false,
in_type_checking_block: false,
seen_import_boundary: false,
futures_allowed: true,
annotations_future_enabled: is_interface_definition_path(path),
handled_exceptions: Vec::default(),
}
}
/// Return `true` if the `Expr` is a reference to `typing.${target}`.
pub fn match_typing_expr(&self, expr: &Expr, target: &str) -> bool {
self.resolve_call_path(expr).map_or(false, |call_path| {
self.match_typing_call_path(&call_path, target)
})
}
/// Return `true` if the call path is a reference to `typing.${target}`.
pub fn match_typing_call_path(&self, call_path: &CallPath, target: &str) -> bool {
if call_path.as_slice() == ["typing", target] {
return true;
}
if TYPING_EXTENSIONS.contains(target) {
if call_path.as_slice() == ["typing_extensions", target] {
return true;
}
}
if self.typing_modules.iter().any(|module| {
let mut module: CallPath = module.split('.').collect();
module.push(target);
*call_path == module
}) {
return true;
}
false
}
/// Return the current `Binding` for a given `name`.
pub fn find_binding(&self, member: &str) -> Option<&Binding> {
self.current_scopes()
.find_map(|scope| scope.bindings.get(member))
.map(|index| &self.bindings[*index])
}
/// Return `true` if `member` is bound as a builtin.
pub fn is_builtin(&self, member: &str) -> bool {
self.find_binding(member)
.map_or(false, |binding| binding.kind.is_builtin())
}
/// Resolves the call path, e.g. if you have a file
///
/// ```python
/// from sys import version_info as python_version
/// print(python_version)
/// ```
///
/// then `python_version` from the print statement will resolve to `sys.version_info`.
pub fn resolve_call_path<'b>(&'a self, value: &'b Expr) -> Option<CallPath<'a>>
where
'b: 'a,
{
let call_path = collect_call_path(value);
let Some(head) = call_path.first() else {
return None;
};
let Some(binding) = self.find_binding(head) else {
return None;
};
match &binding.kind {
BindingKind::Importation(.., name) | BindingKind::SubmoduleImportation(name, ..) => {
if name.starts_with('.') {
if let Some(module) = &self.module_path {
let mut source_path = from_relative_import(module, name);
source_path.extend(call_path.into_iter().skip(1));
Some(source_path)
} else {
None
}
} else {
let mut source_path: CallPath = name.split('.').collect();
source_path.extend(call_path.into_iter().skip(1));
Some(source_path)
}
}
BindingKind::FromImportation(.., name) => {
if name.starts_with('.') {
if let Some(module) = &self.module_path {
let mut source_path = from_relative_import(module, name);
source_path.extend(call_path.into_iter().skip(1));
Some(source_path)
} else {
None
}
} else {
let mut source_path: CallPath = name.split('.').collect();
source_path.extend(call_path.into_iter().skip(1));
Some(source_path)
}
}
BindingKind::Builtin => {
let mut source_path: CallPath = smallvec![];
source_path.push("");
source_path.extend(call_path);
Some(source_path)
}
_ => None,
}
}
pub fn push_parent(&mut self, parent: &'a Stmt) {
let num_existing = self.parents.len();
self.parents.push(RefEquality(parent));
self.depths
.insert(self.parents[num_existing].clone(), num_existing);
if num_existing > 0 {
self.child_to_parent.insert(
self.parents[num_existing].clone(),
self.parents[num_existing - 1].clone(),
);
}
}
pub fn pop_parent(&mut self) {
self.parents.pop().expect("Attempted to pop without parent");
}
pub fn push_expr(&mut self, expr: &'a Expr) {
self.exprs.push(RefEquality(expr));
}
pub fn pop_expr(&mut self) {
self.exprs
.pop()
.expect("Attempted to pop without expression");
}
pub fn push_scope(&mut self, scope: Scope<'a>) {
self.scope_stack.push(self.scopes.len());
self.scopes.push(scope);
}
pub fn pop_scope(&mut self) {
self.dead_scopes.push((
self.scope_stack
.pop()
.expect("Attempted to pop without scope"),
self.scope_stack.clone(),
));
}
/// Return the current `Stmt`.
pub fn current_stmt(&self) -> &RefEquality<'a, Stmt> {
self.parents.iter().rev().next().expect("No parent found")
}
/// Return the parent `Stmt` of the current `Stmt`, if any.
pub fn current_stmt_parent(&self) -> Option<&RefEquality<'a, Stmt>> {
self.parents.iter().rev().nth(1)
}
/// Return the parent `Expr` of the current `Expr`.
pub fn current_expr_parent(&self) -> Option<&RefEquality<'a, Expr>> {
self.exprs.iter().rev().nth(1)
}
/// Return the grandparent `Expr` of the current `Expr`.
pub fn current_expr_grandparent(&self) -> Option<&RefEquality<'a, Expr>> {
self.exprs.iter().rev().nth(2)
}
/// Return the `Stmt` that immediately follows the current `Stmt`, if any.
pub fn current_sibling_stmt(&self) -> Option<&'a Stmt> {
self.body.get(self.body_index + 1)
}
pub fn current_scope(&self) -> &Scope {
&self.scopes[*(self.scope_stack.last().expect("No current scope found"))]
}
pub fn current_scope_parent(&self) -> Option<&Scope> {
self.scope_stack
.iter()
.rev()
.nth(1)
.map(|index| &self.scopes[*index])
}
pub fn current_scopes(&self) -> impl Iterator<Item = &Scope> {
self.scope_stack
.iter()
.rev()
.map(|index| &self.scopes[*index])
}
pub const fn in_exception_handler(&self) -> bool {
self.in_exception_handler
}
pub const fn execution_context(&self) -> ExecutionContext {
if self.in_type_checking_block
|| self.in_annotation
|| self.in_deferred_string_type_definition
{
ExecutionContext::Typing
} else {
ExecutionContext::Runtime
}
}
}

View File

@ -1,8 +1,8 @@
use rustpython_parser::ast::Expr;
use crate::ast::context::Context;
use crate::ast::helpers::{map_callable, to_call_path};
use crate::ast::types::{Scope, ScopeKind};
use crate::checkers::ast::Checker;
const CLASS_METHODS: [&str; 3] = ["__new__", "__init_subclass__", "__class_getitem__"];
const METACLASS_BASES: [(&str, &str); 2] = [("", "type"), ("abc", "ABCMeta")];
@ -16,7 +16,7 @@ pub enum FunctionType {
/// Classify a function based on its scope, name, and decorators.
pub fn classify(
checker: &Checker,
ctx: &Context,
scope: &Scope,
name: &str,
decorator_list: &[Expr],
@ -29,12 +29,12 @@ pub fn classify(
if decorator_list.iter().any(|expr| {
// The method is decorated with a static method decorator (like
// `@staticmethod`).
checker
.resolve_call_path(map_callable(expr))
ctx.resolve_call_path(map_callable(expr))
.map_or(false, |call_path| {
staticmethod_decorators
.iter()
.any(|decorator| call_path == to_call_path(decorator))
call_path.as_slice() == ["", "staticmethod"]
|| staticmethod_decorators
.iter()
.any(|decorator| call_path == to_call_path(decorator))
})
}) {
FunctionType::StaticMethod
@ -42,7 +42,7 @@ pub fn classify(
// Special-case class method, like `__new__`.
|| scope.bases.iter().any(|expr| {
// The class itself extends a known metaclass, so all methods are class methods.
checker.resolve_call_path(map_callable(expr)).map_or(false, |call_path| {
ctx.resolve_call_path(map_callable(expr)).map_or(false, |call_path| {
METACLASS_BASES
.iter()
.any(|(module, member)| call_path.as_slice() == [*module, *member])
@ -50,7 +50,8 @@ pub fn classify(
})
|| decorator_list.iter().any(|expr| {
// The method is decorated with a class method decorator (like `@classmethod`).
checker.resolve_call_path(map_callable(expr)).map_or(false, |call_path| {
ctx.resolve_call_path(map_callable(expr)).map_or(false, |call_path| {
call_path.as_slice() == ["", "classmethod"] ||
classmethod_decorators
.iter()
.any(|decorator| call_path == to_call_path(decorator))

View File

@ -13,10 +13,10 @@ use rustpython_parser::ast::{
use rustpython_parser::{lexer, Mode, StringKind, Tok};
use smallvec::{smallvec, SmallVec};
use crate::ast::context::Context;
use crate::ast::types::{Binding, BindingKind, CallPath, Range};
use crate::ast::visitor;
use crate::ast::visitor::Visitor;
use crate::checkers::ast::Checker;
use crate::source_code::{Generator, Indexer, Locator, Stylist};
/// Create an `Expr` with default location from an `ExprKind`.
@ -99,17 +99,16 @@ pub fn format_call_path(call_path: &[&str]) -> String {
}
/// Return `true` if the `Expr` contains a reference to `${module}.${target}`.
pub fn contains_call_path(checker: &Checker, expr: &Expr, target: &[&str]) -> bool {
pub fn contains_call_path(ctx: &Context, expr: &Expr, target: &[&str]) -> bool {
any_over_expr(expr, &|expr| {
checker
.resolve_call_path(expr)
ctx.resolve_call_path(expr)
.map_or(false, |call_path| call_path.as_slice() == target)
})
}
/// Return `true` if the `Expr` contains an expression that appears to include a
/// side-effect (like a function call).
pub fn contains_effect(checker: &Checker, expr: &Expr) -> bool {
pub fn contains_effect(ctx: &Context, expr: &Expr) -> bool {
any_over_expr(expr, &|expr| {
// Accept empty initializers.
if let ExprKind::Call {
@ -125,7 +124,7 @@ pub fn contains_effect(checker: &Checker, expr: &Expr) -> bool {
|| id == "tuple"
|| id == "dict"
|| id == "frozenset")
&& checker.is_builtin(id);
&& ctx.is_builtin(id);
return !is_empty_initializer;
}
}
@ -669,10 +668,10 @@ pub fn has_comments_in(range: Range, locator: &Locator) -> bool {
}
/// Return `true` if the body uses `locals()`, `globals()`, `vars()`, `eval()`.
pub fn uses_magic_variable_access(checker: &Checker, body: &[Stmt]) -> bool {
pub fn uses_magic_variable_access(ctx: &Context, body: &[Stmt]) -> bool {
any_over_body(body, &|expr| {
if let ExprKind::Call { func, .. } = &expr.node {
checker.resolve_call_path(func).map_or(false, |call_path| {
ctx.resolve_call_path(func).map_or(false, |call_path| {
call_path.as_slice() == ["", "locals"]
|| call_path.as_slice() == ["", "globals"]
|| call_path.as_slice() == ["", "vars"]
@ -1113,6 +1112,32 @@ pub fn first_colon_range(range: Range, locator: &Locator) -> Option<Range> {
range
}
/// Given a statement, find its "logical end".
///
/// For example: the statement could be following by a trailing semicolon, by an end-of-line
/// comment, or by any number of continuation lines (and then by a comment, and so on).
pub fn end_of_statement(stmt: &Stmt, locator: &Locator) -> Location {
let contents = locator.skip(stmt.end_location.unwrap());
// End-of-file, so just return the end of the statement.
if contents.is_empty() {
return stmt.end_location.unwrap();
}
// Otherwise, find the end of the last line that's "part of" the statement.
for (lineno, line) in contents.lines().enumerate() {
if line.ends_with('\\') {
continue;
}
return to_absolute(
Location::new(lineno + 1, line.chars().count()),
stmt.end_location.unwrap(),
);
}
unreachable!("Expected to find end-of-statement")
}
/// Return the `Range` of the first `Elif` or `Else` token in an `If` statement.
pub fn elif_else_range(stmt: &Stmt, locator: &Locator) -> Option<Range> {
let StmtKind::If { body, orelse, .. } = &stmt.node else {

View File

@ -1,6 +1,7 @@
pub mod branch_detection;
pub mod cast;
pub mod comparable;
pub mod context;
pub mod function_type;
pub mod hashable;
pub mod helpers;

View File

@ -3,11 +3,11 @@ use rustc_hash::FxHashMap;
use rustpython_parser::ast::{Cmpop, Constant, Expr, ExprKind, Located, Stmt, StmtKind};
use rustpython_parser::{lexer, Mode, Tok};
use crate::ast::context::Context;
use crate::ast::helpers::any_over_expr;
use crate::ast::types::{BindingKind, Scope};
use crate::ast::visitor;
use crate::ast::visitor::Visitor;
use crate::checkers::ast::Checker;
bitflags! {
#[derive(Default)]
@ -19,7 +19,7 @@ bitflags! {
/// Extract the names bound to a given __all__ assignment.
pub fn extract_all_names(
checker: &Checker,
ctx: &Context,
stmt: &Stmt,
scope: &Scope,
) -> (Vec<String>, AllNamesFlags) {
@ -38,7 +38,7 @@ pub fn extract_all_names(
}
fn extract_elts<'a>(
checker: &'a Checker,
ctx: &'a Context,
expr: &'a Expr,
) -> (Option<&'a Vec<Expr>>, AllNamesFlags) {
match &expr.node {
@ -60,7 +60,7 @@ pub fn extract_all_names(
} => {
// Allow `tuple()` and `list()` calls.
if keywords.is_empty() && args.len() <= 1 {
if checker.resolve_call_path(func).map_or(false, |call_path| {
if ctx.resolve_call_path(func).map_or(false, |call_path| {
call_path.as_slice() == ["", "tuple"]
|| call_path.as_slice() == ["", "list"]
}) {
@ -96,7 +96,7 @@ pub fn extract_all_names(
// Grab the existing bound __all__ values.
if let StmtKind::AugAssign { .. } = &stmt.node {
if let Some(index) = scope.bindings.get("__all__") {
if let BindingKind::Export(existing) = &checker.bindings[*index].kind {
if let BindingKind::Export(existing) = &ctx.bindings[*index].kind {
names.extend_from_slice(existing);
}
}
@ -113,7 +113,7 @@ pub fn extract_all_names(
let mut current_right = right;
loop {
// Process the right side, which should be a "real" value.
let (elts, new_flags) = extract_elts(checker, current_right);
let (elts, new_flags) = extract_elts(ctx, current_right);
flags |= new_flags;
if let Some(elts) = elts {
add_to_names(&mut names, elts, &mut flags);
@ -125,7 +125,7 @@ pub fn extract_all_names(
current_left = left;
current_right = right;
} else {
let (elts, new_flags) = extract_elts(checker, current_left);
let (elts, new_flags) = extract_elts(ctx, current_left);
flags |= new_flags;
if let Some(elts) = elts {
add_to_names(&mut names, elts, &mut flags);
@ -134,7 +134,7 @@ pub fn extract_all_names(
}
}
} else {
let (elts, new_flags) = extract_elts(checker, value);
let (elts, new_flags) = extract_elts(ctx, value);
flags |= new_flags;
if let Some(elts) = elts {
add_to_names(&mut names, elts, &mut flags);

View File

@ -0,0 +1,23 @@
use rustpython_parser::ast::{Expr, Stmt};
use crate::ast::types::RefEquality;
use crate::checkers::ast::AnnotationContext;
use crate::docstrings::definition::Definition;
use crate::visibility::{Visibility, VisibleScope};
use crate::Range;
type Context<'a> = (Vec<usize>, Vec<RefEquality<'a, Stmt>>);
/// A collection of AST nodes that are deferred for later analysis.
/// Used to, e.g., store functions, whose bodies shouldn't be analyzed until all
/// module-level definitions have been analyzed.
#[derive(Default)]
pub struct Deferred<'a> {
pub definitions: Vec<(Definition<'a>, Visibility, Context<'a>)>,
pub string_type_definitions: Vec<(Range, &'a str, AnnotationContext, Context<'a>)>,
pub type_definitions: Vec<(&'a Expr, AnnotationContext, Context<'a>)>,
pub functions: Vec<(&'a Stmt, Context<'a>, VisibleScope)>,
pub lambdas: Vec<(&'a Expr, Context<'a>)>,
pub for_loops: Vec<(&'a Stmt, Context<'a>)>,
pub assignments: Vec<Context<'a>>,
}

View File

@ -1,17 +1,20 @@
#![allow(dead_code, unused_imports, unused_variables)]
use bisection::bisect_left;
use itertools::Itertools;
use rustpython_parser::ast::Location;
use rustpython_parser::lexer::LexResult;
use crate::ast::types::Range;
use crate::registry::Diagnostic;
use crate::registry::{Diagnostic, Rule};
use crate::rules::pycodestyle::logical_lines::{iter_logical_lines, TokenFlags};
use crate::rules::pycodestyle::rules::{
extraneous_whitespace, indentation, missing_whitespace_after_keyword, space_around_operator,
whitespace_around_keywords, whitespace_around_named_parameter_equals,
whitespace_before_comment,
extraneous_whitespace, indentation, missing_whitespace, missing_whitespace_after_keyword,
missing_whitespace_around_operator, space_around_operator, whitespace_around_keywords,
whitespace_around_named_parameter_equals, whitespace_before_comment,
whitespace_before_parameters,
};
use crate::settings::Settings;
use crate::settings::{flags, Settings};
use crate::source_code::{Locator, Stylist};
/// Return the amount of indentation, expanding tabs to the next multiple of 8.
@ -40,6 +43,7 @@ pub fn check_logical_lines(
locator: &Locator,
stylist: &Stylist,
settings: &Settings,
autofix: flags::Autofix,
) -> Vec<Diagnostic> {
let mut diagnostics = vec![];
@ -147,6 +151,44 @@ pub fn check_logical_lines(
});
}
}
for (location, kind) in missing_whitespace_around_operator(&line.tokens) {
if settings.rules.enabled(kind.rule()) {
diagnostics.push(Diagnostic {
kind,
location,
end_location: location,
fix: None,
parent: None,
});
}
}
#[cfg(feature = "logical_lines")]
let should_fix = autofix.into() && settings.rules.should_fix(&Rule::MissingWhitespace);
#[cfg(not(feature = "logical_lines"))]
let should_fix = false;
for diagnostic in missing_whitespace(&line.text, start_loc.row(), should_fix) {
if settings.rules.enabled(diagnostic.kind.rule()) {
diagnostics.push(diagnostic);
}
}
}
if line.flags.contains(TokenFlags::BRACKET) {
#[cfg(feature = "logical_lines")]
let should_fix =
autofix.into() && settings.rules.should_fix(&Rule::WhitespaceBeforeParameters);
#[cfg(not(feature = "logical_lines"))]
let should_fix = false;
for diagnostic in whitespace_before_parameters(&line.tokens, should_fix) {
if settings.rules.enabled(diagnostic.kind.rule()) {
diagnostics.push(diagnostic);
}
}
}
for (index, kind) in indentation(
@ -261,7 +303,7 @@ f()"#;
.into_iter()
.map(|line| line.text)
.collect();
let expected = vec!["def f():", "\"xxx\"", "", "x = 1", "f()"];
let expected = vec!["def f():", "\"xxxxxxxxxxxxxxxxxxxx\"", "", "x = 1", "f()"];
assert_eq!(actual, expected);
}
}

View File

@ -7,8 +7,8 @@ use crate::lex::docstring_detection::StateMachine;
use crate::registry::{Diagnostic, Rule};
use crate::rules::ruff::rules::Context;
use crate::rules::{
eradicate, flake8_commas, flake8_implicit_str_concat, flake8_quotes, pycodestyle, pyupgrade,
ruff,
eradicate, flake8_commas, flake8_implicit_str_concat, flake8_pyi, flake8_quotes, pycodestyle,
pyupgrade, ruff,
};
use crate::settings::{flags, Settings};
use crate::source_code::Locator;
@ -18,6 +18,7 @@ pub fn check_tokens(
tokens: &[LexResult],
settings: &Settings,
autofix: flags::Autofix,
is_interface_definition: bool,
) -> Vec<Diagnostic> {
let mut diagnostics: Vec<Diagnostic> = vec![];
@ -55,6 +56,7 @@ pub fn check_tokens(
.enabled(&Rule::TrailingCommaOnBareTupleProhibited)
|| settings.rules.enabled(&Rule::TrailingCommaProhibited);
let enforce_extraneous_parenthesis = settings.rules.enabled(&Rule::ExtraneousParentheses);
let enforce_type_comment_in_stub = settings.rules.enabled(&Rule::TypeCommentInStub);
// RUF001, RUF002, RUF003
if enforce_ambiguous_unicode_character {
@ -161,5 +163,10 @@ pub fn check_tokens(
);
}
// PYI033
if enforce_type_comment_in_stub && is_interface_definition {
diagnostics.extend(flake8_pyi::rules::type_comment_in_stub(tokens));
}
diagnostics
}

View File

@ -29,6 +29,8 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<Rule> {
#[cfg(feature = "logical_lines")]
(Pycodestyle, "E203") => Rule::WhitespaceBeforePunctuation,
#[cfg(feature = "logical_lines")]
(Pycodestyle, "E211") => Rule::WhitespaceBeforeParameters,
#[cfg(feature = "logical_lines")]
(Pycodestyle, "E221") => Rule::MultipleSpacesBeforeOperator,
#[cfg(feature = "logical_lines")]
(Pycodestyle, "E222") => Rule::MultipleSpacesAfterOperator,
@ -37,6 +39,16 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<Rule> {
#[cfg(feature = "logical_lines")]
(Pycodestyle, "E224") => Rule::TabAfterOperator,
#[cfg(feature = "logical_lines")]
(Pycodestyle, "E225") => Rule::MissingWhitespaceAroundOperator,
#[cfg(feature = "logical_lines")]
(Pycodestyle, "E226") => Rule::MissingWhitespaceAroundArithmeticOperator,
#[cfg(feature = "logical_lines")]
(Pycodestyle, "E227") => Rule::MissingWhitespaceAroundBitwiseOrShiftOperator,
#[cfg(feature = "logical_lines")]
(Pycodestyle, "E228") => Rule::MissingWhitespaceAroundModuloOperator,
#[cfg(feature = "logical_lines")]
(Pycodestyle, "E231") => Rule::MissingWhitespace,
#[cfg(feature = "logical_lines")]
(Pycodestyle, "E251") => Rule::UnexpectedSpacesAroundKeywordParameterEquals,
#[cfg(feature = "logical_lines")]
(Pycodestyle, "E252") => Rule::MissingWhitespaceAroundParameterEquals,
@ -501,6 +513,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<Rule> {
(Flake8Pyi, "011") => Rule::TypedArgumentSimpleDefaults,
(Flake8Pyi, "014") => Rule::ArgumentSimpleDefaults,
(Flake8Pyi, "021") => Rule::DocstringInStub,
(Flake8Pyi, "033") => Rule::TypeCommentInStub,
// flake8-pytest-style
(Flake8PytestStyle, "001") => Rule::IncorrectFixtureParenthesesStyle,

View File

@ -1,70 +1,36 @@
//! Abstractions for Google-style docstrings.
use once_cell::sync::Lazy;
use rustc_hash::FxHashSet;
use crate::docstrings::sections::SectionKind;
pub(crate) static GOOGLE_SECTION_NAMES: Lazy<FxHashSet<&'static str>> = Lazy::new(|| {
FxHashSet::from_iter([
"Args",
"Arguments",
"Attention",
"Attributes",
"Caution",
"Danger",
"Error",
"Example",
"Examples",
"Hint",
"Important",
"Keyword Args",
"Keyword Arguments",
"Methods",
"Note",
"Notes",
"Return",
"Returns",
"Raises",
"References",
"See Also",
"Tip",
"Todo",
"Warning",
"Warnings",
"Warns",
"Yield",
"Yields",
])
});
pub(crate) static LOWERCASE_GOOGLE_SECTION_NAMES: Lazy<FxHashSet<&'static str>> = Lazy::new(|| {
FxHashSet::from_iter([
"args",
"arguments",
"attention",
"attributes",
"caution",
"danger",
"error",
"example",
"examples",
"hint",
"important",
"keyword args",
"keyword arguments",
"methods",
"note",
"notes",
"return",
"returns",
"raises",
"references",
"see also",
"tip",
"todo",
"warning",
"warnings",
"warns",
"yield",
"yields",
])
});
pub(crate) static GOOGLE_SECTIONS: &[SectionKind] = &[
SectionKind::Attributes,
SectionKind::Examples,
SectionKind::Methods,
SectionKind::Notes,
SectionKind::Raises,
SectionKind::References,
SectionKind::Returns,
SectionKind::SeeAlso,
SectionKind::Yields,
// Google-only
SectionKind::Args,
SectionKind::Arguments,
SectionKind::Attention,
SectionKind::Caution,
SectionKind::Danger,
SectionKind::Error,
SectionKind::Example,
SectionKind::Hint,
SectionKind::Important,
SectionKind::KeywordArgs,
SectionKind::KeywordArguments,
SectionKind::Note,
SectionKind::Notes,
SectionKind::Return,
SectionKind::Tip,
SectionKind::Todo,
SectionKind::Warning,
SectionKind::Warnings,
SectionKind::Warns,
SectionKind::Yield,
];

View File

@ -1,40 +1,20 @@
//! Abstractions for NumPy-style docstrings.
use once_cell::sync::Lazy;
use rustc_hash::FxHashSet;
use crate::docstrings::sections::SectionKind;
pub(crate) static LOWERCASE_NUMPY_SECTION_NAMES: Lazy<FxHashSet<&'static str>> = Lazy::new(|| {
FxHashSet::from_iter([
"short summary",
"extended summary",
"parameters",
"returns",
"yields",
"other parameters",
"raises",
"see also",
"notes",
"references",
"examples",
"attributes",
"methods",
])
});
pub(crate) static NUMPY_SECTION_NAMES: Lazy<FxHashSet<&'static str>> = Lazy::new(|| {
FxHashSet::from_iter([
"Short Summary",
"Extended Summary",
"Parameters",
"Returns",
"Yields",
"Other Parameters",
"Raises",
"See Also",
"Notes",
"References",
"Examples",
"Attributes",
"Methods",
])
});
pub(crate) static NUMPY_SECTIONS: &[SectionKind] = &[
SectionKind::Attributes,
SectionKind::Examples,
SectionKind::Methods,
SectionKind::Notes,
SectionKind::Raises,
SectionKind::References,
SectionKind::Returns,
SectionKind::SeeAlso,
SectionKind::Yields,
// NumPy-only
SectionKind::ExtendedSummary,
SectionKind::OtherParameters,
SectionKind::Parameters,
SectionKind::ShortSummary,
];

View File

@ -1,8 +1,126 @@
use strum_macros::EnumIter;
use crate::ast::whitespace;
use crate::docstrings::styles::SectionStyle;
#[derive(EnumIter, PartialEq, Eq, Debug, Clone, Copy)]
pub enum SectionKind {
Args,
Arguments,
Attention,
Attributes,
Caution,
Danger,
Error,
Example,
Examples,
ExtendedSummary,
Hint,
Important,
KeywordArgs,
KeywordArguments,
Methods,
Note,
Notes,
OtherParameters,
Parameters,
Raises,
References,
Return,
Returns,
SeeAlso,
ShortSummary,
Tip,
Todo,
Warning,
Warnings,
Warns,
Yield,
Yields,
}
impl SectionKind {
pub fn from_str(s: &str) -> Option<Self> {
match s.to_ascii_lowercase().as_str() {
"args" => Some(Self::Args),
"arguments" => Some(Self::Arguments),
"attention" => Some(Self::Attention),
"attributes" => Some(Self::Attributes),
"caution" => Some(Self::Caution),
"danger" => Some(Self::Danger),
"error" => Some(Self::Error),
"example" => Some(Self::Example),
"examples" => Some(Self::Examples),
"extended summary" => Some(Self::ExtendedSummary),
"hint" => Some(Self::Hint),
"important" => Some(Self::Important),
"keyword args" => Some(Self::KeywordArgs),
"keyword arguments" => Some(Self::KeywordArguments),
"methods" => Some(Self::Methods),
"note" => Some(Self::Note),
"notes" => Some(Self::Notes),
"other parameters" => Some(Self::OtherParameters),
"parameters" => Some(Self::Parameters),
"raises" => Some(Self::Raises),
"references" => Some(Self::References),
"return" => Some(Self::Return),
"returns" => Some(Self::Returns),
"see also" => Some(Self::SeeAlso),
"short summary" => Some(Self::ShortSummary),
"tip" => Some(Self::Tip),
"todo" => Some(Self::Todo),
"warning" => Some(Self::Warning),
"warnings" => Some(Self::Warnings),
"warns" => Some(Self::Warns),
"yield" => Some(Self::Yield),
"yields" => Some(Self::Yields),
_ => None,
}
}
pub fn as_str(self) -> &'static str {
match self {
Self::Args => "Args",
Self::Arguments => "Arguments",
Self::Attention => "Attention",
Self::Attributes => "Attributes",
Self::Caution => "Caution",
Self::Danger => "Danger",
Self::Error => "Error",
Self::Example => "Example",
Self::Examples => "Examples",
Self::ExtendedSummary => "Extended Summary",
Self::Hint => "Hint",
Self::Important => "Important",
Self::KeywordArgs => "Keyword Args",
Self::KeywordArguments => "Keyword Arguments",
Self::Methods => "Methods",
Self::Note => "Note",
Self::Notes => "Notes",
Self::OtherParameters => "Other Parameters",
Self::Parameters => "Parameters",
Self::Raises => "Raises",
Self::References => "References",
Self::Return => "Return",
Self::Returns => "Returns",
Self::SeeAlso => "See Also",
Self::ShortSummary => "Short Summary",
Self::Tip => "Tip",
Self::Todo => "Todo",
Self::Warning => "Warning",
Self::Warnings => "Warnings",
Self::Warns => "Warns",
Self::Yield => "Yield",
Self::Yields => "Yields",
}
}
}
#[derive(Debug)]
pub(crate) struct SectionContext<'a> {
/// The "kind" of the section, e.g. "SectionKind::Args" or "SectionKind::Returns".
pub(crate) kind: SectionKind,
/// The name of the section as it appears in the docstring, e.g. "Args" or "Returns".
pub(crate) section_name: &'a str,
pub(crate) previous_line: &'a str,
pub(crate) line: &'a str,
@ -11,10 +129,13 @@ pub(crate) struct SectionContext<'a> {
pub(crate) original_index: usize,
}
fn suspected_as_section(line: &str, style: &SectionStyle) -> bool {
style
.lowercase_section_names()
.contains(&whitespace::leading_words(line).to_lowercase().as_str())
fn suspected_as_section(line: &str, style: &SectionStyle) -> Option<SectionKind> {
if let Some(kind) = SectionKind::from_str(whitespace::leading_words(line)) {
if style.sections().contains(&kind) {
return Some(kind);
}
}
None
}
/// Check if the suspected context is really a section header.
@ -49,21 +170,15 @@ pub(crate) fn section_contexts<'a>(
lines: &'a [&'a str],
style: &SectionStyle,
) -> Vec<SectionContext<'a>> {
let suspected_section_indices: Vec<usize> = lines
let mut contexts = vec![];
for (kind, lineno) in lines
.iter()
.enumerate()
.filter_map(|(lineno, line)| {
if lineno > 0 && suspected_as_section(line, style) {
Some(lineno)
} else {
None
}
})
.collect();
let mut contexts = vec![];
for lineno in suspected_section_indices {
.skip(1)
.filter_map(|(lineno, line)| suspected_as_section(line, style).map(|kind| (kind, lineno)))
{
let context = SectionContext {
kind,
section_name: whitespace::leading_words(lines[lineno]),
previous_line: lines[lineno - 1],
line: lines[lineno],
@ -76,11 +191,12 @@ pub(crate) fn section_contexts<'a>(
}
}
let mut truncated_contexts = vec![];
let mut truncated_contexts = Vec::with_capacity(contexts.len());
let mut end: Option<usize> = None;
for context in contexts.into_iter().rev() {
let next_end = context.original_index;
truncated_contexts.push(SectionContext {
kind: context.kind,
section_name: context.section_name,
previous_line: context.previous_line,
line: context.line,

View File

@ -1,8 +1,6 @@
use once_cell::sync::Lazy;
use rustc_hash::FxHashSet;
use crate::docstrings::google::{GOOGLE_SECTION_NAMES, LOWERCASE_GOOGLE_SECTION_NAMES};
use crate::docstrings::numpy::{LOWERCASE_NUMPY_SECTION_NAMES, NUMPY_SECTION_NAMES};
use crate::docstrings::google::GOOGLE_SECTIONS;
use crate::docstrings::numpy::NUMPY_SECTIONS;
use crate::docstrings::sections::SectionKind;
pub(crate) enum SectionStyle {
Numpy,
@ -10,17 +8,10 @@ pub(crate) enum SectionStyle {
}
impl SectionStyle {
pub(crate) fn section_names(&self) -> &Lazy<FxHashSet<&'static str>> {
pub(crate) fn sections(&self) -> &[SectionKind] {
match self {
SectionStyle::Numpy => &NUMPY_SECTION_NAMES,
SectionStyle::Google => &GOOGLE_SECTION_NAMES,
}
}
pub(crate) fn lowercase_section_names(&self) -> &Lazy<FxHashSet<&'static str>> {
match self {
SectionStyle::Numpy => &LOWERCASE_NUMPY_SECTION_NAMES,
SectionStyle::Google => &LOWERCASE_GOOGLE_SECTION_NAMES,
SectionStyle::Numpy => NUMPY_SECTIONS,
SectionStyle::Google => GOOGLE_SECTIONS,
}
}
}

View File

@ -577,6 +577,7 @@ mod tests {
pydocstyle: Some(pydocstyle::settings::Options {
convention: Some(Convention::Numpy),
ignore_decorators: None,
property_decorators: None,
}),
..default_options([Linter::Pydocstyle.into()])
});

View File

@ -1,13 +1,12 @@
use std::ops::Deref;
use std::path::{Path, PathBuf};
use anyhow::{anyhow, Result};
use globset::GlobMatcher;
use log::debug;
use path_absolutize::{path_dedot, Absolutize};
use rustc_hash::FxHashSet;
use crate::registry::Rule;
use crate::settings::hashable::{HashableGlobMatcher, HashableHashSet};
/// Extract the absolute path and basename (as strings) from a Path.
pub fn extract_path_names(path: &Path) -> Result<(&str, &str)> {
@ -25,11 +24,7 @@ pub fn extract_path_names(path: &Path) -> Result<(&str, &str)> {
/// Create a set with codes matching the pattern/code pairs.
pub(crate) fn ignores_from_path<'a>(
path: &Path,
pattern_code_pairs: &'a [(
HashableGlobMatcher,
HashableGlobMatcher,
HashableHashSet<Rule>,
)],
pattern_code_pairs: &'a [(GlobMatcher, GlobMatcher, FxHashSet<Rule>)],
) -> FxHashSet<&'a Rule> {
let (file_path, file_basename) = extract_path_names(path).expect("Unable to parse filename");
pattern_code_pairs
@ -39,8 +34,8 @@ pub(crate) fn ignores_from_path<'a>(
debug!(
"Adding per-file ignores for {:?} due to basename match on {:?}: {:?}",
path,
basename.deref().glob().regex(),
&**codes
basename.glob().regex(),
codes
);
return Some(codes.iter());
}
@ -48,8 +43,8 @@ pub(crate) fn ignores_from_path<'a>(
debug!(
"Adding per-file ignores for {:?} due to absolute match on {:?}: {:?}",
path,
absolute.deref().glob().regex(),
&**codes
absolute.glob().regex(),
codes
);
return Some(codes.iter());
}

View File

@ -13,7 +13,6 @@ pub use violation::{AutofixKind, Availability as AutofixAvailability};
mod ast;
mod autofix;
pub mod cache;
mod checkers;
mod codes;
mod cst;

View File

@ -21,6 +21,7 @@ use crate::doc_lines::{doc_lines_from_ast, doc_lines_from_tokens};
use crate::message::{Message, Source};
use crate::noqa::{add_noqa, rule_is_ignored};
use crate::registry::{Diagnostic, Rule};
use crate::resolver::is_interface_definition_path;
use crate::rules::pycodestyle;
use crate::settings::{flags, Settings};
use crate::source_code::{Indexer, Locator, Stylist};
@ -83,7 +84,14 @@ pub fn check_path(
.iter_enabled()
.any(|rule_code| rule_code.lint_source().is_tokens())
{
diagnostics.extend(check_tokens(locator, &tokens, settings, autofix));
let is_interface_definition = is_interface_definition_path(path);
diagnostics.extend(check_tokens(
locator,
&tokens,
settings,
autofix,
is_interface_definition,
));
}
// Run the filesystem-based rules.
@ -101,7 +109,13 @@ pub fn check_path(
.iter_enabled()
.any(|rule_code| rule_code.lint_source().is_logical_lines())
{
diagnostics.extend(check_logical_lines(&tokens, locator, stylist, settings));
diagnostics.extend(check_logical_lines(
&tokens,
locator,
stylist,
settings,
flags::Autofix::Enabled,
));
}
// Run the AST-based rules.

View File

@ -53,16 +53,28 @@ ruff_macros::register_rules!(
#[cfg(feature = "logical_lines")]
rules::pycodestyle::rules::MultipleSpacesAfterKeyword,
#[cfg(feature = "logical_lines")]
rules::pycodestyle::rules::MissingWhitespace,
#[cfg(feature = "logical_lines")]
rules::pycodestyle::rules::MissingWhitespaceAfterKeyword,
#[cfg(feature = "logical_lines")]
rules::pycodestyle::rules::MultipleSpacesBeforeKeyword,
#[cfg(feature = "logical_lines")]
rules::pycodestyle::rules::MissingWhitespaceAroundOperator,
#[cfg(feature = "logical_lines")]
rules::pycodestyle::rules::MissingWhitespaceAroundArithmeticOperator,
#[cfg(feature = "logical_lines")]
rules::pycodestyle::rules::MissingWhitespaceAroundBitwiseOrShiftOperator,
#[cfg(feature = "logical_lines")]
rules::pycodestyle::rules::MissingWhitespaceAroundModuloOperator,
#[cfg(feature = "logical_lines")]
rules::pycodestyle::rules::TabAfterKeyword,
#[cfg(feature = "logical_lines")]
rules::pycodestyle::rules::UnexpectedSpacesAroundKeywordParameterEquals,
#[cfg(feature = "logical_lines")]
rules::pycodestyle::rules::MissingWhitespaceAroundParameterEquals,
#[cfg(feature = "logical_lines")]
rules::pycodestyle::rules::WhitespaceBeforeParameters,
#[cfg(feature = "logical_lines")]
rules::pycodestyle::rules::TabBeforeKeyword,
rules::pycodestyle::rules::MultipleImportsOnOneLine,
rules::pycodestyle::rules::ModuleImportNotAtTopOfFile,
@ -475,6 +487,7 @@ ruff_macros::register_rules!(
rules::flake8_pyi::rules::DocstringInStub,
rules::flake8_pyi::rules::TypedArgumentSimpleDefaults,
rules::flake8_pyi::rules::ArgumentSimpleDefaults,
rules::flake8_pyi::rules::TypeCommentInStub,
// flake8-pytest-style
rules::flake8_pytest_style::rules::IncorrectFixtureParenthesesStyle,
rules::flake8_pytest_style::rules::FixturePositionalArgs,
@ -828,19 +841,26 @@ impl Rule {
| Rule::MultipleStatementsOnOneLineColon
| Rule::UselessSemicolon
| Rule::MultipleStatementsOnOneLineSemicolon
| Rule::TrailingCommaProhibited => &LintSource::Tokens,
| Rule::TrailingCommaProhibited
| Rule::TypeCommentInStub => &LintSource::Tokens,
Rule::IOError => &LintSource::Io,
Rule::UnsortedImports | Rule::MissingRequiredImport => &LintSource::Imports,
Rule::ImplicitNamespacePackage | Rule::InvalidModuleName => &LintSource::Filesystem,
#[cfg(feature = "logical_lines")]
Rule::IndentationWithInvalidMultiple
| Rule::IndentationWithInvalidMultipleComment
| Rule::MissingWhitespace
| Rule::MissingWhitespaceAfterKeyword
| Rule::MissingWhitespaceAroundArithmeticOperator
| Rule::MissingWhitespaceAroundBitwiseOrShiftOperator
| Rule::MissingWhitespaceAroundModuloOperator
| Rule::MissingWhitespaceAroundOperator
| Rule::MissingWhitespaceAroundParameterEquals
| Rule::MultipleLeadingHashesForBlockComment
| Rule::MultipleSpacesAfterKeyword
| Rule::MultipleSpacesAfterOperator
| Rule::MultipleSpacesBeforeKeyword
| Rule::MultipleSpacesBeforeOperator
| Rule::MissingWhitespaceAfterKeyword
| Rule::NoIndentedBlock
| Rule::NoIndentedBlockComment
| Rule::NoSpaceAfterBlockComment
@ -853,10 +873,10 @@ impl Rule {
| Rule::TooFewSpacesBeforeInlineComment
| Rule::UnexpectedIndentation
| Rule::UnexpectedIndentationComment
| Rule::UnexpectedSpacesAroundKeywordParameterEquals
| Rule::WhitespaceAfterOpenBracket
| Rule::WhitespaceBeforeCloseBracket
| Rule::UnexpectedSpacesAroundKeywordParameterEquals
| Rule::MissingWhitespaceAroundParameterEquals
| Rule::WhitespaceBeforeParameters
| Rule::WhitespaceBeforePunctuation => &LintSource::LogicalLines,
_ => &LintSource::Ast,
}

View File

@ -115,6 +115,7 @@ impl Violation for SysVersionSlice1Referenced {
fn is_sys(checker: &Checker, expr: &Expr, target: &str) -> bool {
checker
.ctx
.resolve_call_path(expr)
.map_or(false, |call_path| call_path.as_slice() == ["sys", target])
}
@ -306,6 +307,7 @@ pub fn compare(checker: &mut Checker, left: &Expr, ops: &[Cmpop], comparators: &
/// YTT202
pub fn name_or_attribute(checker: &mut Checker, expr: &Expr) {
if checker
.ctx
.resolve_call_path(expr)
.map_or(false, |call_path| call_path.as_slice() == ["six", "PY3"])
{

View File

@ -33,7 +33,7 @@ pub fn overloaded_name(checker: &Checker, definition: &Definition) -> Option<Str
| DefinitionKind::NestedFunction(stmt)
| DefinitionKind::Method(stmt) = definition.kind
{
if visibility::is_overload(checker, cast::decorator_list(stmt)) {
if visibility::is_overload(&checker.ctx, cast::decorator_list(stmt)) {
let (name, ..) = match_function_def(stmt);
Some(name.to_string())
} else {
@ -51,7 +51,7 @@ pub fn is_overload_impl(checker: &Checker, definition: &Definition, overloaded_n
| DefinitionKind::NestedFunction(stmt)
| DefinitionKind::Method(stmt) = definition.kind
{
if visibility::is_overload(checker, cast::decorator_list(stmt)) {
if visibility::is_overload(&checker.ctx, cast::decorator_list(stmt)) {
false
} else {
let (name, ..) = match_function_def(stmt);

View File

@ -441,7 +441,7 @@ fn check_dynamically_typed<F>(
) where
F: FnOnce() -> String,
{
if checker.match_typing_expr(annotation, "Any") {
if checker.ctx.match_typing_expr(annotation, "Any") {
diagnostics.push(Diagnostic::new(
AnyType { name: func() },
Range::from_located(annotation),
@ -482,7 +482,8 @@ pub fn definition(
.skip(
// If this is a non-static method, skip `cls` or `self`.
usize::from(
is_method && !visibility::is_staticmethod(checker, cast::decorator_list(stmt)),
is_method
&& !visibility::is_staticmethod(&checker.ctx, cast::decorator_list(stmt)),
),
)
{
@ -580,10 +581,10 @@ pub fn definition(
}
// ANN101, ANN102
if is_method && !visibility::is_staticmethod(checker, cast::decorator_list(stmt)) {
if is_method && !visibility::is_staticmethod(&checker.ctx, cast::decorator_list(stmt)) {
if let Some(arg) = args.posonlyargs.first().or_else(|| args.args.first()) {
if arg.node.annotation.is_none() {
if visibility::is_classmethod(checker, cast::decorator_list(stmt)) {
if visibility::is_classmethod(&checker.ctx, cast::decorator_list(stmt)) {
if checker.settings.rules.enabled(&Rule::MissingTypeCls) {
diagnostics.push(Diagnostic::new(
MissingTypeCls {
@ -619,7 +620,7 @@ pub fn definition(
// (explicitly or implicitly).
checker.settings.flake8_annotations.suppress_none_returning && is_none_returning(body)
) {
if is_method && visibility::is_classmethod(checker, cast::decorator_list(stmt)) {
if is_method && visibility::is_classmethod(&checker.ctx, cast::decorator_list(stmt)) {
if checker
.settings
.rules
@ -632,7 +633,8 @@ pub fn definition(
helpers::identifier_range(stmt, checker.locator),
));
}
} else if is_method && visibility::is_staticmethod(checker, cast::decorator_list(stmt))
} else if is_method
&& visibility::is_staticmethod(&checker.ctx, cast::decorator_list(stmt))
{
if checker
.settings

View File

@ -1,5 +1,6 @@
//! Settings for the `flake-annotations` plugin.
use ruff_macros::CacheKey;
use ruff_macros::ConfigurationOptions;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@ -60,7 +61,7 @@ pub struct Options {
pub ignore_fully_untyped: Option<bool>,
}
#[derive(Debug, Default, Hash)]
#[derive(Debug, Default, CacheKey)]
#[allow(clippy::struct_excessive_bools)]
pub struct Settings {
pub mypy_init_return: bool,

View File

@ -26,16 +26,22 @@ pub fn is_untyped_exception(type_: Option<&Expr>, checker: &Checker) -> bool {
type_.map_or(true, |type_| {
if let ExprKind::Tuple { elts, .. } = &type_.node {
elts.iter().any(|type_| {
checker.resolve_call_path(type_).map_or(false, |call_path| {
checker
.ctx
.resolve_call_path(type_)
.map_or(false, |call_path| {
call_path.as_slice() == ["", "Exception"]
|| call_path.as_slice() == ["", "BaseException"]
})
})
} else {
checker
.ctx
.resolve_call_path(type_)
.map_or(false, |call_path| {
call_path.as_slice() == ["", "Exception"]
|| call_path.as_slice() == ["", "BaseException"]
})
})
} else {
checker.resolve_call_path(type_).map_or(false, |call_path| {
call_path.as_slice() == ["", "Exception"]
|| call_path.as_slice() == ["", "BaseException"]
})
}
})
}

View File

@ -103,6 +103,7 @@ pub fn bad_file_permissions(
keywords: &[Keyword],
) {
if checker
.ctx
.resolve_call_path(func)
.map_or(false, |call_path| call_path.as_slice() == ["os", "chmod"])
{

View File

@ -60,7 +60,7 @@ fn unparse_string_format_expression(checker: &mut Checker, expr: &Expr) -> Optio
op: Operator::Add | Operator::Mod,
..
} => {
let Some(parent) = checker.current_expr_parent() else {
let Some(parent) = checker.ctx.current_expr_parent() else {
if any_over_expr(expr, &has_string_literal) {
return Some(unparse_expr(expr, checker.stylist));
}

View File

@ -48,7 +48,7 @@ pub fn hashlib_insecure_hash_functions(
args: &[Expr],
keywords: &[Keyword],
) {
if let Some(hashlib_call) = checker.resolve_call_path(func).and_then(|call_path| {
if let Some(hashlib_call) = checker.ctx.resolve_call_path(func).and_then(|call_path| {
if call_path.as_slice() == ["hashlib", "new"] {
Some(HashlibCall::New)
} else {

View File

@ -37,9 +37,13 @@ pub fn jinja2_autoescape_false(
args: &[Expr],
keywords: &[Keyword],
) {
if checker.resolve_call_path(func).map_or(false, |call_path| {
call_path.as_slice() == ["jinja2", "Environment"]
}) {
if checker
.ctx
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["jinja2", "Environment"]
})
{
let call_args = SimpleCallArgs::new(args, keywords);
if let Some(autoescape_arg) = call_args.get_argument("autoescape", None) {

View File

@ -24,9 +24,13 @@ pub fn logging_config_insecure_listen(
args: &[Expr],
keywords: &[Keyword],
) {
if checker.resolve_call_path(func).map_or(false, |call_path| {
call_path.as_slice() == ["logging", "config", "listen"]
}) {
if checker
.ctx
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["logging", "config", "listen"]
})
{
let call_args = SimpleCallArgs::new(args, keywords);
if call_args.get_argument("verify", None).is_none() {

View File

@ -44,7 +44,7 @@ pub fn request_with_no_cert_validation(
args: &[Expr],
keywords: &[Keyword],
) {
if let Some(target) = checker.resolve_call_path(func).and_then(|call_path| {
if let Some(target) = checker.ctx.resolve_call_path(func).and_then(|call_path| {
if call_path.len() == 2 {
if call_path[0] == "requests" && REQUESTS_HTTP_VERBS.contains(&call_path[1]) {
return Some("requests");

View File

@ -34,11 +34,15 @@ pub fn request_without_timeout(
args: &[Expr],
keywords: &[Keyword],
) {
if checker.resolve_call_path(func).map_or(false, |call_path| {
HTTP_VERBS
.iter()
.any(|func_name| call_path.as_slice() == ["requests", func_name])
}) {
if checker
.ctx
.resolve_call_path(func)
.map_or(false, |call_path| {
HTTP_VERBS
.iter()
.any(|func_name| call_path.as_slice() == ["requests", func_name])
})
{
let call_args = SimpleCallArgs::new(args, keywords);
if let Some(timeout_arg) = call_args.get_argument("timeout", None) {
if let Some(timeout) = match &timeout_arg.node {

View File

@ -25,9 +25,13 @@ pub fn snmp_insecure_version(
args: &[Expr],
keywords: &[Keyword],
) {
if checker.resolve_call_path(func).map_or(false, |call_path| {
call_path.as_slice() == ["pysnmp", "hlapi", "CommunityData"]
}) {
if checker
.ctx
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["pysnmp", "hlapi", "CommunityData"]
})
{
let call_args = SimpleCallArgs::new(args, keywords);
if let Some(mp_model_arg) = call_args.get_argument("mpModel", None) {
if let ExprKind::Constant {

View File

@ -27,9 +27,13 @@ pub fn snmp_weak_cryptography(
args: &[Expr],
keywords: &[Keyword],
) {
if checker.resolve_call_path(func).map_or(false, |call_path| {
call_path.as_slice() == ["pysnmp", "hlapi", "UsmUserData"]
}) {
if checker
.ctx
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["pysnmp", "hlapi", "UsmUserData"]
})
{
let call_args = SimpleCallArgs::new(args, keywords);
if call_args.len() < 3 {
checker.diagnostics.push(Diagnostic::new(

View File

@ -34,12 +34,14 @@ impl Violation for UnsafeYAMLLoad {
/// S506
pub fn unsafe_yaml_load(checker: &mut Checker, func: &Expr, args: &[Expr], keywords: &[Keyword]) {
if checker
.ctx
.resolve_call_path(func)
.map_or(false, |call_path| call_path.as_slice() == ["yaml", "load"])
{
let call_args = SimpleCallArgs::new(args, keywords);
if let Some(loader_arg) = call_args.get_argument("Loader", Some(1)) {
if !checker
.ctx
.resolve_call_path(loader_arg)
.map_or(false, |call_path| {
call_path.as_slice() == ["yaml", "SafeLoader"]

View File

@ -1,6 +1,6 @@
//! Settings for the `flake8-bandit` plugin.
use ruff_macros::ConfigurationOptions;
use ruff_macros::{CacheKey, ConfigurationOptions};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@ -45,7 +45,7 @@ pub struct Options {
pub check_typed_exception: Option<bool>,
}
#[derive(Debug, Hash)]
#[derive(Debug, CacheKey)]
pub struct Settings {
pub hardcoded_tmp_directory: Vec<String>,
pub check_typed_exception: bool,

View File

@ -35,7 +35,7 @@ pub fn blind_except(
return;
};
for exception in ["BaseException", "Exception"] {
if id == exception && checker.is_builtin(exception) {
if id == exception && checker.ctx.is_builtin(exception) {
// If the exception is re-raised, don't flag an error.
if body.iter().any(|stmt| {
if let StmtKind::Raise { exc, .. } = &stmt.node {

View File

@ -42,12 +42,14 @@ fn is_abc_class(checker: &Checker, bases: &[Expr], keywords: &[Keyword]) -> bool
.as_ref()
.map_or(false, |arg| arg == "metaclass")
&& checker
.ctx
.resolve_call_path(&keyword.node.value)
.map_or(false, |call_path| {
call_path.as_slice() == ["abc", "ABCMeta"]
})
}) || bases.iter().any(|base| {
checker
.ctx
.resolve_call_path(base)
.map_or(false, |call_path| call_path.as_slice() == ["abc", "ABC"])
})
@ -106,7 +108,7 @@ pub fn abstract_base_class(
continue;
};
let has_abstract_decorator = is_abstract(checker, decorator_list);
let has_abstract_decorator = is_abstract(&checker.ctx, decorator_list);
has_abstract_method |= has_abstract_decorator;
if !checker
@ -117,7 +119,10 @@ pub fn abstract_base_class(
continue;
}
if !has_abstract_decorator && is_empty_body(body) && !is_overload(checker, decorator_list) {
if !has_abstract_decorator
&& is_empty_body(body)
&& !is_overload(&checker.ctx, decorator_list)
{
checker.diagnostics.push(Diagnostic::new(
EmptyMethodWithoutAbstractDecorator {
name: format!("{name}.{method_name}"),

View File

@ -54,6 +54,7 @@ pub fn assert_raises_exception(checker: &mut Checker, stmt: &Stmt, items: &[With
return;
}
if !checker
.ctx
.resolve_call_path(args.first().unwrap())
.map_or(false, |call_path| call_path.as_slice() == ["", "Exception"])
{

View File

@ -19,15 +19,18 @@ impl Violation for CachedInstanceMethod {
}
fn is_cache_func(checker: &Checker, expr: &Expr) -> bool {
checker.resolve_call_path(expr).map_or(false, |call_path| {
call_path.as_slice() == ["functools", "lru_cache"]
|| call_path.as_slice() == ["functools", "cache"]
})
checker
.ctx
.resolve_call_path(expr)
.map_or(false, |call_path| {
call_path.as_slice() == ["functools", "lru_cache"]
|| call_path.as_slice() == ["functools", "cache"]
})
}
/// B019
pub fn cached_instance_method(checker: &mut Checker, decorator_list: &[Expr]) {
if !matches!(checker.current_scope().kind, ScopeKind::Class(_)) {
if !matches!(checker.ctx.current_scope().kind, ScopeKind::Class(_)) {
return;
}
for decorator in decorator_list {

View File

@ -38,14 +38,17 @@ const IMMUTABLE_FUNCS: &[&[&str]] = &[
];
fn is_immutable_func(checker: &Checker, func: &Expr, extend_immutable_calls: &[CallPath]) -> bool {
checker.resolve_call_path(func).map_or(false, |call_path| {
IMMUTABLE_FUNCS
.iter()
.any(|target| call_path.as_slice() == *target)
|| extend_immutable_calls
checker
.ctx
.resolve_call_path(func)
.map_or(false, |call_path| {
IMMUTABLE_FUNCS
.iter()
.any(|target| call_path == *target)
})
.any(|target| call_path.as_slice() == *target)
|| extend_immutable_calls
.iter()
.any(|target| call_path == *target)
})
}
struct ArgumentDefaultVisitor<'a> {

View File

@ -67,11 +67,14 @@ const IMMUTABLE_GENERIC_TYPES: &[&[&str]] = &[
];
pub fn is_mutable_func(checker: &Checker, func: &Expr) -> bool {
checker.resolve_call_path(func).map_or(false, |call_path| {
MUTABLE_FUNCS
.iter()
.any(|target| call_path.as_slice() == *target)
})
checker
.ctx
.resolve_call_path(func)
.map_or(false, |call_path| {
MUTABLE_FUNCS
.iter()
.any(|target| call_path.as_slice() == *target)
})
}
fn is_mutable_expr(checker: &Checker, expr: &Expr) -> bool {
@ -89,40 +92,44 @@ fn is_mutable_expr(checker: &Checker, expr: &Expr) -> bool {
fn is_immutable_annotation(checker: &Checker, expr: &Expr) -> bool {
match &expr.node {
ExprKind::Name { .. } | ExprKind::Attribute { .. } => {
checker.resolve_call_path(expr).map_or(false, |call_path| {
ExprKind::Name { .. } | ExprKind::Attribute { .. } => checker
.ctx
.resolve_call_path(expr)
.map_or(false, |call_path| {
IMMUTABLE_TYPES
.iter()
.chain(IMMUTABLE_GENERIC_TYPES)
.any(|target| call_path.as_slice() == *target)
})
}
}),
ExprKind::Subscript { value, slice, .. } => {
checker.resolve_call_path(value).map_or(false, |call_path| {
if IMMUTABLE_GENERIC_TYPES
.iter()
.any(|target| call_path.as_slice() == *target)
{
true
} else if call_path.as_slice() == ["typing", "Union"] {
if let ExprKind::Tuple { elts, .. } = &slice.node {
elts.iter().all(|elt| is_immutable_annotation(checker, elt))
checker
.ctx
.resolve_call_path(value)
.map_or(false, |call_path| {
if IMMUTABLE_GENERIC_TYPES
.iter()
.any(|target| call_path.as_slice() == *target)
{
true
} else if call_path.as_slice() == ["typing", "Union"] {
if let ExprKind::Tuple { elts, .. } = &slice.node {
elts.iter().all(|elt| is_immutable_annotation(checker, elt))
} else {
false
}
} else if call_path.as_slice() == ["typing", "Optional"] {
is_immutable_annotation(checker, slice)
} else if call_path.as_slice() == ["typing", "Annotated"] {
if let ExprKind::Tuple { elts, .. } = &slice.node {
elts.first()
.map_or(false, |elt| is_immutable_annotation(checker, elt))
} else {
false
}
} else {
false
}
} else if call_path.as_slice() == ["typing", "Optional"] {
is_immutable_annotation(checker, slice)
} else if call_path.as_slice() == ["typing", "Annotated"] {
if let ExprKind::Tuple { elts, .. } = &slice.node {
elts.first()
.map_or(false, |elt| is_immutable_annotation(checker, elt))
} else {
false
}
} else {
false
}
})
})
}
ExprKind::BinOp {
left,

View File

@ -75,7 +75,7 @@ pub fn setattr_with_constant(checker: &mut Checker, expr: &Expr, func: &Expr, ar
// We can only replace a `setattr` call (which is an `Expr`) with an assignment
// (which is a `Stmt`) if the `Expr` is already being used as a `Stmt`
// (i.e., it's directly within an `StmtKind::Expr`).
if let StmtKind::Expr { value: child } = &checker.current_stmt().node {
if let StmtKind::Expr { value: child } = &checker.ctx.current_stmt().node {
if expr == child.as_ref() {
let mut diagnostic = Diagnostic::new(SetAttrWithConstant, Range::from_located(expr));

View File

@ -140,7 +140,7 @@ pub fn unused_loop_control_variable(
}
// Avoid fixing any variables that _may_ be used, but undetectably so.
let certainty = Certainty::from(!helpers::uses_magic_variable_access(checker, body));
let certainty = Certainty::from(!helpers::uses_magic_variable_access(&checker.ctx, body));
// Attempt to rename the variable by prepending an underscore, but avoid
// applying the fix if doing so wouldn't actually cause us to ignore the
@ -163,14 +163,14 @@ pub fn unused_loop_control_variable(
if let Some(rename) = rename {
if certainty.into() && checker.patch(diagnostic.kind.rule()) {
// Find the `BindingKind::LoopVar` corresponding to the name.
let scope = checker.current_scope();
let scope = checker.ctx.current_scope();
let binding = scope
.bindings
.get(name)
.into_iter()
.chain(scope.rebounds.get(name).into_iter().flatten())
.find_map(|index| {
let binding = &checker.bindings[*index];
let binding = &checker.ctx.bindings[*index];
binding
.source
.as_ref()

View File

@ -22,9 +22,12 @@ impl Violation for UselessContextlibSuppress {
/// B022
pub fn useless_contextlib_suppress(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) {
if args.is_empty()
&& checker.resolve_call_path(func).map_or(false, |call_path| {
call_path.as_slice() == ["contextlib", "suppress"]
})
&& checker
.ctx
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["contextlib", "suppress"]
})
{
checker.diagnostics.push(Diagnostic::new(
UselessContextlibSuppress,

View File

@ -25,7 +25,7 @@ pub fn zip_without_explicit_strict(
) {
if let ExprKind::Name { id, .. } = &func.node {
if id == "zip"
&& checker.is_builtin("zip")
&& checker.ctx.is_builtin("zip")
&& !kwargs.iter().any(|keyword| {
keyword
.node

View File

@ -1,6 +1,6 @@
//! Settings for the `flake8-bugbear` plugin.
use ruff_macros::ConfigurationOptions;
use ruff_macros::{CacheKey, ConfigurationOptions};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@ -26,7 +26,7 @@ pub struct Options {
pub extend_immutable_calls: Option<Vec<String>>,
}
#[derive(Debug, Default, Hash)]
#[derive(Debug, Default, CacheKey)]
pub struct Settings {
pub extend_immutable_calls: Vec<String>,
}

View File

@ -1,6 +1,6 @@
//! Settings for the `flake8-builtins` plugin.
use ruff_macros::ConfigurationOptions;
use ruff_macros::{CacheKey, ConfigurationOptions};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@ -22,7 +22,7 @@ pub struct Options {
pub builtins_ignorelist: Option<Vec<String>>,
}
#[derive(Debug, Default, Hash)]
#[derive(Debug, Default, CacheKey)]
pub struct Settings {
pub builtins_ignorelist: Vec<String>,
}

View File

@ -75,7 +75,7 @@ pub fn unnecessary_call_around_sorted(
if inner != "sorted" {
return;
}
if !checker.is_builtin(inner) || !checker.is_builtin(outer) {
if !checker.ctx.is_builtin(inner) || !checker.ctx.is_builtin(outer) {
return;
}
let mut diagnostic = Diagnostic::new(

View File

@ -55,7 +55,7 @@ pub fn unnecessary_collection_call(
}
_ => return,
};
if !checker.is_builtin(id) {
if !checker.ctx.is_builtin(id) {
return;
}
let mut diagnostic = Diagnostic::new(

View File

@ -56,7 +56,7 @@ pub fn unnecessary_comprehension(
ExprKind::SetComp { .. } => "set",
_ => return,
};
if !checker.is_builtin(id) {
if !checker.ctx.is_builtin(id) {
return;
}
let mut diagnostic = Diagnostic::new(

View File

@ -100,7 +100,7 @@ pub fn unnecessary_double_cast_or_process(
let Some(inner) = helpers::function_name(func) else {
return;
};
if !checker.is_builtin(inner) || !checker.is_builtin(outer) {
if !checker.ctx.is_builtin(inner) || !checker.ctx.is_builtin(outer) {
return;
}

View File

@ -52,7 +52,7 @@ pub fn unnecessary_generator_list(
let Some(argument) = helpers::exactly_one_argument_with_matching_function("list", func, args, keywords) else {
return;
};
if !checker.is_builtin("list") {
if !checker.ctx.is_builtin("list") {
return;
}
if let ExprKind::GeneratorExp { .. } = argument {

View File

@ -53,7 +53,7 @@ pub fn unnecessary_generator_set(
let Some(argument) = helpers::exactly_one_argument_with_matching_function("set", func, args, keywords) else {
return;
};
if !checker.is_builtin("set") {
if !checker.ctx.is_builtin("set") {
return;
}
if let ExprKind::GeneratorExp { .. } = argument {

View File

@ -28,7 +28,7 @@ pub fn unnecessary_list_call(checker: &mut Checker, expr: &Expr, func: &Expr, ar
let Some(argument) = helpers::first_argument_with_matching_function("list", func, args) else {
return;
};
if !checker.is_builtin("list") {
if !checker.ctx.is_builtin("list") {
return;
}
if !matches!(argument, ExprKind::ListComp { .. }) {

View File

@ -34,7 +34,7 @@ pub fn unnecessary_list_comprehension_dict(
let Some(argument) = helpers::exactly_one_argument_with_matching_function("dict", func, args, keywords) else {
return;
};
if !checker.is_builtin("dict") {
if !checker.ctx.is_builtin("dict") {
return;
}
let ExprKind::ListComp { elt, .. } = &argument else {

View File

@ -34,7 +34,7 @@ pub fn unnecessary_list_comprehension_set(
let Some(argument) = helpers::exactly_one_argument_with_matching_function("set", func, args, keywords) else {
return;
};
if !checker.is_builtin("set") {
if !checker.ctx.is_builtin("set") {
return;
}
if let ExprKind::ListComp { .. } = &argument {

View File

@ -37,7 +37,7 @@ pub fn unnecessary_literal_dict(
let Some(argument) = helpers::exactly_one_argument_with_matching_function("dict", func, args, keywords) else {
return;
};
if !checker.is_builtin("dict") {
if !checker.ctx.is_builtin("dict") {
return;
}
let (kind, elts) = match argument {

View File

@ -37,7 +37,7 @@ pub fn unnecessary_literal_set(
let Some(argument) = helpers::exactly_one_argument_with_matching_function("set", func, args, keywords) else {
return;
};
if !checker.is_builtin("set") {
if !checker.ctx.is_builtin("set") {
return;
}
let kind = match argument {

View File

@ -52,7 +52,7 @@ pub fn unnecessary_literal_within_list_call(
let Some(argument) = helpers::first_argument_with_matching_function("list", func, args) else {
return;
};
if !checker.is_builtin("list") {
if !checker.ctx.is_builtin("list") {
return;
}
let argument_kind = match argument {

View File

@ -53,7 +53,7 @@ pub fn unnecessary_literal_within_tuple_call(
let Some(argument) = helpers::first_argument_with_matching_function("tuple", func, args) else {
return;
};
if !checker.is_builtin("tuple") {
if !checker.ctx.is_builtin("tuple") {
return;
}
let argument_kind = match argument {

View File

@ -88,7 +88,7 @@ pub fn unnecessary_map(
};
match id {
"map" => {
if !checker.is_builtin(id) {
if !checker.ctx.is_builtin(id) {
return;
}
@ -123,7 +123,7 @@ pub fn unnecessary_map(
}
}
"list" | "set" => {
if !checker.is_builtin(id) {
if !checker.ctx.is_builtin(id) {
return;
}
@ -157,7 +157,7 @@ pub fn unnecessary_map(
}
}
"dict" => {
if !checker.is_builtin(id) {
if !checker.ctx.is_builtin(id) {
return;
}

View File

@ -37,7 +37,7 @@ pub fn unnecessary_subscript_reversal(
if !(id == "set" || id == "sorted" || id == "reversed") {
return;
}
if !checker.is_builtin(id) {
if !checker.ctx.is_builtin(id) {
return;
}
let ExprKind::Subscript { slice, .. } = &first_arg.node else {

View File

@ -1,6 +1,6 @@
//! Settings for the `flake8-comprehensions` plugin.
use ruff_macros::ConfigurationOptions;
use ruff_macros::{CacheKey, ConfigurationOptions};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@ -22,7 +22,7 @@ pub struct Options {
pub allow_dict_calls_with_keyword_arguments: Option<bool>,
}
#[derive(Debug, Default, Hash)]
#[derive(Debug, Default, CacheKey)]
pub struct Settings {
pub allow_dict_calls_with_keyword_arguments: bool,
}

View File

@ -124,9 +124,13 @@ pub fn call_datetime_without_tzinfo(
keywords: &[Keyword],
location: Range,
) {
if !checker.resolve_call_path(func).map_or(false, |call_path| {
call_path.as_slice() == ["datetime", "datetime"]
}) {
if !checker
.ctx
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["datetime", "datetime"]
})
{
return;
}
@ -153,9 +157,13 @@ pub fn call_datetime_without_tzinfo(
/// It uses the system local timezone.
/// Use `datetime.datetime.now(tz=)` instead.
pub fn call_datetime_today(checker: &mut Checker, func: &Expr, location: Range) {
if checker.resolve_call_path(func).map_or(false, |call_path| {
call_path.as_slice() == ["datetime", "datetime", "today"]
}) {
if checker
.ctx
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["datetime", "datetime", "today"]
})
{
checker
.diagnostics
.push(Diagnostic::new(CallDatetimeToday, location));
@ -171,9 +179,13 @@ pub fn call_datetime_today(checker: &mut Checker, func: &Expr, location: Range)
/// UTC. As such, the recommended way to create an object representing the
/// current time in UTC is by calling `datetime.now(timezone.utc)`.
pub fn call_datetime_utcnow(checker: &mut Checker, func: &Expr, location: Range) {
if checker.resolve_call_path(func).map_or(false, |call_path| {
call_path.as_slice() == ["datetime", "datetime", "utcnow"]
}) {
if checker
.ctx
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["datetime", "datetime", "utcnow"]
})
{
checker
.diagnostics
.push(Diagnostic::new(CallDatetimeUtcnow, location));
@ -190,9 +202,13 @@ pub fn call_datetime_utcnow(checker: &mut Checker, func: &Expr, location: Range)
/// specific timestamp in UTC is by calling `datetime.fromtimestamp(timestamp,
/// tz=timezone.utc)`.
pub fn call_datetime_utcfromtimestamp(checker: &mut Checker, func: &Expr, location: Range) {
if checker.resolve_call_path(func).map_or(false, |call_path| {
call_path.as_slice() == ["datetime", "datetime", "utcfromtimestamp"]
}) {
if checker
.ctx
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["datetime", "datetime", "utcfromtimestamp"]
})
{
checker
.diagnostics
.push(Diagnostic::new(CallDatetimeUtcfromtimestamp, location));
@ -207,9 +223,13 @@ pub fn call_datetime_now_without_tzinfo(
keywords: &[Keyword],
location: Range,
) {
if !checker.resolve_call_path(func).map_or(false, |call_path| {
call_path.as_slice() == ["datetime", "datetime", "now"]
}) {
if !checker
.ctx
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["datetime", "datetime", "now"]
})
{
return;
}
@ -245,9 +265,13 @@ pub fn call_datetime_fromtimestamp(
keywords: &[Keyword],
location: Range,
) {
if !checker.resolve_call_path(func).map_or(false, |call_path| {
call_path.as_slice() == ["datetime", "datetime", "fromtimestamp"]
}) {
if !checker
.ctx
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["datetime", "datetime", "fromtimestamp"]
})
{
return;
}
@ -282,9 +306,13 @@ pub fn call_datetime_strptime_without_zone(
args: &[Expr],
location: Range,
) {
if !checker.resolve_call_path(func).map_or(false, |call_path| {
call_path.as_slice() == ["datetime", "datetime", "strptime"]
}) {
if !checker
.ctx
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["datetime", "datetime", "strptime"]
})
{
return;
}
@ -299,7 +327,7 @@ pub fn call_datetime_strptime_without_zone(
}
};
let (Some(grandparent), Some(parent)) = (checker.current_expr_grandparent(), checker.current_expr_parent()) else {
let (Some(grandparent), Some(parent)) = (checker.ctx.current_expr_grandparent(), checker.ctx.current_expr_parent()) else {
checker.diagnostics.push(Diagnostic::new(
CallDatetimeStrptimeWithoutZone,
location,
@ -335,9 +363,13 @@ pub fn call_datetime_strptime_without_zone(
/// It uses the system local timezone.
/// Use `datetime.datetime.now(tz=).date()` instead.
pub fn call_date_today(checker: &mut Checker, func: &Expr, location: Range) {
if checker.resolve_call_path(func).map_or(false, |call_path| {
call_path.as_slice() == ["datetime", "date", "today"]
}) {
if checker
.ctx
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["datetime", "date", "today"]
})
{
checker
.diagnostics
.push(Diagnostic::new(CallDateToday, location));
@ -351,9 +383,13 @@ pub fn call_date_today(checker: &mut Checker, func: &Expr, location: Range) {
/// It uses the system local timezone.
/// Use `datetime.datetime.fromtimestamp(, tz=).date()` instead.
pub fn call_date_fromtimestamp(checker: &mut Checker, func: &Expr, location: Range) {
if checker.resolve_call_path(func).map_or(false, |call_path| {
call_path.as_slice() == ["datetime", "date", "fromtimestamp"]
}) {
if checker
.ctx
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["datetime", "date", "fromtimestamp"]
})
{
checker
.diagnostics
.push(Diagnostic::new(CallDateFromtimestamp, location));

View File

@ -46,7 +46,7 @@ const DEBUGGERS: &[&[&str]] = &[
/// Checks for the presence of a debugger call.
pub fn debugger_call(checker: &mut Checker, expr: &Expr, func: &Expr) {
if let Some(target) = checker.resolve_call_path(func).and_then(|call_path| {
if let Some(target) = checker.ctx.resolve_call_path(func).and_then(|call_path| {
DEBUGGERS
.iter()
.find(|target| call_path.as_slice() == **target)

View File

@ -4,22 +4,28 @@ use crate::checkers::ast::Checker;
/// Return `true` if a Python class appears to be a Django model, based on its base classes.
pub fn is_model(checker: &Checker, base: &Expr) -> bool {
checker.resolve_call_path(base).map_or(false, |call_path| {
call_path.as_slice() == ["django", "db", "models", "Model"]
})
checker
.ctx
.resolve_call_path(base)
.map_or(false, |call_path| {
call_path.as_slice() == ["django", "db", "models", "Model"]
})
}
/// Return `true` if a Python class appears to be a Django model form, based on its base classes.
pub fn is_model_form(checker: &Checker, base: &Expr) -> bool {
checker.resolve_call_path(base).map_or(false, |call_path| {
call_path.as_slice() == ["django", "forms", "ModelForm"]
|| call_path.as_slice() == ["django", "forms", "models", "ModelForm"]
})
checker
.ctx
.resolve_call_path(base)
.map_or(false, |call_path| {
call_path.as_slice() == ["django", "forms", "ModelForm"]
|| call_path.as_slice() == ["django", "forms", "models", "ModelForm"]
})
}
/// Return the name of the field type, if the expression is constructor for a Django model field.
pub fn get_model_field_name<'a>(checker: &'a Checker, expr: &'a Expr) -> Option<&'a str> {
checker.resolve_call_path(expr).and_then(|call_path| {
checker.ctx.resolve_call_path(expr).and_then(|call_path| {
let call_path = call_path.as_slice();
if !call_path.starts_with(&["django", "db", "models"]) {
return None;

View File

@ -46,9 +46,13 @@ pub fn locals_in_render_function(
args: &[Expr],
keywords: &[Keyword],
) {
if !checker.resolve_call_path(func).map_or(false, |call_path| {
call_path.as_slice() == ["django", "shortcuts", "render"]
}) {
if !checker
.ctx
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["django", "shortcuts", "render"]
})
{
return;
}
@ -83,6 +87,7 @@ fn is_locals_call(checker: &Checker, expr: &Expr) -> bool {
return false
};
checker
.ctx
.resolve_call_path(func)
.map_or(false, |call_path| call_path.as_slice() == ["", "locals"])
}

View File

@ -1,6 +1,6 @@
//! Settings for the `flake8-errmsg` plugin.
use ruff_macros::ConfigurationOptions;
use ruff_macros::{CacheKey, ConfigurationOptions};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@ -18,7 +18,7 @@ pub struct Options {
pub max_string_length: Option<usize>,
}
#[derive(Debug, Default, Hash)]
#[derive(Debug, Default, CacheKey)]
pub struct Settings {
pub max_string_length: usize,
}

View File

@ -1,6 +1,6 @@
//! Settings for the `flake8-implicit-str-concat` plugin.
use ruff_macros::ConfigurationOptions;
use ruff_macros::{CacheKey, ConfigurationOptions};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@ -32,7 +32,7 @@ pub struct Options {
pub allow_multiline: Option<bool>,
}
#[derive(Debug, Hash)]
#[derive(Debug, CacheKey)]
pub struct Settings {
pub allow_multiline: bool,
}

View File

@ -1,14 +1,10 @@
//! Settings for import conventions.
use std::hash::Hash;
use ruff_macros::ConfigurationOptions;
use ruff_macros::{CacheKey, ConfigurationOptions};
use rustc_hash::FxHashMap;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::settings::hashable::HashableHashMap;
const CONVENTIONAL_ALIASES: &[(&str, &str)] = &[
("altair", "alt"),
("matplotlib", "mpl"),
@ -64,9 +60,9 @@ pub struct Options {
pub extend_aliases: Option<FxHashMap<String, String>>,
}
#[derive(Debug, Hash)]
#[derive(Debug, CacheKey)]
pub struct Settings {
pub aliases: HashableHashMap<String, String>,
pub aliases: FxHashMap<String, String>,
}
fn default_aliases() -> FxHashMap<String, String> {
@ -90,7 +86,7 @@ fn resolve_aliases(options: Options) -> FxHashMap<String, String> {
impl Default for Settings {
fn default() -> Self {
Self {
aliases: default_aliases().into(),
aliases: default_aliases(),
}
}
}
@ -98,7 +94,7 @@ impl Default for Settings {
impl From<Options> for Settings {
fn from(options: Options) -> Self {
Self {
aliases: resolve_aliases(options).into(),
aliases: resolve_aliases(options),
}
}
}
@ -106,7 +102,7 @@ impl From<Options> for Settings {
impl From<Settings> for Options {
fn from(settings: Settings) -> Self {
Self {
aliases: Some(settings.aliases.into()),
aliases: Some(settings.aliases),
extend_aliases: None,
}
}

View File

@ -107,6 +107,7 @@ fn check_log_record_attr_clash(checker: &mut Checker, extra: &Keyword) {
}
ExprKind::Call { func, keywords, .. } => {
if checker
.ctx
.resolve_call_path(func)
.map_or(false, |call_path| call_path.as_slice() == ["", "dict"])
{
@ -180,7 +181,7 @@ pub fn logging_call(checker: &mut Checker, func: &Expr, args: &[Expr], keywords:
.rules
.enabled(&Rule::LoggingRedundantExcInfo)
{
if !checker.in_exception_handler() {
if !checker.ctx.in_exception_handler() {
return;
}
if let Some(exc_info) = find_keyword(keywords, "exc_info") {
@ -193,9 +194,12 @@ pub fn logging_call(checker: &mut Checker, func: &Expr, args: &[Expr], keywords:
..
}
) || if let ExprKind::Call { func, .. } = &exc_info.node.value.node {
checker.resolve_call_path(func).map_or(false, |call_path| {
call_path.as_slice() == ["sys", "exc_info"]
})
checker
.ctx
.resolve_call_path(func)
.map_or(false, |call_path| {
call_path.as_slice() == ["sys", "exc_info"]
})
} else {
false
}) {

View File

@ -242,11 +242,7 @@ pub fn dupe_class_field_definitions<'a, 'b>(
Range::from_located(stmt),
);
if checker.patch(diagnostic.kind.rule()) {
let deleted: Vec<&Stmt> = checker
.deletions
.iter()
.map(std::convert::Into::into)
.collect();
let deleted: Vec<&Stmt> = checker.deletions.iter().map(Into::into).collect();
let locator = checker.locator;
match delete_stmt(
stmt,
@ -281,6 +277,7 @@ where
if !bases.iter().any(|expr| {
checker
.ctx
.resolve_call_path(expr)
.map_or(false, |call_path| call_path.as_slice() == ["enum", "Enum"])
}) {
@ -295,6 +292,7 @@ where
if let ExprKind::Call { func, .. } = &value.node {
if checker
.ctx
.resolve_call_path(func)
.map_or(false, |call_path| call_path.as_slice() == ["enum", "auto"])
{
@ -337,7 +335,7 @@ pub fn unnecessary_comprehension_any_all(
) {
if let ExprKind::Name { id, .. } = &func.node {
if (id == "all" || id == "any") && args.len() == 1 {
if !checker.is_builtin(id) {
if !checker.ctx.is_builtin(id) {
return;
}
if let ExprKind::ListComp { .. } = args[0].node {

View File

@ -31,7 +31,7 @@ impl Violation for PPrintFound {
/// T201, T203
pub fn print_call(checker: &mut Checker, func: &Expr, keywords: &[Keyword]) {
let diagnostic = {
let call_path = checker.resolve_call_path(func);
let call_path = checker.ctx.resolve_call_path(func);
if call_path
.as_ref()
.map_or(false, |call_path| *call_path.as_slice() == ["", "print"])
@ -43,13 +43,13 @@ pub fn print_call(checker: &mut Checker, func: &Expr, keywords: &[Keyword]) {
.find(|keyword| keyword.node.arg.as_ref().map_or(false, |arg| arg == "file"))
{
if !is_const_none(&keyword.node.value) {
if checker
.resolve_call_path(&keyword.node.value)
.map_or(true, |call_path| {
if checker.ctx.resolve_call_path(&keyword.node.value).map_or(
true,
|call_path| {
call_path.as_slice() != ["sys", "stdout"]
&& call_path.as_slice() != ["sys", "stderr"]
})
{
},
) {
return;
}
}

View File

@ -31,6 +31,8 @@ mod tests {
#[test_case(Rule::ArgumentSimpleDefaults, Path::new("PYI014.pyi"))]
#[test_case(Rule::DocstringInStub, Path::new("PYI021.py"))]
#[test_case(Rule::DocstringInStub, Path::new("PYI021.pyi"))]
#[test_case(Rule::TypeCommentInStub, Path::new("PYI033.py"))]
#[test_case(Rule::TypeCommentInStub, Path::new("PYI033.pyi"))]
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
let diagnostics = test_path(

View File

@ -70,9 +70,13 @@ pub fn bad_version_info_comparison(
return;
};
if !checker.resolve_call_path(left).map_or(false, |call_path| {
call_path.as_slice() == ["sys", "version_info"]
}) {
if !checker
.ctx
.resolve_call_path(left)
.map_or(false, |call_path| {
call_path.as_slice() == ["sys", "version_info"]
})
{
return;
}

View File

@ -7,6 +7,7 @@ pub use simple_defaults::{
argument_simple_defaults, typed_argument_simple_defaults, ArgumentSimpleDefaults,
TypedArgumentSimpleDefaults,
};
pub use type_comment_in_stub::{type_comment_in_stub, TypeCommentInStub};
pub use unrecognized_platform::{
unrecognized_platform, UnrecognizedPlatformCheck, UnrecognizedPlatformName,
};
@ -17,4 +18,5 @@ mod non_empty_stub_body;
mod pass_statement_stub_body;
mod prefix_type_params;
mod simple_defaults;
mod type_comment_in_stub;
mod unrecognized_platform;

View File

@ -71,12 +71,12 @@ pub fn prefix_type_params(checker: &mut Checker, value: &Expr, targets: &[Expr])
};
if let ExprKind::Call { func, .. } = &value.node {
let Some(kind) = checker.resolve_call_path(func).and_then(|call_path| {
if checker.match_typing_call_path(&call_path, "ParamSpec") {
let Some(kind) = checker.ctx.resolve_call_path(func).and_then(|call_path| {
if checker.ctx.match_typing_call_path(&call_path, "ParamSpec") {
Some(VarKind::ParamSpec)
} else if checker.match_typing_call_path(&call_path, "TypeVar") {
} else if checker.ctx.match_typing_call_path(&call_path, "TypeVar") {
Some(VarKind::TypeVar)
} else if checker.match_typing_call_path(&call_path, "TypeVarTuple") {
} else if checker.ctx.match_typing_call_path(&call_path, "TypeVarTuple") {
Some(VarKind::TypeVarTuple)
} else {
None

View File

@ -122,6 +122,7 @@ fn is_valid_default_value_with_annotation(default: &Expr, checker: &Checker) ->
// `sys.stdin`, etc.
ExprKind::Attribute { .. } => {
if checker
.ctx
.resolve_call_path(default)
.map_or(false, |call_path| {
ALLOWED_ATTRIBUTES_IN_DEFAULTS

View File

@ -0,0 +1,64 @@
use once_cell::sync::Lazy;
use regex::Regex;
use rustpython_parser::lexer::LexResult;
use rustpython_parser::Tok;
use ruff_macros::{define_violation, derive_message_formats};
use crate::registry::Diagnostic;
use crate::violation::Violation;
use crate::Range;
define_violation!(
/// ## What it does
/// Checks for the use of type comments (e.g., `x = 1 # type: int`) in stub
/// files.
///
/// ## Why is this bad?
/// Stub (`.pyi`) files should use type annotations directly, rather
/// than type comments, even if they're intended to support Python 2, since
/// stub files are not executed at runtime. The one exception is `# type: ignore`.
///
/// ## Example
/// ```python
/// x = 1 # type: int
/// ```
///
/// Use instead:
/// ```python
/// x: int = 1
/// ```
pub struct TypeCommentInStub;
);
impl Violation for TypeCommentInStub {
#[derive_message_formats]
fn message(&self) -> String {
format!("Don't use type comments in stub file")
}
}
static TYPE_COMMENT_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r"^#\s*type:\s*([^#]+)(\s*#.*?)?$").unwrap());
static TYPE_IGNORE_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r"^#\s*type:\s*ignore([^#]+)?(\s*#.*?)?$").unwrap());
/// PYI033
pub fn type_comment_in_stub(tokens: &[LexResult]) -> Vec<Diagnostic> {
let mut diagnostics = vec![];
for token in tokens.iter().flatten() {
if let (location, Tok::Comment(comment), end_location) = token {
if TYPE_COMMENT_REGEX.is_match(comment) && !TYPE_IGNORE_REGEX.is_match(comment) {
diagnostics.push(Diagnostic::new(
TypeCommentInStub,
Range {
location: *location,
end_location: *end_location,
},
));
}
}
}
diagnostics
}

View File

@ -103,9 +103,13 @@ pub fn unrecognized_platform(
let diagnostic_unrecognized_platform_check =
Diagnostic::new(UnrecognizedPlatformCheck, Range::from_located(expr));
if !checker.resolve_call_path(left).map_or(false, |call_path| {
call_path.as_slice() == ["sys", "platform"]
}) {
if !checker
.ctx
.resolve_call_path(left)
.map_or(false, |call_path| {
call_path.as_slice() == ["sys", "platform"]
})
{
return;
}

View File

@ -0,0 +1,6 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
expression: diagnostics
---
[]

View File

@ -0,0 +1,115 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
expression: diagnostics
---
- kind:
TypeCommentInStub: ~
location:
row: 6
column: 21
end_location:
row: 6
column: 127
fix: ~
parent: ~
- kind:
TypeCommentInStub: ~
location:
row: 7
column: 21
end_location:
row: 7
column: 183
fix: ~
parent: ~
- kind:
TypeCommentInStub: ~
location:
row: 8
column: 21
end_location:
row: 8
column: 126
fix: ~
parent: ~
- kind:
TypeCommentInStub: ~
location:
row: 9
column: 21
end_location:
row: 9
column: 132
fix: ~
parent: ~
- kind:
TypeCommentInStub: ~
location:
row: 10
column: 19
end_location:
row: 10
column: 128
fix: ~
parent: ~
- kind:
TypeCommentInStub: ~
location:
row: 11
column: 19
end_location:
row: 11
column: 123
fix: ~
parent: ~
- kind:
TypeCommentInStub: ~
location:
row: 14
column: 11
end_location:
row: 14
column: 128
fix: ~
parent: ~
- kind:
TypeCommentInStub: ~
location:
row: 15
column: 10
end_location:
row: 15
column: 172
fix: ~
parent: ~
- kind:
TypeCommentInStub: ~
location:
row: 19
column: 28
end_location:
row: 19
column: 139
fix: ~
parent: ~
- kind:
TypeCommentInStub: ~
location:
row: 29
column: 21
end_location:
row: 29
column: 44
fix: ~
parent: ~
- kind:
TypeCommentInStub: ~
location:
row: 32
column: 25
end_location:
row: 32
column: 55
fix: ~
parent: ~

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