Compare commits
74 Commits
v0.3.7
...
dhruv/refa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0fc84fd78 | ||
|
|
060141b1de | ||
|
|
b04cb9f92a | ||
|
|
c80b9a4a90 | ||
|
|
99f7f94538 | ||
|
|
7b3c92a979 | ||
|
|
fdbcb62adc | ||
|
|
0ff25a540c | ||
|
|
34873ec009 | ||
|
|
d3cd61f804 | ||
|
|
9b80cc09ee | ||
|
|
9bb23b0a38 | ||
|
|
06c248a126 | ||
|
|
27902b7130 | ||
|
|
97acf1d59b | ||
|
|
adf63d9013 | ||
|
|
5d3c9f2637 | ||
|
|
33529c049e | ||
|
|
e751b4ea82 | ||
|
|
25a9131109 | ||
|
|
b7066e64e7 | ||
|
|
6c4d779140 | ||
|
|
8020d486f6 | ||
|
|
13ffb5bc19 | ||
|
|
e09180b1df | ||
|
|
2cc487eb22 | ||
|
|
5da7299b32 | ||
|
|
4d8890eef5 | ||
|
|
9f01ac3f87 | ||
|
|
b23414e3cc | ||
|
|
1480d72643 | ||
|
|
06b3e376ac | ||
|
|
e8b1125b30 | ||
|
|
16cc9bd78d | ||
|
|
0a6327418d | ||
|
|
518b29a9ef | ||
|
|
caae8d2c68 | ||
|
|
2971655b28 | ||
|
|
f48a794125 | ||
|
|
2882604451 | ||
|
|
eab3c4e334 | ||
|
|
cffc55576f | ||
|
|
4284e079b5 | ||
|
|
65edbfe62f | ||
|
|
45db695c47 | ||
|
|
1801798e85 | ||
|
|
d4e140d47f | ||
|
|
f779babc5f | ||
|
|
effd5188c9 | ||
|
|
6dccbd2b58 | ||
|
|
0c8ba32819 | ||
|
|
4ac523c19d | ||
|
|
f9214f95bb | ||
|
|
49d9ad4c7e | ||
|
|
c2210359e7 | ||
|
|
670d66f54c | ||
|
|
cbd500141f | ||
|
|
b62aeb39d2 | ||
|
|
cdbd754870 | ||
|
|
91efca1837 | ||
|
|
09ae2341e9 | ||
|
|
f07af6fb63 | ||
|
|
b11d17f65c | ||
|
|
b4c7c55ddd | ||
|
|
0f01713257 | ||
|
|
ed9a92d915 | ||
|
|
6da4ea6116 | ||
|
|
f9a828f493 | ||
|
|
812b0976a9 | ||
|
|
b356c4376c | ||
|
|
85ca5b7eed | ||
|
|
c2421068bc | ||
|
|
e9870fe468 | ||
|
|
a013050c11 |
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -8,5 +8,7 @@ crates/ruff_linter/resources/test/fixtures/pycodestyle/W391_3.py text eol=crlf
|
||||
crates/ruff_python_formatter/resources/test/fixtures/ruff/docstring_code_examples_crlf.py text eol=crlf
|
||||
crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples_crlf.py.snap text eol=crlf
|
||||
|
||||
crates/ruff_python_parser/resources/inline linguist-generated=true
|
||||
|
||||
ruff.schema.json linguist-generated=true text=auto eol=lf
|
||||
*.md.snap linguist-language=Markdown
|
||||
|
||||
5
.github/CODEOWNERS
vendored
5
.github/CODEOWNERS
vendored
@@ -5,11 +5,10 @@
|
||||
# - The '*' pattern is global owners.
|
||||
# - Order is important. The last matching pattern has the most precedence.
|
||||
|
||||
# Jupyter
|
||||
/crates/ruff_linter/src/jupyter/ @dhruvmanila
|
||||
/crates/ruff_notebook/ @dhruvmanila
|
||||
/crates/ruff_formatter/ @MichaReiser
|
||||
/crates/ruff_python_formatter/ @MichaReiser
|
||||
/crates/ruff_python_parser/ @MichaReiser
|
||||
/crates/ruff_python_parser/ @MichaReiser @dhruvmanila
|
||||
|
||||
# flake8-pyi
|
||||
/crates/ruff_linter/src/rules/flake8_pyi/ @AlexWaygood
|
||||
|
||||
10
.github/renovate.json5
vendored
10
.github/renovate.json5
vendored
@@ -4,7 +4,8 @@
|
||||
suppressNotifications: ["prEditedNotification"],
|
||||
extends: ["config:recommended"],
|
||||
labels: ["internal"],
|
||||
schedule: ["on Monday"],
|
||||
schedule: ["before 4am on Monday"],
|
||||
semanticCommits: "disabled",
|
||||
separateMajorMinor: false,
|
||||
prHourlyLimit: 10,
|
||||
enabledManagers: ["github-actions", "pre-commit", "cargo", "pep621", "npm"],
|
||||
@@ -52,6 +53,13 @@
|
||||
matchPackagePatterns: ["strum"],
|
||||
description: "Weekly update of strum dependencies",
|
||||
},
|
||||
{
|
||||
groupName: "ESLint",
|
||||
matchManagers: ["npm"],
|
||||
matchPackageNames: ["eslint"],
|
||||
allowedVersions: "<9",
|
||||
description: "Constraint ESLint to version 8 until TypeScript-eslint supports ESLint 9", // https://github.com/typescript-eslint/typescript-eslint/issues/8211
|
||||
},
|
||||
],
|
||||
vulnerabilityAlerts: {
|
||||
commitMessageSuffix: "",
|
||||
|
||||
@@ -41,7 +41,7 @@ repos:
|
||||
)$
|
||||
|
||||
- repo: https://github.com/crate-ci/typos
|
||||
rev: v1.20.4
|
||||
rev: v1.20.8
|
||||
hooks:
|
||||
- id: typos
|
||||
|
||||
@@ -55,7 +55,7 @@ repos:
|
||||
pass_filenames: false # This makes it a lot faster
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.3.5
|
||||
rev: v0.4.0
|
||||
hooks:
|
||||
- id: ruff-format
|
||||
- id: ruff
|
||||
|
||||
91
CHANGELOG.md
91
CHANGELOG.md
@@ -1,5 +1,94 @@
|
||||
# Changelog
|
||||
|
||||
## 0.4.1
|
||||
|
||||
### Preview features
|
||||
|
||||
- \[`pylint`\] Implement `invalid-hash-returned` (`PLE0309`) ([#10961](https://github.com/astral-sh/ruff/pull/10961))
|
||||
- \[`pylint`\] Implement `invalid-index-returned` (`PLE0305`) ([#10962](https://github.com/astral-sh/ruff/pull/10962))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- \[`pylint`\] Allow `NoReturn`-like functions for `__str__`, `__len__`, etc. (`PLE0307`) ([#11017](https://github.com/astral-sh/ruff/pull/11017))
|
||||
- Parser: Use empty range when there's "gap" in token source ([#11032](https://github.com/astral-sh/ruff/pull/11032))
|
||||
- \[`ruff`\] Ignore stub functions in `unused-async` (`RUF029`) ([#11026](https://github.com/astral-sh/ruff/pull/11026))
|
||||
- Parser: Expect indented case block instead of match stmt ([#11033](https://github.com/astral-sh/ruff/pull/11033))
|
||||
|
||||
## 0.4.0
|
||||
|
||||
### A new, hand-written parser
|
||||
|
||||
Ruff's new parser is **>2x faster**, which translates to a **20-40% speedup** for all linting and formatting invocations.
|
||||
There's a lot to say about this exciting change, so check out the [blog post](https://astral.sh/blog/ruff-v0.4.0) for more details!
|
||||
|
||||
See [#10036](https://github.com/astral-sh/ruff/pull/10036) for implementation details.
|
||||
|
||||
### A new language server in Rust
|
||||
|
||||
With this release, we also want to highlight our new language server. `ruff server` is a Rust-powered language
|
||||
server that comes built-in with Ruff. It can be used with any editor that supports the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) (LSP).
|
||||
It uses a multi-threaded, lock-free architecture inspired by `rust-analyzer` and it will open the door for a lot
|
||||
of exciting features. It’s also faster than our previous [Python-based language server](https://github.com/astral-sh/ruff-lsp)
|
||||
-- but you probably guessed that already.
|
||||
|
||||
`ruff server` is only in alpha, but it has a lot of features that you can try out today:
|
||||
|
||||
- Lints Python files automatically and shows quick-fixes when available
|
||||
- Formats Python files, with support for range formatting
|
||||
- Comes with commands for quickly performing actions: `ruff.applyAutofix`, `ruff.applyFormat`, and `ruff.applyOrganizeImports`
|
||||
- Supports `source.fixAll` and `source.organizeImports` source actions
|
||||
- Automatically reloads your project configuration when you change it
|
||||
|
||||
To setup `ruff server` with your editor, refer to the [README.md](https://github.com/astral-sh/ruff/blob/main/crates/ruff_server/README.md).
|
||||
|
||||
### Preview features
|
||||
|
||||
- \[`pycodestyle`\] Do not trigger `E3` rules on `def`s following a function/method with a dummy body ([#10704](https://github.com/astral-sh/ruff/pull/10704))
|
||||
- \[`pylint`\] Implement `invalid-bytes-returned` (`E0308`) ([#10959](https://github.com/astral-sh/ruff/pull/10959))
|
||||
- \[`pylint`\] Implement `invalid-length-returned` (`E0303`) ([#10963](https://github.com/astral-sh/ruff/pull/10963))
|
||||
- \[`pylint`\] Implement `self-cls-assignment` (`W0642`) ([#9267](https://github.com/astral-sh/ruff/pull/9267))
|
||||
- \[`pylint`\] Omit stubs from `invalid-bool` and `invalid-str-return-type` ([#11008](https://github.com/astral-sh/ruff/pull/11008))
|
||||
- \[`ruff`\] New rule `unused-async` (`RUF029`) to detect unneeded `async` keywords on functions ([#9966](https://github.com/astral-sh/ruff/pull/9966))
|
||||
|
||||
### Rule changes
|
||||
|
||||
- \[`flake8-bandit`\] Allow `urllib.request.urlopen` calls with static `Request` argument (`S310`) ([#10964](https://github.com/astral-sh/ruff/pull/10964))
|
||||
- \[`flake8-bugbear`\] Treat `raise NotImplemented`-only bodies as stub functions (`B006`) ([#10990](https://github.com/astral-sh/ruff/pull/10990))
|
||||
- \[`flake8-slots`\] Respect same-file `Enum` subclasses (`SLOT000`) ([#11006](https://github.com/astral-sh/ruff/pull/11006))
|
||||
- \[`pylint`\] Support inverted comparisons (`PLR1730`) ([#10920](https://github.com/astral-sh/ruff/pull/10920))
|
||||
|
||||
### Linter
|
||||
|
||||
- Improve handling of builtin symbols in linter rules ([#10919](https://github.com/astral-sh/ruff/pull/10919))
|
||||
- Improve display of rules in `--show-settings` ([#11003](https://github.com/astral-sh/ruff/pull/11003))
|
||||
- Improve inference capabilities of the `BuiltinTypeChecker` ([#10976](https://github.com/astral-sh/ruff/pull/10976))
|
||||
- Resolve classes and functions relative to script name ([#10965](https://github.com/astral-sh/ruff/pull/10965))
|
||||
- Improve performance of `RuleTable::any_enabled` ([#10971](https://github.com/astral-sh/ruff/pull/10971))
|
||||
|
||||
### Server
|
||||
|
||||
*This section is devoted to updates for our new language server, written in Rust.*
|
||||
|
||||
- Enable ruff-specific source actions ([#10916](https://github.com/astral-sh/ruff/pull/10916))
|
||||
- Refreshes diagnostics for open files when file configuration is changed ([#10988](https://github.com/astral-sh/ruff/pull/10988))
|
||||
- Important errors are now shown as popups ([#10951](https://github.com/astral-sh/ruff/pull/10951))
|
||||
- Introduce settings for directly configuring the linter and formatter ([#10984](https://github.com/astral-sh/ruff/pull/10984))
|
||||
- Resolve configuration for each document individually ([#10950](https://github.com/astral-sh/ruff/pull/10950))
|
||||
- Write a setup guide for Neovim ([#10987](https://github.com/astral-sh/ruff/pull/10987))
|
||||
|
||||
### Configuration
|
||||
|
||||
- Add `RUFF_OUTPUT_FILE` environment variable support ([#10992](https://github.com/astral-sh/ruff/pull/10992))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Avoid `non-augmented-assignment` for reversed, non-commutative operators (`PLR6104`) ([#10909](https://github.com/astral-sh/ruff/pull/10909))
|
||||
- Limit commutative non-augmented-assignments to primitive data types (`PLR6104`) ([#10912](https://github.com/astral-sh/ruff/pull/10912))
|
||||
- Respect `per-file-ignores` for `RUF100` on blanket `# noqa` ([#10908](https://github.com/astral-sh/ruff/pull/10908))
|
||||
- Consider `if` expression for parenthesized with items parsing ([#11010](https://github.com/astral-sh/ruff/pull/11010))
|
||||
- Consider binary expr for parenthesized with items parsing ([#11012](https://github.com/astral-sh/ruff/pull/11012))
|
||||
- Reset `FOR_TARGET` context for all kinds of parentheses ([#11009](https://github.com/astral-sh/ruff/pull/11009))
|
||||
|
||||
## 0.3.7
|
||||
|
||||
### Preview features
|
||||
@@ -1385,7 +1474,7 @@ Read Ruff's new [versioning policy](https://docs.astral.sh/ruff/versioning/).
|
||||
- \[`refurb`\] Add `single-item-membership-test` (`FURB171`) ([#7815](https://github.com/astral-sh/ruff/pull/7815))
|
||||
- \[`pylint`\] Add `and-or-ternary` (`R1706`) ([#7811](https://github.com/astral-sh/ruff/pull/7811))
|
||||
|
||||
_New rules are added in [preview](https://docs.astral.sh/ruff/preview/)._
|
||||
*New rules are added in [preview](https://docs.astral.sh/ruff/preview/).*
|
||||
|
||||
### Configuration
|
||||
|
||||
|
||||
311
Cargo.lock
generated
311
Cargo.lock
generated
@@ -123,15 +123,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.81"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
|
||||
checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"
|
||||
|
||||
[[package]]
|
||||
name = "argfile"
|
||||
version = "0.1.6"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1287c4f82a41c5085e65ee337c7934d71ab43d5187740a81fb69129013f6a5f6"
|
||||
checksum = "b7c5c8e418080ef8aa932039d12eda7b6f5043baf48f1523c166fbc32d004534"
|
||||
dependencies = [
|
||||
"fs-err",
|
||||
"os_str_bytes",
|
||||
@@ -143,15 +143,6 @@ version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
|
||||
|
||||
[[package]]
|
||||
name = "ascii-canvas"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6"
|
||||
dependencies = [
|
||||
"term",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
@@ -173,21 +164,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
|
||||
dependencies = [
|
||||
"bit-vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-vec"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
@@ -261,9 +237,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.37"
|
||||
version = "0.4.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e"
|
||||
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
@@ -371,7 +347,7 @@ dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
"syn 2.0.59",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -395,9 +371,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "codspeed"
|
||||
version = "2.4.0"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b85b056aa0541d1975ebc524149dde72803a5d7352b6aebf9eabc44f9017246"
|
||||
checksum = "655a6d8e698ef8de25b6fb160dc904d98cf3642df48fae8bb05711db1893f47d"
|
||||
dependencies = [
|
||||
"colored",
|
||||
"libc",
|
||||
@@ -406,9 +382,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "codspeed-criterion-compat"
|
||||
version = "2.4.0"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02ae9de916d6315a5129bca2fc7957285f0b9f77a2f6a8734a0a146caee2b0b6"
|
||||
checksum = "cb377d73b85084b1ca5615e42fdbf3b52631d6d8feda4be6dfc2150646f53cea"
|
||||
dependencies = [
|
||||
"codspeed",
|
||||
"colored",
|
||||
@@ -602,7 +578,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim 0.10.0",
|
||||
"syn 2.0.58",
|
||||
"syn 2.0.59",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -613,7 +589,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
"syn 2.0.59",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -640,16 +616,6 @@ dependencies = [
|
||||
"dirs-sys 0.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-next"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"dirs-sys-next",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.3.7"
|
||||
@@ -673,17 +639,6 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys-next"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "drop_bomb"
|
||||
version = "0.1.5"
|
||||
@@ -702,15 +657,6 @@ version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
|
||||
|
||||
[[package]]
|
||||
name = "ena"
|
||||
version = "0.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1"
|
||||
dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "0.3.6"
|
||||
@@ -783,12 +729,6 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.28"
|
||||
@@ -1078,9 +1018,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "insta-cmd"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1980f17994b79f75670aa90cfc8d35edc4aa248f16aa48b5e27835b080e452a2"
|
||||
checksum = "ffeeefa927925cced49ccb01bf3e57c9d4cd132df21e576eb9415baeab2d3de6"
|
||||
dependencies = [
|
||||
"insta",
|
||||
"serde",
|
||||
@@ -1114,7 +1054,7 @@ dependencies = [
|
||||
"Inflector",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
"syn 2.0.59",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1147,15 +1087,6 @@ dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.1"
|
||||
@@ -1206,33 +1137,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lalrpop"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca"
|
||||
dependencies = [
|
||||
"ascii-canvas",
|
||||
"bit-set",
|
||||
"ena",
|
||||
"itertools 0.11.0",
|
||||
"lalrpop-util",
|
||||
"petgraph",
|
||||
"regex",
|
||||
"regex-syntax 0.8.2",
|
||||
"string_cache",
|
||||
"term",
|
||||
"tiny-keccak",
|
||||
"unicode-xid",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lalrpop-util"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
@@ -1297,7 +1201,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5011f2d59093de14a4a90e01b9d85dee9276e58a25f0107dcee167dd601be0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
"syn 2.0.59",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1333,16 +1237,6 @@ version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.21"
|
||||
@@ -1437,12 +1331,6 @@ version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "308d96db8debc727c3fd9744aac51751243420e46edf401010908da7f8d5e57c"
|
||||
|
||||
[[package]]
|
||||
name = "new_debug_unreachable"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.28.0"
|
||||
@@ -1561,29 +1449,6 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.14"
|
||||
@@ -1655,9 +1520,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pep440_rs"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15efd4d885c29126cc93e12af3087896e2518bd5ca0fb328c19c4ef9cecfa8be"
|
||||
checksum = "ca0a570e7ec9171250cac57614e901f62408094b54b3798bb920d3cf0d4a0e09"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"serde",
|
||||
@@ -1687,23 +1552,13 @@ version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
||||
dependencies = [
|
||||
"phf_shared 0.11.2",
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1713,7 +1568,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared 0.11.2",
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1722,19 +1577,10 @@ version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
|
||||
dependencies = [
|
||||
"phf_shared 0.11.2",
|
||||
"phf_shared",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.11.2"
|
||||
@@ -1758,7 +1604,7 @@ checksum = "52a40bc70c2c58040d2d8b167ba9a5ff59fc9dab7ad44771cfde3dcfde7a09c6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
"syn 2.0.59",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1773,12 +1619,6 @@ version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "precomputed-hash"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||
|
||||
[[package]]
|
||||
name = "pretty_assertions"
|
||||
version = "1.4.0"
|
||||
@@ -1791,9 +1631,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.79"
|
||||
version = "1.0.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
|
||||
checksum = "a56dea16b0a29e94408b9aa5e2940a4eedbd128a1ba20e8f7ae60fd3d465af0e"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -1836,9 +1676,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.35"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -1975,7 +1815,7 @@ dependencies = [
|
||||
"pmutil",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
"syn 2.0.59",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1995,7 +1835,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.3.7"
|
||||
version = "0.4.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argfile",
|
||||
@@ -2157,7 +1997,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_linter"
|
||||
version = "0.3.7"
|
||||
version = "0.4.1"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"annotate-snippets 0.9.2",
|
||||
@@ -2181,7 +2021,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"path-absolutize",
|
||||
"pathdiff",
|
||||
"pep440_rs 0.5.0",
|
||||
"pep440_rs 0.6.0",
|
||||
"pyproject-toml",
|
||||
"quick-junit",
|
||||
"regex",
|
||||
@@ -2225,7 +2065,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"ruff_python_trivia",
|
||||
"syn 2.0.58",
|
||||
"syn 2.0.59",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2336,22 +2176,23 @@ dependencies = [
|
||||
name = "ruff_python_parser"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.2",
|
||||
"anyhow",
|
||||
"bitflags 2.5.0",
|
||||
"bstr",
|
||||
"insta",
|
||||
"is-macro",
|
||||
"itertools 0.12.1",
|
||||
"lalrpop",
|
||||
"lalrpop-util",
|
||||
"memchr",
|
||||
"ruff_python_ast",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"rustc-hash",
|
||||
"static_assertions",
|
||||
"tiny-keccak",
|
||||
"unicode-ident",
|
||||
"unicode-normalization",
|
||||
"unicode_names2",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2425,11 +2266,12 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tracing",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_shrinking"
|
||||
version = "0.3.7"
|
||||
version = "0.4.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -2502,7 +2344,7 @@ dependencies = [
|
||||
"itertools 0.12.1",
|
||||
"log",
|
||||
"path-absolutize",
|
||||
"pep440_rs 0.5.0",
|
||||
"pep440_rs 0.6.0",
|
||||
"regex",
|
||||
"ruff_cache",
|
||||
"ruff_formatter",
|
||||
@@ -2631,12 +2473,6 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "seahash"
|
||||
version = "4.1.0"
|
||||
@@ -2671,7 +2507,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
"syn 2.0.59",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2704,7 +2540,7 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
"syn 2.0.59",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2745,7 +2581,7 @@ dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
"syn 2.0.59",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2802,19 +2638,6 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "string_cache"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b"
|
||||
dependencies = [
|
||||
"new_debug_unreachable",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"phf_shared 0.10.0",
|
||||
"precomputed-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strip-ansi-escapes"
|
||||
version = "0.2.0"
|
||||
@@ -2855,7 +2678,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.58",
|
||||
"syn 2.0.59",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2877,9 +2700,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.58"
|
||||
version = "2.0.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687"
|
||||
checksum = "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2898,17 +2721,6 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "term"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f"
|
||||
dependencies = [
|
||||
"dirs-next",
|
||||
"rustversion",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.3.0"
|
||||
@@ -2950,7 +2762,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
"syn 2.0.59",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2961,7 +2773,7 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
"syn 2.0.59",
|
||||
"test-case-core",
|
||||
]
|
||||
|
||||
@@ -2982,7 +2794,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
"syn 2.0.59",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3015,15 +2827,6 @@ dependencies = [
|
||||
"tikv-jemalloc-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-keccak"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
|
||||
dependencies = [
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinytemplate"
|
||||
version = "1.2.1"
|
||||
@@ -3103,7 +2906,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
"syn 2.0.59",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3244,12 +3047,6 @@ version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode_names2"
|
||||
version = "1.2.2"
|
||||
@@ -3339,7 +3136,7 @@ checksum = "9881bea7cbe687e36c9ab3b778c36cd0487402e270304e8b1296d5085303c1a2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
"syn 2.0.59",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3424,7 +3221,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
"syn 2.0.59",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -3458,7 +3255,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
"syn 2.0.59",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -3491,7 +3288,7 @@ checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
"syn 2.0.59",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3753,7 +3550,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
"syn 2.0.59",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -15,7 +15,7 @@ license = "MIT"
|
||||
aho-corasick = { version = "1.1.3" }
|
||||
annotate-snippets = { version = "0.9.2", features = ["color"] }
|
||||
anyhow = { version = "1.0.80" }
|
||||
argfile = { version = "0.1.6" }
|
||||
argfile = { version = "0.2.0" }
|
||||
bincode = { version = "1.3.3" }
|
||||
bitflags = { version = "2.5.0" }
|
||||
bstr = { version = "1.9.1" }
|
||||
@@ -46,13 +46,12 @@ imperative = { version = "1.0.4" }
|
||||
indicatif = { version = "0.17.8" }
|
||||
indoc = { version = "2.0.4" }
|
||||
insta = { version = "1.35.1", feature = ["filters", "glob"] }
|
||||
insta-cmd = { version = "0.5.0" }
|
||||
insta-cmd = { version = "0.6.0" }
|
||||
is-macro = { version = "0.3.5" }
|
||||
is-wsl = { version = "0.4.0" }
|
||||
itertools = { version = "0.12.1" }
|
||||
js-sys = { version = "0.3.69" }
|
||||
jod-thread = { version = "0.1.2" }
|
||||
lalrpop-util = { version = "0.20.0", default-features = false }
|
||||
lexical-parse-float = { version = "0.8.0", features = ["format"] }
|
||||
libc = { version = "0.2.153" }
|
||||
libcst = { version = "1.1.0", default-features = false }
|
||||
@@ -67,7 +66,7 @@ num_cpus = { version = "1.16.0" }
|
||||
once_cell = { version = "1.19.0" }
|
||||
path-absolutize = { version = "3.1.1" }
|
||||
pathdiff = { version = "0.2.1" }
|
||||
pep440_rs = { version = "0.5.0", features = ["serde"] }
|
||||
pep440_rs = { version = "0.6.0", features = ["serde"] }
|
||||
pretty_assertions = "1.3.0"
|
||||
proc-macro2 = { version = "1.0.79" }
|
||||
pyproject-toml = { version = "0.9.0" }
|
||||
|
||||
@@ -151,7 +151,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff
|
||||
```yaml
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.3.7
|
||||
rev: v0.4.1
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
|
||||
@@ -11,3 +11,9 @@ ned = "ned"
|
||||
pn = "pn" # `import panel as pd` is a thing
|
||||
poit = "poit"
|
||||
BA = "BA" # acronym for "Bad Allowed", used in testing.
|
||||
|
||||
[default]
|
||||
extend-ignore-re = [
|
||||
# Line ignore with trailing "spellchecker:disable-line"
|
||||
"(?Rm)^.*#\\s*spellchecker:disable-line$"
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.3.7"
|
||||
version = "0.4.1"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
|
||||
@@ -190,7 +190,7 @@ pub struct CheckCommand {
|
||||
pub output_format: Option<SerializationFormat>,
|
||||
|
||||
/// Specify file to write the linter output to (default: stdout).
|
||||
#[arg(short, long)]
|
||||
#[arg(short, long, env = "RUFF_OUTPUT_FILE")]
|
||||
pub output_file: Option<PathBuf>,
|
||||
/// The minimum Python version that should be supported.
|
||||
#[arg(long, value_enum)]
|
||||
|
||||
@@ -523,7 +523,7 @@ from module import =
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
error: Failed to parse main.py:2:20: Unexpected token '='
|
||||
error: Failed to parse main.py:2:20: Expected an import name
|
||||
"###);
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -731,11 +731,11 @@ fn stdin_parse_error() {
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:17: E999 SyntaxError: Unexpected token '='
|
||||
-:1:17: E999 SyntaxError: Expected an import name
|
||||
Found 1 error.
|
||||
|
||||
----- stderr -----
|
||||
error: Failed to parse at 1:17: Unexpected token '='
|
||||
error: Failed to parse at 1:17: Expected an import name
|
||||
"###);
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ file_resolver.extend_exclude = [
|
||||
"crates/ruff/resources/",
|
||||
"crates/ruff_linter/resources/",
|
||||
"crates/ruff_python_formatter/resources/",
|
||||
"crates/ruff_python_parser/resources/",
|
||||
]
|
||||
file_resolver.force_exclude = false
|
||||
file_resolver.include = [
|
||||
@@ -68,128 +69,128 @@ file_resolver.project_root = "[BASEPATH]"
|
||||
linter.exclude = []
|
||||
linter.project_root = "[BASEPATH]"
|
||||
linter.rules.enabled = [
|
||||
MultipleImportsOnOneLine,
|
||||
ModuleImportNotAtTopOfFile,
|
||||
MultipleStatementsOnOneLineColon,
|
||||
MultipleStatementsOnOneLineSemicolon,
|
||||
UselessSemicolon,
|
||||
NoneComparison,
|
||||
TrueFalseComparison,
|
||||
NotInTest,
|
||||
NotIsTest,
|
||||
TypeComparison,
|
||||
BareExcept,
|
||||
LambdaAssignment,
|
||||
AmbiguousVariableName,
|
||||
AmbiguousClassName,
|
||||
AmbiguousFunctionName,
|
||||
IOError,
|
||||
SyntaxError,
|
||||
UnusedImport,
|
||||
ImportShadowedByLoopVar,
|
||||
UndefinedLocalWithImportStar,
|
||||
LateFutureImport,
|
||||
UndefinedLocalWithImportStarUsage,
|
||||
UndefinedLocalWithNestedImportStarUsage,
|
||||
FutureFeatureNotDefined,
|
||||
PercentFormatInvalidFormat,
|
||||
PercentFormatExpectedMapping,
|
||||
PercentFormatExpectedSequence,
|
||||
PercentFormatExtraNamedArguments,
|
||||
PercentFormatMissingArgument,
|
||||
PercentFormatMixedPositionalAndNamed,
|
||||
PercentFormatPositionalCountMismatch,
|
||||
PercentFormatStarRequiresSequence,
|
||||
PercentFormatUnsupportedFormatCharacter,
|
||||
StringDotFormatInvalidFormat,
|
||||
StringDotFormatExtraNamedArguments,
|
||||
StringDotFormatExtraPositionalArguments,
|
||||
StringDotFormatMissingArguments,
|
||||
StringDotFormatMixingAutomatic,
|
||||
FStringMissingPlaceholders,
|
||||
MultiValueRepeatedKeyLiteral,
|
||||
MultiValueRepeatedKeyVariable,
|
||||
ExpressionsInStarAssignment,
|
||||
MultipleStarredExpressions,
|
||||
AssertTuple,
|
||||
IsLiteral,
|
||||
InvalidPrintSyntax,
|
||||
IfTuple,
|
||||
BreakOutsideLoop,
|
||||
ContinueOutsideLoop,
|
||||
YieldOutsideFunction,
|
||||
ReturnOutsideFunction,
|
||||
DefaultExceptNotLast,
|
||||
ForwardAnnotationSyntaxError,
|
||||
RedefinedWhileUnused,
|
||||
UndefinedName,
|
||||
UndefinedExport,
|
||||
UndefinedLocal,
|
||||
UnusedVariable,
|
||||
UnusedAnnotation,
|
||||
RaiseNotImplemented,
|
||||
multiple-imports-on-one-line (E401),
|
||||
module-import-not-at-top-of-file (E402),
|
||||
multiple-statements-on-one-line-colon (E701),
|
||||
multiple-statements-on-one-line-semicolon (E702),
|
||||
useless-semicolon (E703),
|
||||
none-comparison (E711),
|
||||
true-false-comparison (E712),
|
||||
not-in-test (E713),
|
||||
not-is-test (E714),
|
||||
type-comparison (E721),
|
||||
bare-except (E722),
|
||||
lambda-assignment (E731),
|
||||
ambiguous-variable-name (E741),
|
||||
ambiguous-class-name (E742),
|
||||
ambiguous-function-name (E743),
|
||||
io-error (E902),
|
||||
syntax-error (E999),
|
||||
unused-import (F401),
|
||||
import-shadowed-by-loop-var (F402),
|
||||
undefined-local-with-import-star (F403),
|
||||
late-future-import (F404),
|
||||
undefined-local-with-import-star-usage (F405),
|
||||
undefined-local-with-nested-import-star-usage (F406),
|
||||
future-feature-not-defined (F407),
|
||||
percent-format-invalid-format (F501),
|
||||
percent-format-expected-mapping (F502),
|
||||
percent-format-expected-sequence (F503),
|
||||
percent-format-extra-named-arguments (F504),
|
||||
percent-format-missing-argument (F505),
|
||||
percent-format-mixed-positional-and-named (F506),
|
||||
percent-format-positional-count-mismatch (F507),
|
||||
percent-format-star-requires-sequence (F508),
|
||||
percent-format-unsupported-format-character (F509),
|
||||
string-dot-format-invalid-format (F521),
|
||||
string-dot-format-extra-named-arguments (F522),
|
||||
string-dot-format-extra-positional-arguments (F523),
|
||||
string-dot-format-missing-arguments (F524),
|
||||
string-dot-format-mixing-automatic (F525),
|
||||
f-string-missing-placeholders (F541),
|
||||
multi-value-repeated-key-literal (F601),
|
||||
multi-value-repeated-key-variable (F602),
|
||||
expressions-in-star-assignment (F621),
|
||||
multiple-starred-expressions (F622),
|
||||
assert-tuple (F631),
|
||||
is-literal (F632),
|
||||
invalid-print-syntax (F633),
|
||||
if-tuple (F634),
|
||||
break-outside-loop (F701),
|
||||
continue-outside-loop (F702),
|
||||
yield-outside-function (F704),
|
||||
return-outside-function (F706),
|
||||
default-except-not-last (F707),
|
||||
forward-annotation-syntax-error (F722),
|
||||
redefined-while-unused (F811),
|
||||
undefined-name (F821),
|
||||
undefined-export (F822),
|
||||
undefined-local (F823),
|
||||
unused-variable (F841),
|
||||
unused-annotation (F842),
|
||||
raise-not-implemented (F901),
|
||||
]
|
||||
linter.rules.should_fix = [
|
||||
MultipleImportsOnOneLine,
|
||||
ModuleImportNotAtTopOfFile,
|
||||
MultipleStatementsOnOneLineColon,
|
||||
MultipleStatementsOnOneLineSemicolon,
|
||||
UselessSemicolon,
|
||||
NoneComparison,
|
||||
TrueFalseComparison,
|
||||
NotInTest,
|
||||
NotIsTest,
|
||||
TypeComparison,
|
||||
BareExcept,
|
||||
LambdaAssignment,
|
||||
AmbiguousVariableName,
|
||||
AmbiguousClassName,
|
||||
AmbiguousFunctionName,
|
||||
IOError,
|
||||
SyntaxError,
|
||||
UnusedImport,
|
||||
ImportShadowedByLoopVar,
|
||||
UndefinedLocalWithImportStar,
|
||||
LateFutureImport,
|
||||
UndefinedLocalWithImportStarUsage,
|
||||
UndefinedLocalWithNestedImportStarUsage,
|
||||
FutureFeatureNotDefined,
|
||||
PercentFormatInvalidFormat,
|
||||
PercentFormatExpectedMapping,
|
||||
PercentFormatExpectedSequence,
|
||||
PercentFormatExtraNamedArguments,
|
||||
PercentFormatMissingArgument,
|
||||
PercentFormatMixedPositionalAndNamed,
|
||||
PercentFormatPositionalCountMismatch,
|
||||
PercentFormatStarRequiresSequence,
|
||||
PercentFormatUnsupportedFormatCharacter,
|
||||
StringDotFormatInvalidFormat,
|
||||
StringDotFormatExtraNamedArguments,
|
||||
StringDotFormatExtraPositionalArguments,
|
||||
StringDotFormatMissingArguments,
|
||||
StringDotFormatMixingAutomatic,
|
||||
FStringMissingPlaceholders,
|
||||
MultiValueRepeatedKeyLiteral,
|
||||
MultiValueRepeatedKeyVariable,
|
||||
ExpressionsInStarAssignment,
|
||||
MultipleStarredExpressions,
|
||||
AssertTuple,
|
||||
IsLiteral,
|
||||
InvalidPrintSyntax,
|
||||
IfTuple,
|
||||
BreakOutsideLoop,
|
||||
ContinueOutsideLoop,
|
||||
YieldOutsideFunction,
|
||||
ReturnOutsideFunction,
|
||||
DefaultExceptNotLast,
|
||||
ForwardAnnotationSyntaxError,
|
||||
RedefinedWhileUnused,
|
||||
UndefinedName,
|
||||
UndefinedExport,
|
||||
UndefinedLocal,
|
||||
UnusedVariable,
|
||||
UnusedAnnotation,
|
||||
RaiseNotImplemented,
|
||||
multiple-imports-on-one-line (E401),
|
||||
module-import-not-at-top-of-file (E402),
|
||||
multiple-statements-on-one-line-colon (E701),
|
||||
multiple-statements-on-one-line-semicolon (E702),
|
||||
useless-semicolon (E703),
|
||||
none-comparison (E711),
|
||||
true-false-comparison (E712),
|
||||
not-in-test (E713),
|
||||
not-is-test (E714),
|
||||
type-comparison (E721),
|
||||
bare-except (E722),
|
||||
lambda-assignment (E731),
|
||||
ambiguous-variable-name (E741),
|
||||
ambiguous-class-name (E742),
|
||||
ambiguous-function-name (E743),
|
||||
io-error (E902),
|
||||
syntax-error (E999),
|
||||
unused-import (F401),
|
||||
import-shadowed-by-loop-var (F402),
|
||||
undefined-local-with-import-star (F403),
|
||||
late-future-import (F404),
|
||||
undefined-local-with-import-star-usage (F405),
|
||||
undefined-local-with-nested-import-star-usage (F406),
|
||||
future-feature-not-defined (F407),
|
||||
percent-format-invalid-format (F501),
|
||||
percent-format-expected-mapping (F502),
|
||||
percent-format-expected-sequence (F503),
|
||||
percent-format-extra-named-arguments (F504),
|
||||
percent-format-missing-argument (F505),
|
||||
percent-format-mixed-positional-and-named (F506),
|
||||
percent-format-positional-count-mismatch (F507),
|
||||
percent-format-star-requires-sequence (F508),
|
||||
percent-format-unsupported-format-character (F509),
|
||||
string-dot-format-invalid-format (F521),
|
||||
string-dot-format-extra-named-arguments (F522),
|
||||
string-dot-format-extra-positional-arguments (F523),
|
||||
string-dot-format-missing-arguments (F524),
|
||||
string-dot-format-mixing-automatic (F525),
|
||||
f-string-missing-placeholders (F541),
|
||||
multi-value-repeated-key-literal (F601),
|
||||
multi-value-repeated-key-variable (F602),
|
||||
expressions-in-star-assignment (F621),
|
||||
multiple-starred-expressions (F622),
|
||||
assert-tuple (F631),
|
||||
is-literal (F632),
|
||||
invalid-print-syntax (F633),
|
||||
if-tuple (F634),
|
||||
break-outside-loop (F701),
|
||||
continue-outside-loop (F702),
|
||||
yield-outside-function (F704),
|
||||
return-outside-function (F706),
|
||||
default-except-not-last (F707),
|
||||
forward-annotation-syntax-error (F722),
|
||||
redefined-while-unused (F811),
|
||||
undefined-name (F821),
|
||||
undefined-export (F822),
|
||||
undefined-local (F823),
|
||||
unused-variable (F841),
|
||||
unused-annotation (F842),
|
||||
raise-not-implemented (F901),
|
||||
]
|
||||
linter.per_file_ignores = {}
|
||||
linter.safety_table.forced_safe = []
|
||||
|
||||
@@ -65,7 +65,7 @@ use seahash::SeaHasher;
|
||||
/// The main reason is that hashes and cache keys have different constraints:
|
||||
///
|
||||
/// * Cache keys are less performance sensitive: Hashes must be super fast to compute for performant hashed-collections. That's
|
||||
/// why some standard types don't implement [`Hash`] where it would be safe to to implement [`CacheKey`], e.g. `HashSet`
|
||||
/// why some standard types don't implement [`Hash`] where it would be safe to implement [`CacheKey`], e.g. `HashSet`
|
||||
/// * Cache keys must be deterministic where hash keys do not have this constraint. That's why pointers don't implement [`CacheKey`] but they implement [`Hash`].
|
||||
/// * Ideally, cache keys are portable
|
||||
///
|
||||
|
||||
@@ -138,7 +138,7 @@ pub const fn empty_line() -> Line {
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// The line breaks are emitted as spaces if the enclosing `Group` fits on a a single line:
|
||||
/// The line breaks are emitted as spaces if the enclosing `Group` fits on a single line:
|
||||
/// ```
|
||||
/// use ruff_formatter::{format, format_args};
|
||||
/// use ruff_formatter::prelude::*;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_linter"
|
||||
version = "0.3.7"
|
||||
version = "0.4.1"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
|
||||
@@ -17,3 +17,9 @@ urllib.request.URLopener().open(fullurl='http://www.google.com')
|
||||
urllib.request.URLopener().open('http://www.google.com')
|
||||
urllib.request.URLopener().open('file:///foo/bar/baz')
|
||||
urllib.request.URLopener().open(url)
|
||||
|
||||
urllib.request.urlopen(url=urllib.request.Request('http://www.google.com'))
|
||||
urllib.request.urlopen(url=urllib.request.Request('http://www.google.com'), **kwargs)
|
||||
urllib.request.urlopen(urllib.request.Request('http://www.google.com'))
|
||||
urllib.request.urlopen(urllib.request.Request('file:///foo/bar/baz'))
|
||||
urllib.request.urlopen(urllib.request.Request(url))
|
||||
|
||||
@@ -6,6 +6,25 @@ def this_is_a_bug():
|
||||
print("Ooh, callable! Or is it?")
|
||||
|
||||
|
||||
def still_a_bug():
|
||||
import builtins
|
||||
o = object()
|
||||
if builtins.hasattr(o, "__call__"):
|
||||
print("B U G")
|
||||
if builtins.getattr(o, "__call__", False):
|
||||
print("B U G")
|
||||
|
||||
|
||||
def trickier_fix_for_this_one():
|
||||
o = object()
|
||||
|
||||
def callable(x):
|
||||
return True
|
||||
|
||||
if hasattr(o, "__call__"):
|
||||
print("STILL a bug!")
|
||||
|
||||
|
||||
def this_is_fine():
|
||||
o = object()
|
||||
if callable(o):
|
||||
|
||||
20
crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_8.py
vendored
Normal file
20
crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_8.py
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
def foo(a: list = []):
|
||||
raise NotImplementedError("")
|
||||
|
||||
|
||||
def bar(a: dict = {}):
|
||||
""" This one also has a docstring"""
|
||||
raise NotImplementedError("and has some text in here")
|
||||
|
||||
|
||||
def baz(a: list = []):
|
||||
"""This one raises a different exception"""
|
||||
raise IndexError()
|
||||
|
||||
|
||||
def qux(a: list = []):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def quux(a: list = []):
|
||||
raise NotImplemented
|
||||
@@ -23,3 +23,15 @@ def okay(data: custom.ImmutableTypeA = foo()):
|
||||
|
||||
def error_due_to_missing_import(data: List[str] = Depends(None)):
|
||||
...
|
||||
|
||||
|
||||
class Class:
|
||||
pass
|
||||
|
||||
|
||||
def okay(obj=Class()):
|
||||
...
|
||||
|
||||
|
||||
def error(obj=OtherClass()):
|
||||
...
|
||||
|
||||
@@ -64,3 +64,6 @@ setattr(*foo, "bar", None)
|
||||
# Regression test for: https://github.com/astral-sh/ruff/issues/7455#issuecomment-1739800901
|
||||
getattr(self.
|
||||
registration.registry, '__name__')
|
||||
|
||||
import builtins
|
||||
builtins.getattr(foo, "bar")
|
||||
|
||||
@@ -23,3 +23,7 @@ zip([1, 2, 3], repeat(1, times=None))
|
||||
# Errors (limited iterators).
|
||||
zip([1, 2, 3], repeat(1, 1))
|
||||
zip([1, 2, 3], repeat(1, times=4))
|
||||
|
||||
import builtins
|
||||
# Still an error even though it uses the qualified name
|
||||
builtins.zip([1, 2, 3])
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
# PIE808
|
||||
range(0, 10)
|
||||
|
||||
import builtins
|
||||
builtins.range(0, 10)
|
||||
|
||||
# OK
|
||||
range(x, 10)
|
||||
range(-15, 10)
|
||||
|
||||
@@ -73,3 +73,10 @@ class BadFive:
|
||||
class BadSix:
|
||||
def __exit__(self, typ, exc, tb, weird_extra_arg, extra_arg2 = None) -> None: ... # PYI036: Extra arg must have default
|
||||
async def __aexit__(self, typ, exc, tb, *, weird_extra_arg) -> None: ... # PYI036: kwargs must have default
|
||||
|
||||
|
||||
def isolated_scope():
|
||||
from builtins import type as Type
|
||||
|
||||
class ShouldNotError:
|
||||
def __exit__(self, typ: Type[BaseException] | None, exc: BaseException | None, tb: TracebackType | None) -> None: ...
|
||||
|
||||
@@ -58,3 +58,8 @@ for key in (
|
||||
.keys()
|
||||
):
|
||||
continue
|
||||
|
||||
from builtins import dict as SneakyDict
|
||||
|
||||
d = SneakyDict()
|
||||
key in d.keys() # SIM118
|
||||
|
||||
@@ -11,3 +11,11 @@ from enum import Enum
|
||||
|
||||
class Fine(str, Enum): # Ok
|
||||
__slots__ = ["foo"]
|
||||
|
||||
|
||||
class SubEnum(Enum):
|
||||
pass
|
||||
|
||||
|
||||
class Ok(str, SubEnum): # Ok
|
||||
pass
|
||||
|
||||
@@ -19,3 +19,9 @@ class Bad(Tuple[str, int, float]): # SLOT001
|
||||
|
||||
class Good(Tuple[str, int, float]): # OK
|
||||
__slots__ = ("foo",)
|
||||
|
||||
|
||||
import builtins
|
||||
|
||||
class AlsoBad(builtins.tuple[int, int]): # SLOT001
|
||||
pass
|
||||
|
||||
@@ -80,3 +80,8 @@ for i in list(foo_list): # OK
|
||||
for i in list(foo_list): # OK
|
||||
if True:
|
||||
del foo_list[i + 1]
|
||||
|
||||
import builtins
|
||||
|
||||
for i in builtins.list(nested_tuple): # PERF101
|
||||
pass
|
||||
|
||||
@@ -81,3 +81,11 @@ def foo():
|
||||
result = {}
|
||||
for idx, name in enumerate(fruit):
|
||||
result[name] = idx # PERF403
|
||||
|
||||
|
||||
def foo():
|
||||
from builtins import dict as SneakyDict
|
||||
fruit = ["apple", "pear", "orange"]
|
||||
result = SneakyDict()
|
||||
for idx, name in enumerate(fruit):
|
||||
result[name] = idx # PERF403
|
||||
|
||||
@@ -445,6 +445,39 @@ def test():
|
||||
# end
|
||||
|
||||
|
||||
# no error
|
||||
class Foo:
|
||||
"""Demo."""
|
||||
|
||||
@overload
|
||||
def bar(self, x: int) -> int: ...
|
||||
@overload
|
||||
def bar(self, x: str) -> str: ...
|
||||
def bar(self, x: int | str) -> int | str:
|
||||
return x
|
||||
# end
|
||||
|
||||
|
||||
# no error
|
||||
@overload
|
||||
def foo(x: int) -> int: ...
|
||||
@overload
|
||||
def foo(x: str) -> str: ...
|
||||
def foo(x: int | str) -> int | str:
|
||||
if not isinstance(x, (int, str)):
|
||||
raise TypeError
|
||||
return x
|
||||
# end
|
||||
|
||||
|
||||
# no error
|
||||
def foo(self, x: int) -> int: ...
|
||||
def bar(self, x: str) -> str: ...
|
||||
def baz(self, x: int | str) -> int | str:
|
||||
return x
|
||||
# end
|
||||
|
||||
|
||||
# E301
|
||||
class Class(object):
|
||||
|
||||
@@ -489,6 +522,20 @@ class Class:
|
||||
# end
|
||||
|
||||
|
||||
# E301
|
||||
class Foo:
|
||||
"""Demo."""
|
||||
|
||||
@overload
|
||||
def bar(self, x: int) -> int: ...
|
||||
@overload
|
||||
def bar(self, x: str) -> str:
|
||||
...
|
||||
def bar(self, x: int | str) -> int | str:
|
||||
return x
|
||||
# end
|
||||
|
||||
|
||||
# E302
|
||||
"""Main module."""
|
||||
def fn():
|
||||
@@ -580,6 +627,23 @@ class Test:
|
||||
# end
|
||||
|
||||
|
||||
# E302
|
||||
class A:...
|
||||
class B: ...
|
||||
# end
|
||||
|
||||
|
||||
# E302
|
||||
@overload
|
||||
def fn(a: int) -> int: ...
|
||||
@overload
|
||||
def fn(a: str) -> str: ...
|
||||
|
||||
def fn(a: int | str) -> int | str:
|
||||
...
|
||||
# end
|
||||
|
||||
|
||||
# E303
|
||||
def fn():
|
||||
_ = None
|
||||
|
||||
@@ -138,3 +138,8 @@ np.dtype(int) == float
|
||||
|
||||
#: E721
|
||||
dtype == float
|
||||
|
||||
import builtins
|
||||
|
||||
if builtins.type(res) == memoryview: # E721
|
||||
pass
|
||||
|
||||
@@ -4,3 +4,8 @@ def f() -> None:
|
||||
|
||||
def g() -> None:
|
||||
raise NotImplemented
|
||||
|
||||
|
||||
def h() -> None:
|
||||
NotImplementedError = "foo"
|
||||
raise NotImplemented
|
||||
|
||||
@@ -32,3 +32,6 @@ pathlib.Path(NAME).open(mode)
|
||||
pathlib.Path(NAME).open("rwx") # [bad-open-mode]
|
||||
pathlib.Path(NAME).open(mode="rwx") # [bad-open-mode]
|
||||
pathlib.Path(NAME).open("rwx", encoding="utf-8") # [bad-open-mode]
|
||||
|
||||
import builtins
|
||||
builtins.open(NAME, "Ua", encoding="utf-8")
|
||||
|
||||
@@ -134,3 +134,28 @@ if value.attr > 3:
|
||||
value.
|
||||
attr
|
||||
) = 3
|
||||
|
||||
class Foo:
|
||||
_min = 0
|
||||
_max = 0
|
||||
|
||||
def foo(self, value) -> None:
|
||||
if value < self._min:
|
||||
self._min = value
|
||||
if value > self._max:
|
||||
self._max = value
|
||||
|
||||
if self._min < value:
|
||||
self._min = value
|
||||
if self._max > value:
|
||||
self._max = value
|
||||
|
||||
if value <= self._min:
|
||||
self._min = value
|
||||
if value >= self._max:
|
||||
self._max = value
|
||||
|
||||
if self._min <= value:
|
||||
self._min = value
|
||||
if self._max >= value:
|
||||
self._max = value
|
||||
|
||||
65
crates/ruff_linter/resources/test/fixtures/pylint/invalid_return_type_bytes.py
vendored
Normal file
65
crates/ruff_linter/resources/test/fixtures/pylint/invalid_return_type_bytes.py
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
# These testcases should raise errors
|
||||
|
||||
|
||||
class Float:
|
||||
def __bytes__(self):
|
||||
return 3.05 # [invalid-bytes-return]
|
||||
|
||||
|
||||
class Int:
|
||||
def __bytes__(self):
|
||||
return 0 # [invalid-bytes-return]
|
||||
|
||||
|
||||
class Str:
|
||||
def __bytes__(self):
|
||||
return "some bytes" # [invalid-bytes-return]
|
||||
|
||||
|
||||
class BytesNoReturn:
|
||||
def __bytes__(self):
|
||||
print("ruff") # [invalid-bytes-return]
|
||||
|
||||
|
||||
# TODO: Once Ruff has better type checking
|
||||
def return_bytes():
|
||||
return "some string"
|
||||
|
||||
|
||||
class ComplexReturn:
|
||||
def __bytes__(self):
|
||||
return return_bytes() # [invalid-bytes-return]
|
||||
|
||||
|
||||
# These testcases should NOT raise errors
|
||||
|
||||
|
||||
class Bytes:
|
||||
def __bytes__(self):
|
||||
return b"some bytes"
|
||||
|
||||
|
||||
class Bytes2:
|
||||
def __bytes__(self):
|
||||
x = b"some bytes"
|
||||
return x
|
||||
|
||||
|
||||
class Bytes3:
|
||||
def __bytes__(self): ...
|
||||
|
||||
|
||||
class Bytes4:
|
||||
def __bytes__(self):
|
||||
pass
|
||||
|
||||
|
||||
class Bytes5:
|
||||
def __bytes__(self):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class Bytes6:
|
||||
def __bytes__(self):
|
||||
print("raise some error")
|
||||
raise NotImplementedError
|
||||
65
crates/ruff_linter/resources/test/fixtures/pylint/invalid_return_type_hash.py
vendored
Normal file
65
crates/ruff_linter/resources/test/fixtures/pylint/invalid_return_type_hash.py
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
# These testcases should raise errors
|
||||
|
||||
|
||||
class Bool:
|
||||
def __hash__(self):
|
||||
return True # [invalid-hash-return]
|
||||
|
||||
|
||||
class Float:
|
||||
def __hash__(self):
|
||||
return 3.05 # [invalid-hash-return]
|
||||
|
||||
|
||||
class Str:
|
||||
def __hash__(self):
|
||||
return "ruff" # [invalid-hash-return]
|
||||
|
||||
|
||||
class HashNoReturn:
|
||||
def __hash__(self):
|
||||
print("ruff") # [invalid-hash-return]
|
||||
|
||||
|
||||
# TODO: Once Ruff has better type checking
|
||||
def return_int():
|
||||
return "3"
|
||||
|
||||
|
||||
class ComplexReturn:
|
||||
def __hash__(self):
|
||||
return return_int() # [invalid-hash-return]
|
||||
|
||||
|
||||
# These testcases should NOT raise errors
|
||||
|
||||
|
||||
class Hash:
|
||||
def __hash__(self):
|
||||
return 7741
|
||||
|
||||
|
||||
class Hash2:
|
||||
def __hash__(self):
|
||||
x = 7741
|
||||
return x
|
||||
|
||||
|
||||
class Hash3:
|
||||
def __hash__(self): ...
|
||||
|
||||
|
||||
class Has4:
|
||||
def __hash__(self):
|
||||
pass
|
||||
|
||||
|
||||
class Hash5:
|
||||
def __hash__(self):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class HashWrong6:
|
||||
def __hash__(self):
|
||||
print("raise some error")
|
||||
raise NotImplementedError
|
||||
73
crates/ruff_linter/resources/test/fixtures/pylint/invalid_return_type_index.py
vendored
Normal file
73
crates/ruff_linter/resources/test/fixtures/pylint/invalid_return_type_index.py
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
# These testcases should raise errors
|
||||
|
||||
|
||||
class Bool:
|
||||
"""pylint would not raise, but ruff does - see explanation in the docs"""
|
||||
|
||||
def __index__(self):
|
||||
return True # [invalid-index-return]
|
||||
|
||||
|
||||
class Float:
|
||||
def __index__(self):
|
||||
return 3.05 # [invalid-index-return]
|
||||
|
||||
|
||||
class Dict:
|
||||
def __index__(self):
|
||||
return {"1": "1"} # [invalid-index-return]
|
||||
|
||||
|
||||
class Str:
|
||||
def __index__(self):
|
||||
return "ruff" # [invalid-index-return]
|
||||
|
||||
|
||||
class IndexNoReturn:
|
||||
def __index__(self):
|
||||
print("ruff") # [invalid-index-return]
|
||||
|
||||
|
||||
# TODO: Once Ruff has better type checking
|
||||
def return_index():
|
||||
return "3"
|
||||
|
||||
|
||||
class ComplexReturn:
|
||||
def __index__(self):
|
||||
return return_index() # [invalid-index-return]
|
||||
|
||||
|
||||
# These testcases should NOT raise errors
|
||||
|
||||
|
||||
class Index:
|
||||
def __index__(self):
|
||||
return 0
|
||||
|
||||
|
||||
class Index2:
|
||||
def __index__(self):
|
||||
x = 1
|
||||
return x
|
||||
|
||||
|
||||
class Index3:
|
||||
def __index__(self):
|
||||
...
|
||||
|
||||
|
||||
class Index4:
|
||||
def __index__(self):
|
||||
pass
|
||||
|
||||
|
||||
class Index5:
|
||||
def __index__(self):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class Index6:
|
||||
def __index__(self):
|
||||
print("raise some error")
|
||||
raise NotImplementedError
|
||||
70
crates/ruff_linter/resources/test/fixtures/pylint/invalid_return_type_length.py
vendored
Normal file
70
crates/ruff_linter/resources/test/fixtures/pylint/invalid_return_type_length.py
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
# These testcases should raise errors
|
||||
|
||||
|
||||
class Bool:
|
||||
def __len__(self):
|
||||
return True # [invalid-length-return]
|
||||
|
||||
|
||||
class Float:
|
||||
def __len__(self):
|
||||
return 3.05 # [invalid-length-return]
|
||||
|
||||
|
||||
class Str:
|
||||
def __len__(self):
|
||||
return "ruff" # [invalid-length-return]
|
||||
|
||||
|
||||
class LengthNoReturn:
|
||||
def __len__(self):
|
||||
print("ruff") # [invalid-length-return]
|
||||
|
||||
|
||||
class LengthNegative:
|
||||
def __len__(self):
|
||||
return -42 # [invalid-length-return]
|
||||
|
||||
|
||||
# TODO: Once Ruff has better type checking
|
||||
def return_int():
|
||||
return "3"
|
||||
|
||||
|
||||
class ComplexReturn:
|
||||
def __len__(self):
|
||||
return return_int() # [invalid-length-return]
|
||||
|
||||
|
||||
# These testcases should NOT raise errors
|
||||
|
||||
|
||||
class Length:
|
||||
def __len__(self):
|
||||
return 42
|
||||
|
||||
|
||||
class Length2:
|
||||
def __len__(self):
|
||||
x = 42
|
||||
return x
|
||||
|
||||
|
||||
class Length3:
|
||||
def __len__(self): ...
|
||||
|
||||
|
||||
class Length4:
|
||||
def __len__(self):
|
||||
pass
|
||||
|
||||
|
||||
class Length5:
|
||||
def __len__(self):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class Length6:
|
||||
def __len__(self):
|
||||
print("raise some error")
|
||||
raise NotImplementedError
|
||||
@@ -1,36 +1,60 @@
|
||||
# These testcases should raise errors
|
||||
|
||||
|
||||
class Float:
|
||||
def __str__(self):
|
||||
return 3.05
|
||||
|
||||
|
||||
class Int:
|
||||
def __str__(self):
|
||||
return 1
|
||||
|
||||
|
||||
class Int2:
|
||||
def __str__(self):
|
||||
return 0
|
||||
|
||||
|
||||
class Bool:
|
||||
def __str__(self):
|
||||
return False
|
||||
|
||||
|
||||
# TODO: Once Ruff has better type checking
|
||||
def return_int():
|
||||
return 3
|
||||
|
||||
|
||||
class ComplexReturn:
|
||||
def __str__(self):
|
||||
return return_int()
|
||||
|
||||
|
||||
# These testcases should NOT raise errors
|
||||
|
||||
|
||||
class Str:
|
||||
def __str__(self):
|
||||
return "ruff"
|
||||
|
||||
|
||||
class Str2:
|
||||
def __str__(self):
|
||||
x = "ruff"
|
||||
return x
|
||||
|
||||
|
||||
class Str3:
|
||||
def __str__(self): ...
|
||||
|
||||
|
||||
class Str4:
|
||||
def __str__(self):
|
||||
raise RuntimeError("__str__ not allowed")
|
||||
|
||||
|
||||
class Str5:
|
||||
def __str__(self): # PLE0307 (returns None if x <= 0)
|
||||
if x > 0:
|
||||
raise RuntimeError("__str__ not allowed")
|
||||
|
||||
@@ -47,6 +47,12 @@ if y == np.nan:
|
||||
if y == npy_nan:
|
||||
pass
|
||||
|
||||
import builtins
|
||||
|
||||
# PLW0117
|
||||
if x == builtins.float("nan"):
|
||||
pass
|
||||
|
||||
# OK
|
||||
if math.isnan(x):
|
||||
pass
|
||||
|
||||
@@ -39,3 +39,6 @@ max(max(tuples_list))
|
||||
|
||||
# Starred argument should be copied as it is.
|
||||
max(1, max(*a))
|
||||
|
||||
import builtins
|
||||
builtins.min(1, min(2, 3))
|
||||
|
||||
@@ -18,6 +18,8 @@ index = index - 1
|
||||
a_list = a_list + ["to concat"]
|
||||
some_set = some_set | {"to concat"}
|
||||
to_multiply = to_multiply * 5
|
||||
to_multiply = 5 * to_multiply
|
||||
to_multiply = to_multiply * to_multiply
|
||||
to_divide = to_divide / 5
|
||||
to_divide = to_divide // 5
|
||||
to_cube = to_cube**3
|
||||
@@ -53,3 +55,4 @@ a_list[0] = a_list[:] * 3
|
||||
index = a_number = a_number + 1
|
||||
a_number = index = a_number + 1
|
||||
index = index * index + 10
|
||||
some_string = "a very long start to the string" + some_string
|
||||
|
||||
43
crates/ruff_linter/resources/test/fixtures/pylint/self_or_cls_assignment.py
vendored
Normal file
43
crates/ruff_linter/resources/test/fixtures/pylint/self_or_cls_assignment.py
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
class Fruit:
|
||||
@classmethod
|
||||
def list_fruits(cls) -> None:
|
||||
cls = "apple" # PLW0642
|
||||
cls: Fruit = "apple" # PLW0642
|
||||
cls += "orange" # PLW0642
|
||||
*cls = "banana" # PLW0642
|
||||
cls, blah = "apple", "orange" # PLW0642
|
||||
blah, (cls, blah2) = "apple", ("orange", "banana") # PLW0642
|
||||
blah, [cls, blah2] = "apple", ("orange", "banana") # PLW0642
|
||||
|
||||
@classmethod
|
||||
def add_fruits(cls, fruits, /) -> None:
|
||||
cls = fruits # PLW0642
|
||||
|
||||
def print_color(self) -> None:
|
||||
self = "red" # PLW0642
|
||||
self: Self = "red" # PLW0642
|
||||
self += "blue" # PLW0642
|
||||
*self = "blue" # PLW0642
|
||||
self, blah = "red", "blue" # PLW0642
|
||||
blah, (self, blah2) = "apple", ("orange", "banana") # PLW0642
|
||||
blah, [self, blah2] = "apple", ("orange", "banana") # PLW0642
|
||||
|
||||
def print_color(self, color, /) -> None:
|
||||
self = color
|
||||
|
||||
def ok(self) -> None:
|
||||
cls = None # OK because the rule looks for the name in the signature
|
||||
|
||||
@classmethod
|
||||
def ok(cls) -> None:
|
||||
self = None
|
||||
|
||||
@staticmethod
|
||||
def list_fruits_static(self, cls) -> None:
|
||||
self = "apple" # Ok
|
||||
cls = "banana" # Ok
|
||||
|
||||
|
||||
def list_fruits(self, cls) -> None:
|
||||
self = "apple" # Ok
|
||||
cls = "banana" # Ok
|
||||
@@ -152,3 +152,9 @@ object = A
|
||||
|
||||
class B(object):
|
||||
...
|
||||
|
||||
|
||||
import builtins
|
||||
|
||||
class Unusual(builtins.object):
|
||||
...
|
||||
|
||||
@@ -59,6 +59,21 @@ with open("file.txt", "w", newline="\r\n") as f:
|
||||
f.write(foobar)
|
||||
|
||||
|
||||
import builtins
|
||||
|
||||
|
||||
# FURB103
|
||||
with builtins.open("file.txt", "w", newline="\r\n") as f:
|
||||
f.write(foobar)
|
||||
|
||||
|
||||
from builtins import open as o
|
||||
|
||||
|
||||
# FURB103
|
||||
with o("file.txt", "w", newline="\r\n") as f:
|
||||
f.write(foobar)
|
||||
|
||||
# Non-errors.
|
||||
|
||||
with open("file.txt", errors="ignore", mode="wb") as f:
|
||||
|
||||
@@ -41,6 +41,22 @@ def func():
|
||||
pass
|
||||
|
||||
|
||||
import builtins
|
||||
|
||||
|
||||
with builtins.open("FURB129.py") as f:
|
||||
for line in f.readlines():
|
||||
pass
|
||||
|
||||
|
||||
from builtins import open as o
|
||||
|
||||
|
||||
with o("FURB129.py") as f:
|
||||
for line in f.readlines():
|
||||
pass
|
||||
|
||||
|
||||
# False positives
|
||||
def func(f):
|
||||
for _line in f.readlines():
|
||||
|
||||
@@ -49,6 +49,14 @@ def yes_five(x: Dict[int, str]):
|
||||
|
||||
x = 1
|
||||
|
||||
|
||||
from builtins import list as SneakyList
|
||||
|
||||
|
||||
sneaky = SneakyList()
|
||||
# FURB131
|
||||
del sneaky[:]
|
||||
|
||||
# these should not
|
||||
|
||||
del names["key"]
|
||||
|
||||
@@ -12,6 +12,8 @@ dict.fromkeys(pierogi_fillings, {})
|
||||
dict.fromkeys(pierogi_fillings, set())
|
||||
dict.fromkeys(pierogi_fillings, {"pre": "populated!"})
|
||||
dict.fromkeys(pierogi_fillings, dict())
|
||||
import builtins
|
||||
builtins.dict.fromkeys(pierogi_fillings, dict())
|
||||
|
||||
# Okay.
|
||||
dict.fromkeys(pierogi_fillings)
|
||||
|
||||
71
crates/ruff_linter/resources/test/fixtures/ruff/RUF029.py
vendored
Normal file
71
crates/ruff_linter/resources/test/fixtures/ruff/RUF029.py
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
import time
|
||||
import asyncio
|
||||
|
||||
|
||||
async def pass_1a(): # OK: awaits a coroutine
|
||||
await asyncio.sleep(1)
|
||||
|
||||
|
||||
async def pass_1b(): # OK: awaits a coroutine
|
||||
def foo(optional_arg=await bar()):
|
||||
pass
|
||||
|
||||
|
||||
async def pass_2(): # OK: uses an async context manager
|
||||
async with None as i:
|
||||
pass
|
||||
|
||||
|
||||
async def pass_3(): # OK: uses an async loop
|
||||
async for i in []:
|
||||
pass
|
||||
|
||||
|
||||
class Foo:
|
||||
async def pass_4(self): # OK: method of a class
|
||||
pass
|
||||
|
||||
|
||||
def foo():
|
||||
async def pass_5(): # OK: uses an await
|
||||
await bla
|
||||
|
||||
|
||||
async def pass_6(): # OK: just a stub
|
||||
...
|
||||
|
||||
|
||||
async def fail_1a(): # RUF029
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
async def fail_1b(): # RUF029: yield does not require async
|
||||
yield "hello"
|
||||
|
||||
|
||||
async def fail_2(): # RUF029
|
||||
with None as i:
|
||||
pass
|
||||
|
||||
|
||||
async def fail_3(): # RUF029
|
||||
for i in []:
|
||||
pass
|
||||
|
||||
return foo
|
||||
|
||||
|
||||
async def fail_4a(): # RUF029: the /outer/ function does not await
|
||||
async def foo():
|
||||
await bla
|
||||
|
||||
|
||||
async def fail_4b(): # RUF029: the /outer/ function does not await
|
||||
class Foo:
|
||||
async def foo(self):
|
||||
await bla
|
||||
|
||||
|
||||
def foo():
|
||||
async def fail_4c(): # RUF029: the /inner/ function does not await
|
||||
pass
|
||||
@@ -1,2 +1,3 @@
|
||||
import os
|
||||
import foo # noqa: F401
|
||||
import bar # noqa
|
||||
|
||||
@@ -247,7 +247,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
}
|
||||
ExprContext::Del => {}
|
||||
_ => {}
|
||||
}
|
||||
if checker.enabled(Rule::SixPY3) {
|
||||
flake8_2020::rules::name_or_attribute(checker, expr);
|
||||
|
||||
@@ -95,10 +95,22 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::InvalidBoolReturnType) {
|
||||
pylint::rules::invalid_bool_return(checker, name, body);
|
||||
pylint::rules::invalid_bool_return(checker, function_def);
|
||||
}
|
||||
if checker.enabled(Rule::InvalidLengthReturnType) {
|
||||
pylint::rules::invalid_length_return(checker, function_def);
|
||||
}
|
||||
if checker.enabled(Rule::InvalidBytesReturnType) {
|
||||
pylint::rules::invalid_bytes_return(checker, function_def);
|
||||
}
|
||||
if checker.enabled(Rule::InvalidIndexReturnType) {
|
||||
pylint::rules::invalid_index_return(checker, function_def);
|
||||
}
|
||||
if checker.enabled(Rule::InvalidHashReturnType) {
|
||||
pylint::rules::invalid_hash_return(checker, function_def);
|
||||
}
|
||||
if checker.enabled(Rule::InvalidStrReturnType) {
|
||||
pylint::rules::invalid_str_return(checker, name, body);
|
||||
pylint::rules::invalid_str_return(checker, function_def);
|
||||
}
|
||||
if checker.enabled(Rule::InvalidFunctionName) {
|
||||
if let Some(diagnostic) = pep8_naming::rules::invalid_function_name(
|
||||
@@ -146,7 +158,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
decorator_list,
|
||||
returns.as_ref().map(AsRef::as_ref),
|
||||
parameters,
|
||||
type_params.as_ref(),
|
||||
type_params.as_deref(),
|
||||
);
|
||||
}
|
||||
if checker.source_type.is_stub() {
|
||||
@@ -348,6 +360,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::SslWithBadDefaults) {
|
||||
flake8_bandit::rules::ssl_with_bad_defaults(checker, function_def);
|
||||
}
|
||||
if checker.enabled(Rule::UnusedAsync) {
|
||||
ruff::rules::unused_async(checker, function_def);
|
||||
}
|
||||
}
|
||||
Stmt::Return(_) => {
|
||||
if checker.enabled(Rule::ReturnOutsideFunction) {
|
||||
@@ -606,7 +621,8 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
pylint::rules::manual_from_import(checker, stmt, alias, names);
|
||||
}
|
||||
if checker.enabled(Rule::ImportSelf) {
|
||||
if let Some(diagnostic) = pylint::rules::import_self(alias, checker.module_path)
|
||||
if let Some(diagnostic) =
|
||||
pylint::rules::import_self(alias, checker.module.qualified_name())
|
||||
{
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
@@ -770,9 +786,11 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
flake8_bandit::rules::suspicious_imports(checker, stmt);
|
||||
}
|
||||
if checker.enabled(Rule::BannedApi) {
|
||||
if let Some(module) =
|
||||
helpers::resolve_imported_module_path(level, module, checker.module_path)
|
||||
{
|
||||
if let Some(module) = helpers::resolve_imported_module_path(
|
||||
level,
|
||||
module,
|
||||
checker.module.qualified_name(),
|
||||
) {
|
||||
flake8_tidy_imports::rules::banned_api(
|
||||
checker,
|
||||
&flake8_tidy_imports::matchers::NameMatchPolicy::MatchNameOrParent(
|
||||
@@ -799,9 +817,11 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::BannedModuleLevelImports) {
|
||||
if let Some(module) =
|
||||
helpers::resolve_imported_module_path(level, module, checker.module_path)
|
||||
{
|
||||
if let Some(module) = helpers::resolve_imported_module_path(
|
||||
level,
|
||||
module,
|
||||
checker.module.qualified_name(),
|
||||
) {
|
||||
flake8_tidy_imports::rules::banned_module_level_imports(
|
||||
checker,
|
||||
&flake8_tidy_imports::matchers::NameMatchPolicy::MatchNameOrParent(
|
||||
@@ -888,7 +908,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
stmt,
|
||||
level,
|
||||
module,
|
||||
checker.module_path,
|
||||
checker.module.qualified_name(),
|
||||
checker.settings.flake8_tidy_imports.ban_relative_imports,
|
||||
) {
|
||||
checker.diagnostics.push(diagnostic);
|
||||
@@ -987,9 +1007,12 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::ImportSelf) {
|
||||
if let Some(diagnostic) =
|
||||
pylint::rules::import_from_self(level, module, names, checker.module_path)
|
||||
{
|
||||
if let Some(diagnostic) = pylint::rules::import_from_self(
|
||||
level,
|
||||
module,
|
||||
names,
|
||||
checker.module.qualified_name(),
|
||||
) {
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
@@ -1055,6 +1078,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
Stmt::AugAssign(aug_assign @ ast::StmtAugAssign { target, .. }) => {
|
||||
if checker.enabled(Rule::SelfOrClsAssignment) {
|
||||
pylint::rules::self_or_cls_assignment(checker, target);
|
||||
}
|
||||
if checker.enabled(Rule::GlobalStatement) {
|
||||
if let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() {
|
||||
pylint::rules::global_statement(checker, id);
|
||||
@@ -1410,6 +1436,11 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
Stmt::Assign(assign @ ast::StmtAssign { targets, value, .. }) => {
|
||||
if checker.enabled(Rule::SelfOrClsAssignment) {
|
||||
for target in targets {
|
||||
pylint::rules::self_or_cls_assignment(checker, target);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::RedeclaredAssignedName) {
|
||||
pylint::rules::redeclared_assigned_name(checker, targets);
|
||||
}
|
||||
@@ -1547,6 +1578,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::SelfOrClsAssignment) {
|
||||
pylint::rules::self_or_cls_assignment(checker, target);
|
||||
}
|
||||
if checker.enabled(Rule::SelfAssigningVariable) {
|
||||
pylint::rules::self_annotated_assignment(checker, assign_stmt);
|
||||
}
|
||||
|
||||
@@ -33,4 +33,7 @@ pub(crate) fn string_like(string_like: StringLike, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::UnnecessaryEscapedQuote) {
|
||||
flake8_quotes::rules::unnecessary_escaped_quote(checker, string_like);
|
||||
}
|
||||
if checker.enabled(Rule::AvoidableEscapedQuote) && checker.settings.flake8_quotes.avoid_escape {
|
||||
flake8_quotes::rules::avoidable_escaped_quote(checker, string_like);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,11 +50,11 @@ use ruff_python_ast::{helpers, str, visitor, PySourceType};
|
||||
use ruff_python_codegen::{Generator, Stylist};
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_python_parser::typing::{parse_type_annotation, AnnotationKind};
|
||||
use ruff_python_semantic::analyze::{imports, typing, visibility};
|
||||
use ruff_python_semantic::analyze::{imports, typing};
|
||||
use ruff_python_semantic::{
|
||||
BindingFlags, BindingId, BindingKind, Exceptions, Export, FromImport, Globals, Import, Module,
|
||||
ModuleKind, NodeId, ScopeId, ScopeKind, SemanticModel, SemanticModelFlags, StarImport,
|
||||
SubmoduleImport,
|
||||
ModuleKind, ModuleSource, NodeId, ScopeId, ScopeKind, SemanticModel, SemanticModelFlags,
|
||||
StarImport, SubmoduleImport,
|
||||
};
|
||||
use ruff_python_stdlib::builtins::{IPYTHON_BUILTINS, MAGIC_GLOBALS, PYTHON_BUILTINS};
|
||||
use ruff_source_file::{Locator, OneIndexed, SourceRow};
|
||||
@@ -110,7 +110,7 @@ pub(crate) struct Checker<'a> {
|
||||
/// The [`Path`] to the package containing the current file.
|
||||
package: Option<&'a Path>,
|
||||
/// The module representation of the current file (e.g., `foo.bar`).
|
||||
module_path: Option<&'a [String]>,
|
||||
module: Module<'a>,
|
||||
/// The [`PySourceType`] of the current file.
|
||||
pub(crate) source_type: PySourceType,
|
||||
/// The [`CellOffsets`] for the current file, if it's a Jupyter notebook.
|
||||
@@ -174,7 +174,7 @@ impl<'a> Checker<'a> {
|
||||
noqa,
|
||||
path,
|
||||
package,
|
||||
module_path: module.path(),
|
||||
module,
|
||||
source_type,
|
||||
locator,
|
||||
stylist,
|
||||
@@ -784,17 +784,13 @@ impl<'a> Visitor<'a> for Checker<'a> {
|
||||
}) => {
|
||||
let mut handled_exceptions = Exceptions::empty();
|
||||
for type_ in extract_handled_exceptions(handlers) {
|
||||
if let Some(qualified_name) = self.semantic.resolve_qualified_name(type_) {
|
||||
match qualified_name.segments() {
|
||||
["", "NameError"] => {
|
||||
handled_exceptions |= Exceptions::NAME_ERROR;
|
||||
}
|
||||
["", "ModuleNotFoundError"] => {
|
||||
if let Some(builtins_name) = self.semantic.resolve_builtin_symbol(type_) {
|
||||
match builtins_name {
|
||||
"NameError" => handled_exceptions |= Exceptions::NAME_ERROR,
|
||||
"ModuleNotFoundError" => {
|
||||
handled_exceptions |= Exceptions::MODULE_NOT_FOUND_ERROR;
|
||||
}
|
||||
["", "ImportError"] => {
|
||||
handled_exceptions |= Exceptions::IMPORT_ERROR;
|
||||
}
|
||||
"ImportError" => handled_exceptions |= Exceptions::IMPORT_ERROR,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -1002,6 +998,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
|
||||
ExprContext::Load => self.handle_node_load(expr),
|
||||
ExprContext::Store => self.handle_node_store(id, expr),
|
||||
ExprContext::Del => self.handle_node_delete(expr),
|
||||
ExprContext::Invalid => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
@@ -1125,7 +1122,8 @@ impl<'a> Visitor<'a> for Checker<'a> {
|
||||
]
|
||||
) {
|
||||
Some(typing::Callable::MypyExtension)
|
||||
} else if matches!(qualified_name.segments(), ["", "bool"]) {
|
||||
} else if matches!(qualified_name.segments(), ["" | "builtins", "bool"])
|
||||
{
|
||||
Some(typing::Callable::Bool)
|
||||
} else {
|
||||
None
|
||||
@@ -1912,7 +1910,7 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
{
|
||||
let (all_names, all_flags) =
|
||||
extract_all_names(parent, |name| self.semantic.is_builtin(name));
|
||||
extract_all_names(parent, |name| self.semantic.has_builtin_binding(name));
|
||||
|
||||
if all_flags.intersects(DunderAllFlags::INVALID_OBJECT) {
|
||||
flags |= BindingFlags::INVALID_ALL_OBJECT;
|
||||
@@ -2285,10 +2283,15 @@ pub(crate) fn check_ast(
|
||||
} else {
|
||||
ModuleKind::Module
|
||||
},
|
||||
source: if let Some(module_path) = module_path.as_ref() {
|
||||
visibility::ModuleSource::Path(module_path)
|
||||
name: if let Some(module_path) = &module_path {
|
||||
module_path.last().map(String::as_str)
|
||||
} else {
|
||||
visibility::ModuleSource::File(path)
|
||||
path.file_stem().and_then(std::ffi::OsStr::to_str)
|
||||
},
|
||||
source: if let Some(module_path) = module_path.as_ref() {
|
||||
ModuleSource::Path(module_path)
|
||||
} else {
|
||||
ModuleSource::File(path)
|
||||
},
|
||||
python_ast,
|
||||
};
|
||||
|
||||
@@ -111,6 +111,7 @@ pub(crate) fn check_noqa(
|
||||
FileExemption::All => true,
|
||||
FileExemption::Codes(codes) => codes.contains(&Rule::UnusedNOQA.noqa_code()),
|
||||
})
|
||||
&& !per_file_ignores.contains(Rule::UnusedNOQA)
|
||||
{
|
||||
for line in noqa_directives.lines() {
|
||||
match &line.directive {
|
||||
@@ -129,7 +130,7 @@ pub(crate) fn check_noqa(
|
||||
let mut unknown_codes = vec![];
|
||||
let mut unmatched_codes = vec![];
|
||||
let mut valid_codes = vec![];
|
||||
let mut self_ignore = per_file_ignores.contains(Rule::UnusedNOQA);
|
||||
let mut self_ignore = false;
|
||||
for code in directive.codes() {
|
||||
let code = get_redirect_target(code).unwrap_or(code);
|
||||
if Rule::UnusedNOQA.noqa_code() == code {
|
||||
|
||||
@@ -16,7 +16,7 @@ use crate::registry::{AsRule, Rule};
|
||||
use crate::rules::pycodestyle::rules::BlankLinesChecker;
|
||||
use crate::rules::{
|
||||
eradicate, flake8_commas, flake8_executable, flake8_fixme, flake8_implicit_str_concat,
|
||||
flake8_pyi, flake8_quotes, flake8_todos, pycodestyle, pygrep_hooks, pylint, pyupgrade, ruff,
|
||||
flake8_pyi, flake8_todos, pycodestyle, pygrep_hooks, pylint, pyupgrade, ruff,
|
||||
};
|
||||
use crate::settings::LinterSettings;
|
||||
|
||||
@@ -122,10 +122,6 @@ pub(crate) fn check_tokens(
|
||||
);
|
||||
}
|
||||
|
||||
if settings.rules.enabled(Rule::AvoidableEscapedQuote) && settings.flake8_quotes.avoid_escape {
|
||||
flake8_quotes::rules::avoidable_escaped_quote(&mut diagnostics, tokens, locator, settings);
|
||||
}
|
||||
|
||||
if settings.rules.any_enabled(&[
|
||||
Rule::SingleLineImplicitStringConcatenation,
|
||||
Rule::MultiLineImplicitStringConcatenation,
|
||||
|
||||
@@ -241,8 +241,12 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Pylint, "E0237") => (RuleGroup::Stable, rules::pylint::rules::NonSlotAssignment),
|
||||
(Pylint, "E0241") => (RuleGroup::Stable, rules::pylint::rules::DuplicateBases),
|
||||
(Pylint, "E0302") => (RuleGroup::Stable, rules::pylint::rules::UnexpectedSpecialMethodSignature),
|
||||
(Pylint, "E0303") => (RuleGroup::Preview, rules::pylint::rules::InvalidLengthReturnType),
|
||||
(Pylint, "E0304") => (RuleGroup::Preview, rules::pylint::rules::InvalidBoolReturnType),
|
||||
(Pylint, "E0305") => (RuleGroup::Preview, rules::pylint::rules::InvalidIndexReturnType),
|
||||
(Pylint, "E0307") => (RuleGroup::Stable, rules::pylint::rules::InvalidStrReturnType),
|
||||
(Pylint, "E0308") => (RuleGroup::Preview, rules::pylint::rules::InvalidBytesReturnType),
|
||||
(Pylint, "E0309") => (RuleGroup::Preview, rules::pylint::rules::InvalidHashReturnType),
|
||||
(Pylint, "E0604") => (RuleGroup::Stable, rules::pylint::rules::InvalidAllObject),
|
||||
(Pylint, "E0605") => (RuleGroup::Stable, rules::pylint::rules::InvalidAllFormat),
|
||||
(Pylint, "E0643") => (RuleGroup::Preview, rules::pylint::rules::PotentialIndexError),
|
||||
@@ -311,6 +315,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Pylint, "W0602") => (RuleGroup::Stable, rules::pylint::rules::GlobalVariableNotAssigned),
|
||||
(Pylint, "W0603") => (RuleGroup::Stable, rules::pylint::rules::GlobalStatement),
|
||||
(Pylint, "W0604") => (RuleGroup::Preview, rules::pylint::rules::GlobalAtModuleLevel),
|
||||
(Pylint, "W0642") => (RuleGroup::Preview, rules::pylint::rules::SelfOrClsAssignment),
|
||||
(Pylint, "W0711") => (RuleGroup::Stable, rules::pylint::rules::BinaryOpException),
|
||||
(Pylint, "W1501") => (RuleGroup::Preview, rules::pylint::rules::BadOpenMode),
|
||||
(Pylint, "W1508") => (RuleGroup::Stable, rules::pylint::rules::InvalidEnvvarDefault),
|
||||
@@ -962,6 +967,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Ruff, "026") => (RuleGroup::Preview, rules::ruff::rules::DefaultFactoryKwarg),
|
||||
(Ruff, "027") => (RuleGroup::Preview, rules::ruff::rules::MissingFStringSyntax),
|
||||
(Ruff, "028") => (RuleGroup::Preview, rules::ruff::rules::InvalidFormatterSuppressionComment),
|
||||
(Ruff, "029") => (RuleGroup::Preview, rules::ruff::rules::UnusedAsync),
|
||||
(Ruff, "100") => (RuleGroup::Stable, rules::ruff::rules::UnusedNOQA),
|
||||
(Ruff, "200") => (RuleGroup::Stable, rules::ruff::rules::InvalidPyprojectToml),
|
||||
#[cfg(feature = "test-rules")]
|
||||
|
||||
@@ -229,6 +229,31 @@ impl<'a> Importer<'a> {
|
||||
.map_or_else(|| self.import_symbol(symbol, at, None, semantic), Ok)
|
||||
}
|
||||
|
||||
/// For a given builtin symbol, determine whether an [`Edit`] is necessary to make the symbol
|
||||
/// available in the current scope. For example, if `zip` has been overridden in the relevant
|
||||
/// scope, the `builtins` module will need to be imported in order for a `Fix` to reference
|
||||
/// `zip`; but otherwise, that won't be necessary.
|
||||
///
|
||||
/// Returns a two-item tuple. The first item is either `Some(Edit)` (indicating) that an
|
||||
/// edit is necessary to make the symbol available, or `None`, indicating that the symbol has
|
||||
/// not been overridden in the current scope. The second item in the tuple is the bound name
|
||||
/// of the symbol.
|
||||
///
|
||||
/// Attempts to reuse existing imports when possible.
|
||||
pub(crate) fn get_or_import_builtin_symbol(
|
||||
&self,
|
||||
symbol: &str,
|
||||
at: TextSize,
|
||||
semantic: &SemanticModel,
|
||||
) -> Result<(Option<Edit>, String), ResolutionError> {
|
||||
if semantic.has_builtin_binding(symbol) {
|
||||
return Ok((None, symbol.to_string()));
|
||||
}
|
||||
let (import_edit, binding) =
|
||||
self.get_or_import_symbol(&ImportRequest::import("builtins", symbol), at, semantic)?;
|
||||
Ok((Some(import_edit), binding))
|
||||
}
|
||||
|
||||
/// Return the [`ImportedName`] to for existing symbol, if it's present in the given [`SemanticModel`].
|
||||
fn find_symbol(
|
||||
symbol: &ImportRequest,
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
//! Extract docstrings via tokenization.
|
||||
//!
|
||||
//! See: <https://github.com/zheller/flake8-quotes/blob/ef0d9a90249a080e460b70ab62bf4b65e5aa5816/flake8_quotes/docstring_detection.py#L29>
|
||||
//!
|
||||
//! TODO(charlie): Consolidate with the existing AST-based docstring extraction.
|
||||
|
||||
use ruff_python_parser::Tok;
|
||||
|
||||
#[derive(Default, Copy, Clone)]
|
||||
enum State {
|
||||
// Start of the module: first string gets marked as a docstring.
|
||||
#[default]
|
||||
ExpectModuleDocstring,
|
||||
// After seeing a class definition, we're waiting for the block colon (and do bracket
|
||||
// counting).
|
||||
ExpectClassColon,
|
||||
// After seeing the block colon in a class definition, we expect a docstring.
|
||||
ExpectClassDocstring,
|
||||
// Same as ExpectClassColon, but for function definitions.
|
||||
ExpectFunctionColon,
|
||||
// Same as ExpectClassDocstring, but for function definitions.
|
||||
ExpectFunctionDocstring,
|
||||
// Skip tokens until we observe a `class` or `def`.
|
||||
Other,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct StateMachine {
|
||||
state: State,
|
||||
bracket_count: usize,
|
||||
}
|
||||
|
||||
impl StateMachine {
|
||||
pub(crate) fn consume(&mut self, tok: &Tok) -> bool {
|
||||
match tok {
|
||||
Tok::NonLogicalNewline
|
||||
| Tok::Newline
|
||||
| Tok::Indent
|
||||
| Tok::Dedent
|
||||
| Tok::Comment(..) => false,
|
||||
|
||||
Tok::String { .. } => {
|
||||
if matches!(
|
||||
self.state,
|
||||
State::ExpectModuleDocstring
|
||||
| State::ExpectClassDocstring
|
||||
| State::ExpectFunctionDocstring
|
||||
) {
|
||||
self.state = State::Other;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
Tok::Class => {
|
||||
self.state = State::ExpectClassColon;
|
||||
self.bracket_count = 0;
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
Tok::Def => {
|
||||
self.state = State::ExpectFunctionColon;
|
||||
self.bracket_count = 0;
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
Tok::Colon => {
|
||||
if self.bracket_count == 0 {
|
||||
if matches!(self.state, State::ExpectClassColon) {
|
||||
self.state = State::ExpectClassDocstring;
|
||||
} else if matches!(self.state, State::ExpectFunctionColon) {
|
||||
self.state = State::ExpectFunctionDocstring;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
Tok::Lpar | Tok::Lbrace | Tok::Lsqb => {
|
||||
self.bracket_count = self.bracket_count.saturating_add(1);
|
||||
if matches!(
|
||||
self.state,
|
||||
State::ExpectModuleDocstring
|
||||
| State::ExpectClassDocstring
|
||||
| State::ExpectFunctionDocstring
|
||||
) {
|
||||
self.state = State::Other;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
Tok::Rpar | Tok::Rbrace | Tok::Rsqb => {
|
||||
self.bracket_count = self.bracket_count.saturating_sub(1);
|
||||
if matches!(
|
||||
self.state,
|
||||
State::ExpectModuleDocstring
|
||||
| State::ExpectClassDocstring
|
||||
| State::ExpectFunctionDocstring
|
||||
) {
|
||||
self.state = State::Other;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
_ => {
|
||||
if matches!(
|
||||
self.state,
|
||||
State::ExpectModuleDocstring
|
||||
| State::ExpectClassDocstring
|
||||
| State::ExpectFunctionDocstring
|
||||
) {
|
||||
self.state = State::Other;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
pub(crate) mod docstring_detection;
|
||||
@@ -24,7 +24,6 @@ mod docstrings;
|
||||
mod fix;
|
||||
pub mod fs;
|
||||
mod importer;
|
||||
mod lex;
|
||||
pub mod line_width;
|
||||
pub mod linter;
|
||||
pub mod logging;
|
||||
|
||||
@@ -194,7 +194,7 @@ impl DisplayParseError {
|
||||
// Translate the byte offset to a location in the originating source.
|
||||
let location =
|
||||
if let Some(jupyter_index) = source_kind.as_ipy_notebook().map(Notebook::index) {
|
||||
let source_location = source_code.source_location(error.offset);
|
||||
let source_location = source_code.source_location(error.location.start());
|
||||
|
||||
ErrorLocation::Cell(
|
||||
jupyter_index
|
||||
@@ -208,7 +208,7 @@ impl DisplayParseError {
|
||||
},
|
||||
)
|
||||
} else {
|
||||
ErrorLocation::File(source_code.source_location(error.offset))
|
||||
ErrorLocation::File(source_code.source_location(error.location.start()))
|
||||
};
|
||||
|
||||
Self {
|
||||
@@ -275,27 +275,7 @@ impl<'a> DisplayParseErrorType<'a> {
|
||||
|
||||
impl Display for DisplayParseErrorType<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self.0 {
|
||||
ParseErrorType::Eof => write!(f, "Expected token but reached end of file."),
|
||||
ParseErrorType::ExtraToken(ref tok) => write!(
|
||||
f,
|
||||
"Got extraneous token: {tok}",
|
||||
tok = TruncateAtNewline(&tok)
|
||||
),
|
||||
ParseErrorType::InvalidToken => write!(f, "Got invalid token"),
|
||||
ParseErrorType::UnrecognizedToken(ref tok, ref expected) => {
|
||||
if let Some(expected) = expected.as_ref() {
|
||||
write!(
|
||||
f,
|
||||
"Expected '{expected}', but got {tok}",
|
||||
tok = TruncateAtNewline(&tok)
|
||||
)
|
||||
} else {
|
||||
write!(f, "Unexpected token {tok}", tok = TruncateAtNewline(&tok))
|
||||
}
|
||||
}
|
||||
ParseErrorType::Lexical(ref error) => write!(f, "{error}"),
|
||||
}
|
||||
write!(f, "{}", TruncateAtNewline(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -256,7 +256,6 @@ impl Rule {
|
||||
| Rule::MixedSpacesAndTabs
|
||||
| Rule::TrailingWhitespace => LintSource::PhysicalLines,
|
||||
Rule::AmbiguousUnicodeCharacterComment
|
||||
| Rule::AvoidableEscapedQuote
|
||||
| Rule::BlanketNOQA
|
||||
| Rule::BlanketTypeIgnore
|
||||
| Rule::BlankLineAfterDecorator
|
||||
|
||||
@@ -3,7 +3,7 @@ use ruff_macros::CacheKey;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use std::iter::FusedIterator;
|
||||
|
||||
const RULESET_SIZE: usize = 13;
|
||||
const RULESET_SIZE: usize = 14;
|
||||
|
||||
/// A set of [`Rule`]s.
|
||||
///
|
||||
@@ -234,6 +234,7 @@ impl RuleSet {
|
||||
/// assert!(set.contains(Rule::AmbiguousFunctionName));
|
||||
/// assert!(!set.contains(Rule::BreakOutsideLoop));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn contains(&self, rule: Rule) -> bool {
|
||||
let rule = rule as u16;
|
||||
let index = rule as usize / Self::SLICE_BITS as usize;
|
||||
@@ -243,6 +244,20 @@ impl RuleSet {
|
||||
self.0[index] & mask != 0
|
||||
}
|
||||
|
||||
/// Returns `true` if any of the rules in `rules` are in this set.
|
||||
#[inline]
|
||||
pub const fn any(&self, rules: &[Rule]) -> bool {
|
||||
let mut any = false;
|
||||
let mut i = 0;
|
||||
|
||||
while i < rules.len() {
|
||||
any |= self.contains(rules[i]);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
any
|
||||
}
|
||||
|
||||
/// Returns an iterator over the rules in this set.
|
||||
///
|
||||
/// ## Examples
|
||||
@@ -276,7 +291,9 @@ impl Display for RuleSet {
|
||||
} else {
|
||||
writeln!(f, "[")?;
|
||||
for rule in self {
|
||||
writeln!(f, "\t{rule:?},")?;
|
||||
let name = rule.as_ref();
|
||||
let code = rule.noqa_code();
|
||||
writeln!(f, "\t{name} ({code}),")?;
|
||||
}
|
||||
write!(f, "]")?;
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ fn is_open_sleep_or_subprocess_call(func: &Expr, semantic: &SemanticModel) -> bo
|
||||
.is_some_and(|qualified_name| {
|
||||
matches!(
|
||||
qualified_name.segments(),
|
||||
["", "open"]
|
||||
["" | "builtins", "open"]
|
||||
| ["time", "sleep"]
|
||||
| [
|
||||
"subprocess",
|
||||
|
||||
@@ -24,23 +24,13 @@ pub(super) fn is_untyped_exception(type_: Option<&Expr>, semantic: &SemanticMode
|
||||
if let Expr::Tuple(ast::ExprTuple { elts, .. }) = &type_ {
|
||||
elts.iter().any(|type_| {
|
||||
semantic
|
||||
.resolve_qualified_name(type_)
|
||||
.is_some_and(|qualified_name| {
|
||||
matches!(
|
||||
qualified_name.segments(),
|
||||
["", "Exception" | "BaseException"]
|
||||
)
|
||||
})
|
||||
.resolve_builtin_symbol(type_)
|
||||
.is_some_and(|builtin| matches!(builtin, "Exception" | "BaseException"))
|
||||
})
|
||||
} else {
|
||||
semantic
|
||||
.resolve_qualified_name(type_)
|
||||
.is_some_and(|qualified_name| {
|
||||
matches!(
|
||||
qualified_name.segments(),
|
||||
["", "Exception" | "BaseException"]
|
||||
)
|
||||
})
|
||||
.resolve_builtin_symbol(type_)
|
||||
.is_some_and(|builtin| matches!(builtin, "Exception" | "BaseException"))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -464,7 +464,7 @@ fn find_shell_keyword<'a>(
|
||||
semantic: &SemanticModel,
|
||||
) -> Option<ShellKeyword<'a>> {
|
||||
arguments.find_keyword("shell").map(|keyword| ShellKeyword {
|
||||
truthiness: Truthiness::from_expr(&keyword.value, |id| semantic.is_builtin(id)),
|
||||
truthiness: Truthiness::from_expr(&keyword.value, |id| semantic.has_builtin_binding(id)),
|
||||
keyword,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -344,7 +344,7 @@ impl Violation for SuspiciousMarkSafeUsage {
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of URL open functions that unexpected schemes.
|
||||
/// Checks for instances where URL open functions are used with unexpected schemes.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Some URL open functions allow the use of `file:` or custom schemes (for use
|
||||
@@ -849,15 +849,45 @@ pub(crate) fn suspicious_function_call(checker: &mut Checker, call: &ExprCall) {
|
||||
["" | "builtins", "eval"] => Some(SuspiciousEvalUsage.into()),
|
||||
// MarkSafe
|
||||
["django", "utils", "safestring" | "html", "mark_safe"] => Some(SuspiciousMarkSafeUsage.into()),
|
||||
// URLOpen (`urlopen`, `urlretrieve`, `Request`)
|
||||
["urllib", "request", "urlopen" | "urlretrieve" | "Request"] |
|
||||
["six", "moves", "urllib", "request", "urlopen" | "urlretrieve" | "Request"] => {
|
||||
// URLOpen (`Request`)
|
||||
["urllib", "request","Request"] |
|
||||
["six", "moves", "urllib", "request","Request"] => {
|
||||
// If the `url` argument is a string literal, allow `http` and `https` schemes.
|
||||
if call.arguments.args.iter().all(|arg| !arg.is_starred_expr()) && call.arguments.keywords.iter().all(|keyword| keyword.arg.is_some()) {
|
||||
if let Some(Expr::StringLiteral(ast::ExprStringLiteral { value, .. })) = &call.arguments.find_argument("url", 0) {
|
||||
let url = value.to_str().trim_start();
|
||||
if url.starts_with("http://") || url.starts_with("https://") {
|
||||
return None;
|
||||
let url = value.to_str().trim_start();
|
||||
if url.starts_with("http://") || url.starts_with("https://") {
|
||||
return None;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Some(SuspiciousURLOpenUsage.into())
|
||||
}
|
||||
// URLOpen (`urlopen`, `urlretrieve`)
|
||||
["urllib", "request", "urlopen" | "urlretrieve" ] |
|
||||
["six", "moves", "urllib", "request", "urlopen" | "urlretrieve" ] => {
|
||||
if call.arguments.args.iter().all(|arg| !arg.is_starred_expr()) && call.arguments.keywords.iter().all(|keyword| keyword.arg.is_some()) {
|
||||
if let Some(arg) = &call.arguments.find_argument("url", 0) {
|
||||
// If the `url` argument is a string literal, allow `http` and `https` schemes.
|
||||
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = arg {
|
||||
let url = value.to_str().trim_start();
|
||||
if url.starts_with("http://") || url.starts_with("https://") {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
// If the `url` argument is a `urllib.request.Request` object, allow `http` and `https` schemes.
|
||||
if let Expr::Call(ExprCall { func, arguments, .. }) = arg {
|
||||
if checker.semantic().resolve_qualified_name(func.as_ref()).is_some_and(|name| name.segments() == ["urllib", "request", "Request"]) {
|
||||
if let Some( Expr::StringLiteral(ast::ExprStringLiteral { value, .. })) = arguments.find_argument("url", 0) {
|
||||
let url = value.to_str().trim_start();
|
||||
if url.starts_with("http://") || url.starts_with("https://") {
|
||||
return None;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +102,49 @@ S310.py:19:1: S310 Audit URL open for permitted schemes. Allowing use of `file:`
|
||||
18 | urllib.request.URLopener().open('file:///foo/bar/baz')
|
||||
19 | urllib.request.URLopener().open(url)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ S310
|
||||
20 |
|
||||
21 | urllib.request.urlopen(url=urllib.request.Request('http://www.google.com'))
|
||||
|
|
||||
|
||||
S310.py:22:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
||||
|
|
||||
21 | urllib.request.urlopen(url=urllib.request.Request('http://www.google.com'))
|
||||
22 | urllib.request.urlopen(url=urllib.request.Request('http://www.google.com'), **kwargs)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ S310
|
||||
23 | urllib.request.urlopen(urllib.request.Request('http://www.google.com'))
|
||||
24 | urllib.request.urlopen(urllib.request.Request('file:///foo/bar/baz'))
|
||||
|
|
||||
|
||||
S310.py:24:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
||||
|
|
||||
22 | urllib.request.urlopen(url=urllib.request.Request('http://www.google.com'), **kwargs)
|
||||
23 | urllib.request.urlopen(urllib.request.Request('http://www.google.com'))
|
||||
24 | urllib.request.urlopen(urllib.request.Request('file:///foo/bar/baz'))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ S310
|
||||
25 | urllib.request.urlopen(urllib.request.Request(url))
|
||||
|
|
||||
|
||||
S310.py:24:24: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
||||
|
|
||||
22 | urllib.request.urlopen(url=urllib.request.Request('http://www.google.com'), **kwargs)
|
||||
23 | urllib.request.urlopen(urllib.request.Request('http://www.google.com'))
|
||||
24 | urllib.request.urlopen(urllib.request.Request('file:///foo/bar/baz'))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ S310
|
||||
25 | urllib.request.urlopen(urllib.request.Request(url))
|
||||
|
|
||||
|
||||
S310.py:25:1: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
||||
|
|
||||
23 | urllib.request.urlopen(urllib.request.Request('http://www.google.com'))
|
||||
24 | urllib.request.urlopen(urllib.request.Request('file:///foo/bar/baz'))
|
||||
25 | urllib.request.urlopen(urllib.request.Request(url))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ S310
|
||||
|
|
||||
|
||||
S310.py:25:24: S310 Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected.
|
||||
|
|
||||
23 | urllib.request.urlopen(urllib.request.Request('http://www.google.com'))
|
||||
24 | urllib.request.urlopen(urllib.request.Request('file:///foo/bar/baz'))
|
||||
25 | urllib.request.urlopen(urllib.request.Request(url))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ S310
|
||||
|
|
||||
|
||||
@@ -79,15 +79,12 @@ pub(crate) fn blind_except(
|
||||
let Some(type_) = type_ else {
|
||||
return;
|
||||
};
|
||||
let Expr::Name(ast::ExprName { id, .. }) = &type_ else {
|
||||
|
||||
let semantic = checker.semantic();
|
||||
let Some(builtin_exception_type) = semantic.resolve_builtin_symbol(type_) else {
|
||||
return;
|
||||
};
|
||||
|
||||
if !matches!(id.as_str(), "BaseException" | "Exception") {
|
||||
return;
|
||||
}
|
||||
|
||||
if !checker.semantic().is_builtin(id) {
|
||||
if !matches!(builtin_exception_type, "BaseException" | "Exception") {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -121,7 +118,7 @@ pub(crate) fn blind_except(
|
||||
Expr::Attribute(ast::ExprAttribute { attr, .. }) => {
|
||||
if logging::is_logger_candidate(
|
||||
func,
|
||||
checker.semantic(),
|
||||
semantic,
|
||||
&checker.settings.logger_objects,
|
||||
) {
|
||||
match attr.as_str() {
|
||||
@@ -138,9 +135,8 @@ pub(crate) fn blind_except(
|
||||
}
|
||||
}
|
||||
Expr::Name(ast::ExprName { .. }) => {
|
||||
if checker
|
||||
.semantic()
|
||||
.resolve_qualified_name(func.as_ref())
|
||||
if semantic
|
||||
.resolve_qualified_name(func)
|
||||
.is_some_and(|qualified_name| match qualified_name.segments() {
|
||||
["logging", "exception"] => true,
|
||||
["logging", "error"] => {
|
||||
@@ -170,7 +166,7 @@ pub(crate) fn blind_except(
|
||||
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
BlindExcept {
|
||||
name: id.to_string(),
|
||||
name: builtin_exception_type.to_string(),
|
||||
},
|
||||
type_.range(),
|
||||
));
|
||||
|
||||
@@ -148,7 +148,7 @@ pub(crate) fn boolean_type_hint_positional_argument(
|
||||
}
|
||||
|
||||
// If `bool` isn't actually a reference to the `bool` built-in, return.
|
||||
if !checker.semantic().is_builtin("bool") {
|
||||
if !checker.semantic().has_builtin_binding("bool") {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ mod tests {
|
||||
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_5.py"))]
|
||||
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_6.py"))]
|
||||
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_7.py"))]
|
||||
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_8.py"))]
|
||||
#[test_case(Rule::MutableArgumentDefault, Path::new("B006_B008.py"))]
|
||||
#[test_case(Rule::NoExplicitStacklevel, Path::new("B028.py"))]
|
||||
#[test_case(Rule::RaiseLiteral, Path::new("B016.py"))]
|
||||
@@ -113,6 +114,7 @@ mod tests {
|
||||
"fastapi.Depends".to_string(),
|
||||
"fastapi.Query".to_string(),
|
||||
"custom.ImmutableTypeA".to_string(),
|
||||
"B008_extended.Class".to_string(),
|
||||
],
|
||||
},
|
||||
..LinterSettings::for_rule(Rule::FunctionCallInDefaultArgument)
|
||||
|
||||
@@ -95,24 +95,22 @@ pub(crate) fn assert_raises_exception(checker: &mut Checker, items: &[WithItem])
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(exception) =
|
||||
checker
|
||||
.semantic()
|
||||
.resolve_qualified_name(arg)
|
||||
.and_then(|qualified_name| match qualified_name.segments() {
|
||||
["", "Exception"] => Some(ExceptionKind::Exception),
|
||||
["", "BaseException"] => Some(ExceptionKind::BaseException),
|
||||
_ => None,
|
||||
})
|
||||
else {
|
||||
let semantic = checker.semantic();
|
||||
|
||||
let Some(builtin_symbol) = semantic.resolve_builtin_symbol(arg) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let exception = match builtin_symbol {
|
||||
"Exception" => ExceptionKind::Exception,
|
||||
"BaseException" => ExceptionKind::BaseException,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let assertion = if matches!(func.as_ref(), Expr::Attribute(ast::ExprAttribute { attr, .. }) if attr == "assertRaises")
|
||||
{
|
||||
AssertionKind::AssertRaises
|
||||
} else if checker
|
||||
.semantic()
|
||||
} else if semantic
|
||||
.resolve_qualified_name(func)
|
||||
.is_some_and(|qualified_name| matches!(qualified_name.segments(), ["pytest", "raises"]))
|
||||
&& arguments.find_keyword("match").is_none()
|
||||
|
||||
@@ -34,7 +34,7 @@ use crate::checkers::ast::Checker;
|
||||
/// ```python
|
||||
/// from functools import partial
|
||||
///
|
||||
/// adders = [partial(lambda x, i: x + i, i) for i in range(3)]
|
||||
/// adders = [partial(lambda x, i: x + i, i=i) for i in range(3)]
|
||||
/// values = [adder(1) for adder in adders] # [1, 2, 3]
|
||||
/// ```
|
||||
///
|
||||
@@ -67,7 +67,7 @@ impl<'a> Visitor<'a> for LoadedNamesVisitor<'a> {
|
||||
Expr::Name(name) => match &name.ctx {
|
||||
ExprContext::Load => self.loaded.push(name),
|
||||
ExprContext::Store => self.stored.push(name),
|
||||
ExprContext::Del => {}
|
||||
_ => {}
|
||||
},
|
||||
_ => visitor::walk_expr(self, expr),
|
||||
}
|
||||
|
||||
@@ -54,12 +54,6 @@ pub(crate) fn getattr_with_constant(
|
||||
func: &Expr,
|
||||
args: &[Expr],
|
||||
) {
|
||||
let Expr::Name(ast::ExprName { id, .. }) = func else {
|
||||
return;
|
||||
};
|
||||
if id != "getattr" {
|
||||
return;
|
||||
}
|
||||
let [obj, arg] = args else {
|
||||
return;
|
||||
};
|
||||
@@ -75,7 +69,7 @@ pub(crate) fn getattr_with_constant(
|
||||
if is_mangled_private(value.to_str()) {
|
||||
return;
|
||||
}
|
||||
if !checker.semantic().is_builtin("getattr") {
|
||||
if !checker.semantic().match_builtin_expr(func, "getattr") {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,12 @@ use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::helpers::is_docstring_stmt;
|
||||
use ruff_python_ast::name::QualifiedName;
|
||||
use ruff_python_ast::{self as ast, Expr, Parameter, ParameterWithDefault, Stmt};
|
||||
use ruff_python_ast::{self as ast, Expr, Parameter, ParameterWithDefault};
|
||||
use ruff_python_codegen::{Generator, Stylist};
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_python_semantic::analyze::function_type::is_stub;
|
||||
use ruff_python_semantic::analyze::typing::{is_immutable_annotation, is_mutable_expr};
|
||||
use ruff_python_semantic::SemanticModel;
|
||||
use ruff_python_trivia::{indentation_at_offset, textwrap};
|
||||
use ruff_source_file::Locator;
|
||||
use ruff_text_size::Ranged;
|
||||
@@ -118,6 +120,7 @@ pub(crate) fn mutable_argument_default(checker: &mut Checker, function_def: &ast
|
||||
function_def,
|
||||
parameter,
|
||||
default,
|
||||
checker.semantic(),
|
||||
checker.locator(),
|
||||
checker.stylist(),
|
||||
checker.indexer(),
|
||||
@@ -132,10 +135,12 @@ pub(crate) fn mutable_argument_default(checker: &mut Checker, function_def: &ast
|
||||
|
||||
/// Generate a [`Fix`] to move a mutable argument default initialization
|
||||
/// into the function body.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn move_initialization(
|
||||
function_def: &ast::StmtFunctionDef,
|
||||
parameter: &Parameter,
|
||||
default: &Expr,
|
||||
semantic: &SemanticModel,
|
||||
locator: &Locator,
|
||||
stylist: &Stylist,
|
||||
indexer: &Indexer,
|
||||
@@ -153,7 +158,7 @@ fn move_initialization(
|
||||
let default_edit = Edit::range_replacement("None".to_string(), default.range());
|
||||
|
||||
// If the function is a stub, this is the only necessary edit.
|
||||
if is_stub(function_def) {
|
||||
if is_stub(function_def, semantic) {
|
||||
return Some(Fix::unsafe_edit(default_edit));
|
||||
}
|
||||
|
||||
@@ -209,20 +214,3 @@ fn move_initialization(
|
||||
let initialization_edit = Edit::insertion(content, pos);
|
||||
Some(Fix::unsafe_edits(default_edit, [initialization_edit]))
|
||||
}
|
||||
|
||||
/// Returns `true` if a function has an empty body, and is therefore a stub.
|
||||
///
|
||||
/// A function body is considered to be empty if it contains only `pass` statements, `...` literals,
|
||||
/// and docstrings.
|
||||
fn is_stub(function_def: &ast::StmtFunctionDef) -> bool {
|
||||
function_def.body.iter().all(|stmt| match stmt {
|
||||
Stmt::Pass(_) => true,
|
||||
Stmt::Expr(ast::StmtExpr { value, range: _ }) => {
|
||||
matches!(
|
||||
value.as_ref(),
|
||||
Expr::StringLiteral(_) | Expr::EllipsisLiteral(_)
|
||||
)
|
||||
}
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -68,12 +68,6 @@ pub(crate) fn setattr_with_constant(
|
||||
func: &Expr,
|
||||
args: &[Expr],
|
||||
) {
|
||||
let Expr::Name(ast::ExprName { id, .. }) = func else {
|
||||
return;
|
||||
};
|
||||
if id != "setattr" {
|
||||
return;
|
||||
}
|
||||
let [obj, name, value] = args else {
|
||||
return;
|
||||
};
|
||||
@@ -89,7 +83,7 @@ pub(crate) fn setattr_with_constant(
|
||||
if is_mangled_private(name.to_str()) {
|
||||
return;
|
||||
}
|
||||
if !checker.semantic().is_builtin("setattr") {
|
||||
if !checker.semantic().match_builtin_expr(func, "setattr") {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use ruff_python_ast::{self as ast, Expr};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::{self as ast, Expr};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
@@ -58,12 +57,6 @@ pub(crate) fn unreliable_callable_check(
|
||||
func: &Expr,
|
||||
args: &[Expr],
|
||||
) {
|
||||
let Expr::Name(ast::ExprName { id, .. }) = func else {
|
||||
return;
|
||||
};
|
||||
if !matches!(id.as_str(), "hasattr" | "getattr") {
|
||||
return;
|
||||
}
|
||||
let [obj, attr, ..] = args else {
|
||||
return;
|
||||
};
|
||||
@@ -73,15 +66,27 @@ pub(crate) fn unreliable_callable_check(
|
||||
if value != "__call__" {
|
||||
return;
|
||||
}
|
||||
let Some(builtins_function) = checker.semantic().resolve_builtin_symbol(func) else {
|
||||
return;
|
||||
};
|
||||
if !matches!(builtins_function, "hasattr" | "getattr") {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(UnreliableCallableCheck, expr.range());
|
||||
if id == "hasattr" {
|
||||
if checker.semantic().is_builtin("callable") {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
format!("callable({})", checker.locator().slice(obj)),
|
||||
if builtins_function == "hasattr" {
|
||||
diagnostic.try_set_fix(|| {
|
||||
let (import_edit, binding) = checker.importer().get_or_import_builtin_symbol(
|
||||
"callable",
|
||||
expr.start(),
|
||||
checker.semantic(),
|
||||
)?;
|
||||
let binding_edit = Edit::range_replacement(
|
||||
format!("{binding}({})", checker.locator().slice(obj)),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
);
|
||||
Ok(Fix::safe_edits(binding_edit, import_edit))
|
||||
});
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ pub(crate) fn unused_loop_control_variable(checker: &mut Checker, stmt_for: &ast
|
||||
// Avoid fixing any variables that _may_ be used, but undetectably so.
|
||||
let certainty =
|
||||
Certainty::from(!helpers::uses_magic_variable_access(&stmt_for.body, |id| {
|
||||
checker.semantic().is_builtin(id)
|
||||
checker.semantic().has_builtin_binding(id)
|
||||
}));
|
||||
|
||||
// Attempt to rename the variable by prepending an underscore, but avoid
|
||||
|
||||
@@ -95,7 +95,7 @@ pub(crate) fn useless_expression(checker: &mut Checker, value: &Expr) {
|
||||
}
|
||||
|
||||
// Ignore statements that have side effects.
|
||||
if contains_effect(value, |id| checker.semantic().is_builtin(id)) {
|
||||
if contains_effect(value, |id| checker.semantic().has_builtin_binding(id)) {
|
||||
// Flag attributes as useless expressions, even if they're attached to calls or other
|
||||
// expressions.
|
||||
if value.is_attribute_expr() {
|
||||
|
||||
@@ -17,7 +17,8 @@ use crate::fix::edits::add_argument;
|
||||
/// iterable. This can lead to subtle bugs.
|
||||
///
|
||||
/// Use the `strict` parameter to raise a `ValueError` if the iterables are of
|
||||
/// non-uniform length.
|
||||
/// non-uniform length. If the iterables are intentionally different lengths, the
|
||||
/// parameter should be explicitly set to `False`.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
@@ -52,18 +53,18 @@ impl AlwaysFixableViolation for ZipWithoutExplicitStrict {
|
||||
|
||||
/// B905
|
||||
pub(crate) fn zip_without_explicit_strict(checker: &mut Checker, call: &ast::ExprCall) {
|
||||
if let Expr::Name(ast::ExprName { id, .. }) = call.func.as_ref() {
|
||||
if id == "zip"
|
||||
&& checker.semantic().is_builtin("zip")
|
||||
&& call.arguments.find_keyword("strict").is_none()
|
||||
&& !call
|
||||
.arguments
|
||||
.args
|
||||
.iter()
|
||||
.any(|arg| is_infinite_iterator(arg, checker.semantic()))
|
||||
{
|
||||
let mut diagnostic = Diagnostic::new(ZipWithoutExplicitStrict, call.range());
|
||||
diagnostic.set_fix(Fix::applicable_edit(
|
||||
let semantic = checker.semantic();
|
||||
|
||||
if semantic.match_builtin_expr(&call.func, "zip")
|
||||
&& call.arguments.find_keyword("strict").is_none()
|
||||
&& !call
|
||||
.arguments
|
||||
.args
|
||||
.iter()
|
||||
.any(|arg| is_infinite_iterator(arg, semantic))
|
||||
{
|
||||
checker.diagnostics.push(
|
||||
Diagnostic::new(ZipWithoutExplicitStrict, call.range()).with_fix(Fix::applicable_edit(
|
||||
add_argument(
|
||||
"strict=False",
|
||||
&call.arguments,
|
||||
@@ -81,9 +82,8 @@ pub(crate) fn zip_without_explicit_strict(checker: &mut Checker, call: &ast::Exp
|
||||
} else {
|
||||
Applicability::Safe
|
||||
},
|
||||
));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,4 +31,58 @@ B004.py:5:8: B004 Using `hasattr(x, "__call__")` to test if x is callable is unr
|
||||
|
|
||||
= help: Replace with `callable()`
|
||||
|
||||
B004.py:12:8: B004 [*] Using `hasattr(x, "__call__")` to test if x is callable is unreliable. Use `callable(x)` for consistent results.
|
||||
|
|
||||
10 | import builtins
|
||||
11 | o = object()
|
||||
12 | if builtins.hasattr(o, "__call__"):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ B004
|
||||
13 | print("B U G")
|
||||
14 | if builtins.getattr(o, "__call__", False):
|
||||
|
|
||||
= help: Replace with `callable()`
|
||||
|
||||
ℹ Safe fix
|
||||
9 9 | def still_a_bug():
|
||||
10 10 | import builtins
|
||||
11 11 | o = object()
|
||||
12 |- if builtins.hasattr(o, "__call__"):
|
||||
12 |+ if callable(o):
|
||||
13 13 | print("B U G")
|
||||
14 14 | if builtins.getattr(o, "__call__", False):
|
||||
15 15 | print("B U G")
|
||||
|
||||
B004.py:14:8: B004 Using `hasattr(x, "__call__")` to test if x is callable is unreliable. Use `callable(x)` for consistent results.
|
||||
|
|
||||
12 | if builtins.hasattr(o, "__call__"):
|
||||
13 | print("B U G")
|
||||
14 | if builtins.getattr(o, "__call__", False):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ B004
|
||||
15 | print("B U G")
|
||||
|
|
||||
= help: Replace with `callable()`
|
||||
|
||||
B004.py:24:8: B004 [*] Using `hasattr(x, "__call__")` to test if x is callable is unreliable. Use `callable(x)` for consistent results.
|
||||
|
|
||||
22 | return True
|
||||
23 |
|
||||
24 | if hasattr(o, "__call__"):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ B004
|
||||
25 | print("STILL a bug!")
|
||||
|
|
||||
= help: Replace with `callable()`
|
||||
|
||||
ℹ Safe fix
|
||||
1 |+import builtins
|
||||
1 2 | def this_is_a_bug():
|
||||
2 3 | o = object()
|
||||
3 4 | if hasattr(o, "__call__"):
|
||||
--------------------------------------------------------------------------------
|
||||
21 22 | def callable(x):
|
||||
22 23 | return True
|
||||
23 24 |
|
||||
24 |- if hasattr(o, "__call__"):
|
||||
25 |+ if builtins.callable(o):
|
||||
25 26 | print("STILL a bug!")
|
||||
26 27 |
|
||||
27 28 |
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs
|
||||
---
|
||||
B006_8.py:1:19: B006 [*] Do not use mutable data structures for argument defaults
|
||||
|
|
||||
1 | def foo(a: list = []):
|
||||
| ^^ B006
|
||||
2 | raise NotImplementedError("")
|
||||
|
|
||||
= help: Replace with `None`; initialize within function
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |-def foo(a: list = []):
|
||||
1 |+def foo(a: list = None):
|
||||
2 2 | raise NotImplementedError("")
|
||||
3 3 |
|
||||
4 4 |
|
||||
|
||||
B006_8.py:5:19: B006 [*] Do not use mutable data structures for argument defaults
|
||||
|
|
||||
5 | def bar(a: dict = {}):
|
||||
| ^^ B006
|
||||
6 | """ This one also has a docstring"""
|
||||
7 | raise NotImplementedError("and has some text in here")
|
||||
|
|
||||
= help: Replace with `None`; initialize within function
|
||||
|
||||
ℹ Unsafe fix
|
||||
2 2 | raise NotImplementedError("")
|
||||
3 3 |
|
||||
4 4 |
|
||||
5 |-def bar(a: dict = {}):
|
||||
5 |+def bar(a: dict = None):
|
||||
6 6 | """ This one also has a docstring"""
|
||||
7 7 | raise NotImplementedError("and has some text in here")
|
||||
8 8 |
|
||||
|
||||
B006_8.py:10:19: B006 [*] Do not use mutable data structures for argument defaults
|
||||
|
|
||||
10 | def baz(a: list = []):
|
||||
| ^^ B006
|
||||
11 | """This one raises a different exception"""
|
||||
12 | raise IndexError()
|
||||
|
|
||||
= help: Replace with `None`; initialize within function
|
||||
|
||||
ℹ Unsafe fix
|
||||
7 7 | raise NotImplementedError("and has some text in here")
|
||||
8 8 |
|
||||
9 9 |
|
||||
10 |-def baz(a: list = []):
|
||||
10 |+def baz(a: list = None):
|
||||
11 11 | """This one raises a different exception"""
|
||||
12 |+ if a is None:
|
||||
13 |+ a = []
|
||||
12 14 | raise IndexError()
|
||||
13 15 |
|
||||
14 16 |
|
||||
|
||||
B006_8.py:15:19: B006 [*] Do not use mutable data structures for argument defaults
|
||||
|
|
||||
15 | def qux(a: list = []):
|
||||
| ^^ B006
|
||||
16 | raise NotImplementedError
|
||||
|
|
||||
= help: Replace with `None`; initialize within function
|
||||
|
||||
ℹ Unsafe fix
|
||||
12 12 | raise IndexError()
|
||||
13 13 |
|
||||
14 14 |
|
||||
15 |-def qux(a: list = []):
|
||||
15 |+def qux(a: list = None):
|
||||
16 16 | raise NotImplementedError
|
||||
17 17 |
|
||||
18 18 |
|
||||
|
||||
B006_8.py:19:20: B006 [*] Do not use mutable data structures for argument defaults
|
||||
|
|
||||
19 | def quux(a: list = []):
|
||||
| ^^ B006
|
||||
20 | raise NotImplemented
|
||||
|
|
||||
= help: Replace with `None`; initialize within function
|
||||
|
||||
ℹ Unsafe fix
|
||||
16 16 | raise NotImplementedError
|
||||
17 17 |
|
||||
18 18 |
|
||||
19 |-def quux(a: list = []):
|
||||
19 |+def quux(a: list = None):
|
||||
20 20 | raise NotImplemented
|
||||
@@ -342,6 +342,8 @@ B009_B010.py:65:1: B009 [*] Do not call `getattr` with a constant attribute valu
|
||||
65 | / getattr(self.
|
||||
66 | | registration.registry, '__name__')
|
||||
| |_____________________________________^ B009
|
||||
67 |
|
||||
68 | import builtins
|
||||
|
|
||||
= help: Replace `getattr` with attribute access
|
||||
|
||||
@@ -353,5 +355,21 @@ B009_B010.py:65:1: B009 [*] Do not call `getattr` with a constant attribute valu
|
||||
66 |- registration.registry, '__name__')
|
||||
65 |+(self.
|
||||
66 |+ registration.registry).__name__
|
||||
67 67 |
|
||||
68 68 | import builtins
|
||||
69 69 | builtins.getattr(foo, "bar")
|
||||
|
||||
B009_B010.py:69:1: B009 [*] Do not call `getattr` with a constant attribute value. It is not any safer than normal property access.
|
||||
|
|
||||
68 | import builtins
|
||||
69 | builtins.getattr(foo, "bar")
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ B009
|
||||
|
|
||||
= help: Replace `getattr` with attribute access
|
||||
|
||||
ℹ Safe fix
|
||||
66 66 | registration.registry, '__name__')
|
||||
67 67 |
|
||||
68 68 | import builtins
|
||||
69 |-builtins.getattr(foo, "bar")
|
||||
69 |+foo.bar
|
||||
|
||||
@@ -162,6 +162,8 @@ B905.py:24:1: B905 [*] `zip()` without an explicit `strict=` parameter
|
||||
24 |-zip([1, 2, 3], repeat(1, 1))
|
||||
24 |+zip([1, 2, 3], repeat(1, 1), strict=False)
|
||||
25 25 | zip([1, 2, 3], repeat(1, times=4))
|
||||
26 26 |
|
||||
27 27 | import builtins
|
||||
|
||||
B905.py:25:1: B905 [*] `zip()` without an explicit `strict=` parameter
|
||||
|
|
||||
@@ -169,6 +171,8 @@ B905.py:25:1: B905 [*] `zip()` without an explicit `strict=` parameter
|
||||
24 | zip([1, 2, 3], repeat(1, 1))
|
||||
25 | zip([1, 2, 3], repeat(1, times=4))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ B905
|
||||
26 |
|
||||
27 | import builtins
|
||||
|
|
||||
= help: Add explicit `strict=False`
|
||||
|
||||
@@ -178,5 +182,22 @@ B905.py:25:1: B905 [*] `zip()` without an explicit `strict=` parameter
|
||||
24 24 | zip([1, 2, 3], repeat(1, 1))
|
||||
25 |-zip([1, 2, 3], repeat(1, times=4))
|
||||
25 |+zip([1, 2, 3], repeat(1, times=4), strict=False)
|
||||
26 26 |
|
||||
27 27 | import builtins
|
||||
28 28 | # Still an error even though it uses the qualified name
|
||||
|
||||
B905.py:29:1: B905 [*] `zip()` without an explicit `strict=` parameter
|
||||
|
|
||||
27 | import builtins
|
||||
28 | # Still an error even though it uses the qualified name
|
||||
29 | builtins.zip([1, 2, 3])
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ B905
|
||||
|
|
||||
= help: Add explicit `strict=False`
|
||||
|
||||
ℹ Safe fix
|
||||
26 26 |
|
||||
27 27 | import builtins
|
||||
28 28 | # Still an error even though it uses the qualified name
|
||||
29 |-builtins.zip([1, 2, 3])
|
||||
29 |+builtins.zip([1, 2, 3], strict=False)
|
||||
|
||||
@@ -8,4 +8,9 @@ B008_extended.py:24:51: B008 Do not perform function call `Depends` in argument
|
||||
25 | ...
|
||||
|
|
||||
|
||||
|
||||
B008_extended.py:36:15: B008 Do not perform function call `OtherClass` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
|
||||
|
|
||||
36 | def error(obj=OtherClass()):
|
||||
| ^^^^^^^^^^^^ B008
|
||||
37 | ...
|
||||
|
|
||||
|
||||
@@ -60,40 +60,35 @@ impl AlwaysFixableViolation for UnnecessaryCallAroundSorted {
|
||||
pub(crate) fn unnecessary_call_around_sorted(
|
||||
checker: &mut Checker,
|
||||
expr: &Expr,
|
||||
func: &Expr,
|
||||
outer_func: &Expr,
|
||||
args: &[Expr],
|
||||
) {
|
||||
let Some(outer) = func.as_name_expr() else {
|
||||
let Some(Expr::Call(ast::ExprCall {
|
||||
func: inner_func, ..
|
||||
})) = args.first()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
if !matches!(outer.id.as_str(), "list" | "reversed") {
|
||||
let semantic = checker.semantic();
|
||||
let Some(outer_func_name) = semantic.resolve_builtin_symbol(outer_func) else {
|
||||
return;
|
||||
};
|
||||
if !matches!(outer_func_name, "list" | "reversed") {
|
||||
return;
|
||||
}
|
||||
let Some(arg) = args.first() else {
|
||||
return;
|
||||
};
|
||||
let Expr::Call(ast::ExprCall { func, .. }) = arg else {
|
||||
return;
|
||||
};
|
||||
let Some(inner) = func.as_name_expr() else {
|
||||
return;
|
||||
};
|
||||
if inner.id != "sorted" {
|
||||
return;
|
||||
}
|
||||
if !checker.semantic().is_builtin(&inner.id) || !checker.semantic().is_builtin(&outer.id) {
|
||||
if !semantic.match_builtin_expr(inner_func, "sorted") {
|
||||
return;
|
||||
}
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
UnnecessaryCallAroundSorted {
|
||||
func: outer.id.to_string(),
|
||||
func: outer_func_name.to_string(),
|
||||
},
|
||||
expr.range(),
|
||||
);
|
||||
diagnostic.try_set_fix(|| {
|
||||
Ok(Fix::applicable_edit(
|
||||
fixes::fix_unnecessary_call_around_sorted(expr, checker.locator(), checker.stylist())?,
|
||||
if outer.id == "reversed" {
|
||||
if outer_func_name == "reversed" {
|
||||
Applicability::Unsafe
|
||||
} else {
|
||||
Applicability::Safe
|
||||
|
||||
@@ -65,10 +65,10 @@ pub(crate) fn unnecessary_collection_call(
|
||||
if !call.arguments.args.is_empty() {
|
||||
return;
|
||||
}
|
||||
let Some(func) = call.func.as_name_expr() else {
|
||||
let Some(builtin) = checker.semantic().resolve_builtin_symbol(&call.func) else {
|
||||
return;
|
||||
};
|
||||
let collection = match func.id.as_str() {
|
||||
let collection = match builtin {
|
||||
"dict"
|
||||
if call.arguments.keywords.is_empty()
|
||||
|| (!settings.allow_dict_calls_with_keyword_arguments
|
||||
@@ -87,13 +87,10 @@ pub(crate) fn unnecessary_collection_call(
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
if !checker.semantic().is_builtin(func.id.as_str()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
UnnecessaryCollectionCall {
|
||||
obj_type: func.id.to_string(),
|
||||
obj_type: builtin.to_string(),
|
||||
},
|
||||
call.range(),
|
||||
);
|
||||
|
||||
@@ -60,7 +60,7 @@ fn add_diagnostic(checker: &mut Checker, expr: &Expr) {
|
||||
Expr::DictComp(_) => "dict",
|
||||
_ => return,
|
||||
};
|
||||
if !checker.semantic().is_builtin(id) {
|
||||
if !checker.semantic().has_builtin_binding(id) {
|
||||
return;
|
||||
}
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
|
||||
@@ -90,15 +90,6 @@ pub(crate) fn unnecessary_comprehension_in_call(
|
||||
if !keywords.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let Expr::Name(ast::ExprName { id, .. }) = func else {
|
||||
return;
|
||||
};
|
||||
if !(matches!(id.as_str(), "any" | "all")
|
||||
|| (checker.settings.preview.is_enabled() && matches!(id.as_str(), "sum" | "min" | "max")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
let [arg] = args else {
|
||||
return;
|
||||
};
|
||||
@@ -110,7 +101,13 @@ pub(crate) fn unnecessary_comprehension_in_call(
|
||||
if contains_await(elt) {
|
||||
return;
|
||||
}
|
||||
if !checker.semantic().is_builtin(id) {
|
||||
let Some(builtin_function) = checker.semantic().resolve_builtin_symbol(func) else {
|
||||
return;
|
||||
};
|
||||
if !(matches!(builtin_function, "any" | "all")
|
||||
|| (checker.settings.preview.is_enabled()
|
||||
&& matches!(builtin_function, "sum" | "min" | "max")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,24 +70,15 @@ impl AlwaysFixableViolation for UnnecessaryDoubleCastOrProcess {
|
||||
pub(crate) fn unnecessary_double_cast_or_process(
|
||||
checker: &mut Checker,
|
||||
expr: &Expr,
|
||||
func: &Expr,
|
||||
outer_func: &Expr,
|
||||
args: &[Expr],
|
||||
outer_kw: &[Keyword],
|
||||
) {
|
||||
let Some(outer) = func.as_name_expr() else {
|
||||
return;
|
||||
};
|
||||
if !matches!(
|
||||
outer.id.as_str(),
|
||||
"list" | "tuple" | "set" | "reversed" | "sorted"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
let Some(arg) = args.first() else {
|
||||
return;
|
||||
};
|
||||
let Expr::Call(ast::ExprCall {
|
||||
func,
|
||||
func: inner_func,
|
||||
arguments: Arguments {
|
||||
keywords: inner_kw, ..
|
||||
},
|
||||
@@ -96,16 +87,23 @@ pub(crate) fn unnecessary_double_cast_or_process(
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Some(inner) = func.as_name_expr() else {
|
||||
let semantic = checker.semantic();
|
||||
let Some(outer_func_name) = semantic.resolve_builtin_symbol(outer_func) else {
|
||||
return;
|
||||
};
|
||||
if !checker.semantic().is_builtin(&inner.id) || !checker.semantic().is_builtin(&outer.id) {
|
||||
if !matches!(
|
||||
outer_func_name,
|
||||
"list" | "tuple" | "set" | "reversed" | "sorted"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
let Some(inner_func_name) = semantic.resolve_builtin_symbol(inner_func) else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Avoid collapsing nested `sorted` calls with non-identical keyword arguments
|
||||
// (i.e., `key`, `reverse`).
|
||||
if inner.id == "sorted" && outer.id == "sorted" {
|
||||
if inner_func_name == "sorted" && outer_func_name == "sorted" {
|
||||
if inner_kw.len() != outer_kw.len() {
|
||||
return;
|
||||
}
|
||||
@@ -122,15 +120,15 @@ pub(crate) fn unnecessary_double_cast_or_process(
|
||||
// Ex) `list(tuple(...))`
|
||||
// Ex) `set(set(...))`
|
||||
if matches!(
|
||||
(outer.id.as_str(), inner.id.as_str()),
|
||||
(outer_func_name, inner_func_name),
|
||||
("set" | "sorted", "list" | "tuple" | "reversed" | "sorted")
|
||||
| ("set", "set")
|
||||
| ("list" | "tuple", "list" | "tuple")
|
||||
) {
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
UnnecessaryDoubleCastOrProcess {
|
||||
inner: inner.id.to_string(),
|
||||
outer: outer.id.to_string(),
|
||||
inner: inner_func_name.to_string(),
|
||||
outer: outer_func_name.to_string(),
|
||||
},
|
||||
expr.range(),
|
||||
);
|
||||
|
||||
@@ -71,7 +71,7 @@ pub(crate) fn unnecessary_generator_list(checker: &mut Checker, call: &ast::Expr
|
||||
) else {
|
||||
return;
|
||||
};
|
||||
if !checker.semantic().is_builtin("list") {
|
||||
if !checker.semantic().has_builtin_binding("list") {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ pub(crate) fn unnecessary_generator_set(checker: &mut Checker, call: &ast::ExprC
|
||||
) else {
|
||||
return;
|
||||
};
|
||||
if !checker.semantic().is_builtin("set") {
|
||||
if !checker.semantic().has_builtin_binding("set") {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ pub(crate) fn unnecessary_list_call(
|
||||
let Some(argument) = helpers::first_argument_with_matching_function("list", func, args) else {
|
||||
return;
|
||||
};
|
||||
if !checker.semantic().is_builtin("list") {
|
||||
if !checker.semantic().has_builtin_binding("list") {
|
||||
return;
|
||||
}
|
||||
if !argument.is_list_comp_expr() {
|
||||
|
||||
@@ -56,7 +56,7 @@ pub(crate) fn unnecessary_list_comprehension_dict(
|
||||
else {
|
||||
return;
|
||||
};
|
||||
if !checker.semantic().is_builtin("dict") {
|
||||
if !checker.semantic().has_builtin_binding("dict") {
|
||||
return;
|
||||
}
|
||||
let Expr::ListComp(ast::ExprListComp { elt, .. }) = argument else {
|
||||
|
||||
@@ -52,7 +52,7 @@ pub(crate) fn unnecessary_list_comprehension_set(checker: &mut Checker, call: &a
|
||||
) else {
|
||||
return;
|
||||
};
|
||||
if !checker.semantic().is_builtin("set") {
|
||||
if !checker.semantic().has_builtin_binding("set") {
|
||||
return;
|
||||
}
|
||||
if argument.is_list_comp_expr() {
|
||||
|
||||
@@ -63,7 +63,7 @@ pub(crate) fn unnecessary_literal_dict(
|
||||
else {
|
||||
return;
|
||||
};
|
||||
if !checker.semantic().is_builtin("dict") {
|
||||
if !checker.semantic().has_builtin_binding("dict") {
|
||||
return;
|
||||
}
|
||||
let (kind, elts) = match argument {
|
||||
|
||||
@@ -60,7 +60,7 @@ pub(crate) fn unnecessary_literal_set(checker: &mut Checker, call: &ast::ExprCal
|
||||
) else {
|
||||
return;
|
||||
};
|
||||
if !checker.semantic().is_builtin("set") {
|
||||
if !checker.semantic().has_builtin_binding("set") {
|
||||
return;
|
||||
}
|
||||
let kind = match argument {
|
||||
|
||||
@@ -75,7 +75,7 @@ pub(crate) fn unnecessary_literal_within_dict_call(checker: &mut Checker, call:
|
||||
else {
|
||||
return;
|
||||
};
|
||||
if !checker.semantic().is_builtin("dict") {
|
||||
if !checker.semantic().has_builtin_binding("dict") {
|
||||
return;
|
||||
}
|
||||
let argument_kind = match argument {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user