Compare commits
1 Commits
v0.1.6
...
schemastor
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d65addffa0 |
@@ -1,3 +1,37 @@
|
||||
[alias]
|
||||
dev = "run --package ruff_dev --bin ruff_dev"
|
||||
benchmark = "bench -p ruff_benchmark --bench linter --bench formatter --"
|
||||
|
||||
[target.'cfg(all())']
|
||||
rustflags = [
|
||||
# CLIPPY LINT SETTINGS
|
||||
# This is a workaround to configure lints for the entire workspace, pending the ability to configure this via TOML.
|
||||
# See: `https://github.com/rust-lang/cargo/issues/5034`
|
||||
# `https://github.com/EmbarkStudios/rust-ecosystem/issues/22#issuecomment-947011395`
|
||||
"-Dunsafe_code",
|
||||
"-Wclippy::pedantic",
|
||||
# Allowed pedantic lints
|
||||
"-Wclippy::char_lit_as_u8",
|
||||
"-Aclippy::collapsible_else_if",
|
||||
"-Aclippy::collapsible_if",
|
||||
"-Aclippy::implicit_hasher",
|
||||
"-Aclippy::match_same_arms",
|
||||
"-Aclippy::missing_errors_doc",
|
||||
"-Aclippy::missing_panics_doc",
|
||||
"-Aclippy::module_name_repetitions",
|
||||
"-Aclippy::must_use_candidate",
|
||||
"-Aclippy::similar_names",
|
||||
"-Aclippy::too_many_lines",
|
||||
# Disallowed restriction lints
|
||||
"-Wclippy::print_stdout",
|
||||
"-Wclippy::print_stderr",
|
||||
"-Wclippy::dbg_macro",
|
||||
"-Wclippy::empty_drop",
|
||||
"-Wclippy::empty_structs_with_brackets",
|
||||
"-Wclippy::exit",
|
||||
"-Wclippy::get_unwrap",
|
||||
"-Wclippy::rc_buffer",
|
||||
"-Wclippy::rc_mutex",
|
||||
"-Wclippy::rest_pat_in_fully_bound_structs",
|
||||
"-Wunreachable_pub"
|
||||
]
|
||||
|
||||
2
.github/workflows/ci.yaml
vendored
2
.github/workflows/ci.yaml
vendored
@@ -392,7 +392,7 @@ jobs:
|
||||
run: mkdocs build --strict -f mkdocs.insiders.yml
|
||||
- name: "Build docs"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS != 'true' }}
|
||||
run: mkdocs build --strict -f mkdocs.public.yml
|
||||
run: mkdocs build --strict -f mkdocs.generated.yml
|
||||
|
||||
check-formatter-instability-and-black-similarity:
|
||||
name: "formatter instabilities and black similarity"
|
||||
|
||||
2
.github/workflows/docs.yaml
vendored
2
.github/workflows/docs.yaml
vendored
@@ -44,7 +44,7 @@ jobs:
|
||||
run: mkdocs build --strict -f mkdocs.insiders.yml
|
||||
- name: "Build docs"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS != 'true' }}
|
||||
run: mkdocs build --strict -f mkdocs.public.yml
|
||||
run: mkdocs build --strict -f mkdocs.generated.yml
|
||||
- name: "Deploy to Cloudflare Pages"
|
||||
if: ${{ env.CF_API_TOKEN_EXISTS == 'true' }}
|
||||
uses: cloudflare/wrangler-action@v3.3.2
|
||||
|
||||
73
CHANGELOG.md
73
CHANGELOG.md
@@ -1,78 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## 0.1.6
|
||||
|
||||
### Preview features
|
||||
|
||||
- \[`flake8-boolean-trap`\] Extend `boolean-type-hint-positional-argument` (`FBT001`) to include booleans in unions ([#7501](https://github.com/astral-sh/ruff/pull/7501))
|
||||
- \[`flake8-pie`\] Extend `reimplemented-list-builtin` (`PIE807`) to `dict` reimplementations ([#8608](https://github.com/astral-sh/ruff/pull/8608))
|
||||
- \[`flake8-pie`\] Extend `unnecessary-pass` (`PIE790`) to include ellipses (`...`) ([#8641](https://github.com/astral-sh/ruff/pull/8641))
|
||||
- \[`flake8-pie`\] Implement fix for `unnecessary-spread` (`PIE800`) ([#8668](https://github.com/astral-sh/ruff/pull/8668))
|
||||
- \[`flake8-quotes`\] Implement `unnecessary-escaped-quote` (`Q004`) ([#8630](https://github.com/astral-sh/ruff/pull/8630))
|
||||
- \[`pycodestyle`\] Implement fix for `multiple-spaces-after-keyword` (`E271`) and `multiple-spaces-before-keyword` (`E272`) ([#8622](https://github.com/astral-sh/ruff/pull/8622))
|
||||
- \[`pycodestyle`\] Implement fix for `multiple-spaces-after-operator` (`E222`) and `multiple-spaces-before-operator` (`E221`) ([#8623](https://github.com/astral-sh/ruff/pull/8623))
|
||||
- \[`pyflakes`\] Extend `is-literal` (`F632`) to include comparisons against mutable initializers ([#8607](https://github.com/astral-sh/ruff/pull/8607))
|
||||
- \[`pylint`\] Implement `redefined-argument-from-local` (`PLR1704`) ([#8159](https://github.com/astral-sh/ruff/pull/8159))
|
||||
- \[`pylint`\] Implement fix for `unnecessary-lambda` (`PLW0108`) ([#8621](https://github.com/astral-sh/ruff/pull/8621))
|
||||
- \[`refurb`\] Implement `if-expr-min-max` (`FURB136`) ([#8664](https://github.com/astral-sh/ruff/pull/8664))
|
||||
- \[`refurb`\] Implement `math-constant` (`FURB152`) ([#8727](https://github.com/astral-sh/ruff/pull/8727))
|
||||
|
||||
### Rule changes
|
||||
|
||||
- \[`flake8-annotations`\] Add autotyping-like return type inference for annotation rules ([#8643](https://github.com/astral-sh/ruff/pull/8643))
|
||||
- \[`flake8-future-annotations`\] Implement fix for `future-required-type-annotation` (`FA102`) ([#8711](https://github.com/astral-sh/ruff/pull/8711))
|
||||
- \[`flake8-implicit-namespace-package`\] Avoid missing namespace violations in scripts with shebangs ([#8710](https://github.com/astral-sh/ruff/pull/8710))
|
||||
- \[`pydocstyle`\] Update `over-indentation` (`D208`) to preserve indentation offsets when fixing overindented lines ([#8699](https://github.com/astral-sh/ruff/pull/8699))
|
||||
- \[`pyupgrade`\] Refine `timeout-error-alias` (`UP041`) to remove false positives ([#8587](https://github.com/astral-sh/ruff/pull/8587))
|
||||
|
||||
### Formatter
|
||||
|
||||
- Fix instability in `await` formatting with fluent style ([#8676](https://github.com/astral-sh/ruff/pull/8676))
|
||||
- Compare formatted and unformatted ASTs during formatter tests ([#8624](https://github.com/astral-sh/ruff/pull/8624))
|
||||
- Preserve trailing semicolon for Notebooks ([#8590](https://github.com/astral-sh/ruff/pull/8590))
|
||||
|
||||
### CLI
|
||||
|
||||
- Improve debug printing for resolving origin of config settings ([#8729](https://github.com/astral-sh/ruff/pull/8729))
|
||||
- Write unchanged, excluded files to stdout when read via stdin ([#8596](https://github.com/astral-sh/ruff/pull/8596))
|
||||
|
||||
### Configuration
|
||||
|
||||
- \[`isort`\] Support disabling sections with `no-sections = true` ([#8657](https://github.com/astral-sh/ruff/pull/8657))
|
||||
- \[`pep8-naming`\] Support local and dynamic class- and static-method decorators ([#8592](https://github.com/astral-sh/ruff/pull/8592))
|
||||
- \[`pydocstyle`\] Allow overriding pydocstyle convention rules ([#8586](https://github.com/astral-sh/ruff/pull/8586))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Avoid syntax error via importing `trio.lowlevel` ([#8730](https://github.com/astral-sh/ruff/pull/8730))
|
||||
- Omit unrolled augmented assignments in `PIE794` ([#8634](https://github.com/astral-sh/ruff/pull/8634))
|
||||
- Slice source code instead of generating it for `EM` fixes ([#7746](https://github.com/astral-sh/ruff/pull/7746))
|
||||
- Allow whitespace around colon in slices for `whitespace-before-punctuation` (`E203`) ([#8654](https://github.com/astral-sh/ruff/pull/8654))
|
||||
- Use function range for `no-self-use` ([#8637](https://github.com/astral-sh/ruff/pull/8637))
|
||||
- F-strings doesn't contain bytes literal for `PLW0129` ([#8675](https://github.com/astral-sh/ruff/pull/8675))
|
||||
- Improve detection of `TYPE_CHECKING` blocks imported from `typing_extensions` or `_typeshed` ([#8429](https://github.com/astral-sh/ruff/pull/8429))
|
||||
- Treat display as a builtin in IPython ([#8707](https://github.com/astral-sh/ruff/pull/8707))
|
||||
- Avoid `FURB113` autofix if comments are present ([#8494](https://github.com/astral-sh/ruff/pull/8494))
|
||||
- Consider the new f-string tokens for `flake8-commas` ([#8582](https://github.com/astral-sh/ruff/pull/8582))
|
||||
- Remove erroneous bad-dunder-name reference ([#8742](https://github.com/astral-sh/ruff/pull/8742))
|
||||
- Avoid recommending Self usages in metaclasses ([#8639](https://github.com/astral-sh/ruff/pull/8639))
|
||||
- Detect runtime-evaluated base classes defined in the current file ([#8572](https://github.com/astral-sh/ruff/pull/8572))
|
||||
- Avoid inserting trailing commas within f-strings ([#8574](https://github.com/astral-sh/ruff/pull/8574))
|
||||
- Remove incorrect deprecation label for stdout and stderr ([#8743](https://github.com/astral-sh/ruff/pull/8743))
|
||||
- Fix unnecessary parentheses in UP007 fix ([#8610](https://github.com/astral-sh/ruff/pull/8610))
|
||||
- Remove repeated and erroneous scoped settings headers in docs ([#8670](https://github.com/astral-sh/ruff/pull/8670))
|
||||
- Trim trailing empty strings when converting to f-strings ([#8712](https://github.com/astral-sh/ruff/pull/8712))
|
||||
- Fix ordering for `force-sort-within-sections` ([#8665](https://github.com/astral-sh/ruff/pull/8665))
|
||||
- Run unicode prefix rule over tokens ([#8709](https://github.com/astral-sh/ruff/pull/8709))
|
||||
- Update UP032 to unescape curly braces in literal parts of converted strings ([#8697](https://github.com/astral-sh/ruff/pull/8697))
|
||||
- List all ipython builtins ([#8719](https://github.com/astral-sh/ruff/pull/8719))
|
||||
|
||||
### Documentation
|
||||
|
||||
- Document conventions in the FAQ ([#8638](https://github.com/astral-sh/ruff/pull/8638))
|
||||
- Redirect from rule codes to rule pages in docs ([#8636](https://github.com/astral-sh/ruff/pull/8636))
|
||||
- Fix permalink to convention setting ([#8575](https://github.com/astral-sh/ruff/pull/8575))
|
||||
|
||||
## 0.1.5
|
||||
|
||||
### Preview features
|
||||
|
||||
98
Cargo.lock
generated
98
Cargo.lock
generated
@@ -64,9 +64,9 @@ checksum = "c7021ce4924a3f25f802b2cccd1af585e39ea1a363a1aa2e72afe54b67a3a7a7"
|
||||
|
||||
[[package]]
|
||||
name = "annotate-snippets"
|
||||
version = "0.9.2"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccaf7e9dfbb6ab22c82e473cd1a8a7bd313c19a5b7e40970f3d89ef5a5c9e81e"
|
||||
checksum = "c3b9d411ecbaf79885c6df4d75fff75858d5995ff25385657a28af47e82f9c36"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
"yansi-term",
|
||||
@@ -278,7 +278,9 @@ checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
@@ -808,7 +810,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.1.6"
|
||||
version = "0.1.5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -827,7 +829,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"toml 0.7.8",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -857,12 +859,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fs-err"
|
||||
version = "2.10.0"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb5fd9bcbe8b1087cbd395b51498c01bc997cef73e778a80b77a811af5e2d29f"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541"
|
||||
|
||||
[[package]]
|
||||
name = "fsevent-sys"
|
||||
@@ -928,9 +927,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.2"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
|
||||
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
@@ -1034,12 +1033,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.1.0"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
|
||||
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.14.2",
|
||||
"hashbrown 0.14.0",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@@ -1802,37 +1801,36 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pyproject-toml"
|
||||
version = "0.8.1"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46d4a5e69187f23a29f8aa0ea57491d104ba541bc55f76552c2a74962aa20e04"
|
||||
checksum = "0774c13ff0b8b7ebb4791c050c497aefcfe3f6a222c0829c7017161ed38391ff"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"pep440_rs",
|
||||
"pep508_rs",
|
||||
"serde",
|
||||
"toml 0.8.2",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-junit"
|
||||
version = "0.3.5"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9599bffc2cd7511355996e0cfd979266b2cfa3f3ff5247d07a3a6e1ded6158"
|
||||
checksum = "6bf780b59d590c25f8c59b44c124166a2a93587868b619fb8f5b47fb15e9ed6d"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"indexmap",
|
||||
"nextest-workspace-hack",
|
||||
"quick-xml",
|
||||
"strip-ansi-escapes",
|
||||
"thiserror",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.31.0"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33"
|
||||
checksum = "81b9228215d82c7b61490fec1de287136b5de6f5700f6e58ea9ad61a7964ca51"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -2062,9 +2060,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_cli"
|
||||
version = "0.1.6"
|
||||
version = "0.1.5"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.2",
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
"argfile",
|
||||
"assert_cmd",
|
||||
@@ -2154,7 +2152,7 @@ dependencies = [
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"tempfile",
|
||||
"toml 0.7.8",
|
||||
"toml",
|
||||
"tracing",
|
||||
"tracing-indicatif",
|
||||
"tracing-subscriber",
|
||||
@@ -2198,10 +2196,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_linter"
|
||||
version = "0.1.6"
|
||||
version = "0.1.5"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"annotate-snippets 0.9.2",
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
"bitflags 2.4.1",
|
||||
"chrono",
|
||||
@@ -2254,7 +2252,7 @@ dependencies = [
|
||||
"tempfile",
|
||||
"test-case",
|
||||
"thiserror",
|
||||
"toml 0.7.8",
|
||||
"toml",
|
||||
"typed-arena",
|
||||
"unicode-width",
|
||||
"unicode_names2",
|
||||
@@ -2449,7 +2447,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_shrinking"
|
||||
version = "0.1.6"
|
||||
version = "0.1.5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -2540,7 +2538,7 @@ dependencies = [
|
||||
"shellexpand",
|
||||
"strum",
|
||||
"tempfile",
|
||||
"toml 0.7.8",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2804,9 +2802,9 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.11.2"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
|
||||
checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
@@ -2833,15 +2831,6 @@ dependencies = [
|
||||
"precomputed-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strip-ansi-escapes"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55ff8ef943b384c414f54aefa961dd2bd853add74ec75e7ac74cf91dba62bcfa"
|
||||
dependencies = [
|
||||
"vte",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
@@ -3097,19 +3086,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit 0.19.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit 0.20.2",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3134,19 +3111,6 @@ dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.40"
|
||||
|
||||
34
Cargo.toml
34
Cargo.toml
@@ -38,7 +38,7 @@ serde = { version = "1.0.190", features = ["derive"] }
|
||||
serde_json = { version = "1.0.108" }
|
||||
shellexpand = { version = "3.0.0" }
|
||||
similar = { version = "2.3.0", features = ["inline"] }
|
||||
smallvec = { version = "1.11.2" }
|
||||
smallvec = { version = "1.11.1" }
|
||||
static_assertions = "1.1.0"
|
||||
strum = { version = "0.25.0", features = ["strum_macros"] }
|
||||
strum_macros = { version = "0.25.3" }
|
||||
@@ -55,38 +55,6 @@ unicode-width = { version = "0.1.11" }
|
||||
uuid = { version = "1.5.0", features = ["v4", "fast-rng", "macro-diagnostics", "js"] }
|
||||
wsl = { version = "0.1.0" }
|
||||
|
||||
[workspace.lints.rust]
|
||||
unsafe_code = "warn"
|
||||
unreachable_pub = "warn"
|
||||
|
||||
[workspace.lints.clippy]
|
||||
pedantic = { level = "warn", priority = -2 }
|
||||
# Allowed pedantic lints
|
||||
char_lit_as_u8 = "allow"
|
||||
collapsible_else_if = "allow"
|
||||
collapsible_if = "allow"
|
||||
implicit_hasher = "allow"
|
||||
match_same_arms = "allow"
|
||||
missing_errors_doc = "allow"
|
||||
missing_panics_doc = "allow"
|
||||
module_name_repetitions = "allow"
|
||||
must_use_candidate = "allow"
|
||||
similar_names = "allow"
|
||||
too_many_lines = "allow"
|
||||
# To allow `#[allow(clippy::all)]` in `crates/ruff_python_parser/src/python.rs`.
|
||||
needless_raw_string_hashes = "allow"
|
||||
# Disallowed restriction lints
|
||||
print_stdout = "warn"
|
||||
print_stderr = "warn"
|
||||
dbg_macro = "warn"
|
||||
empty_drop = "warn"
|
||||
empty_structs_with_brackets = "warn"
|
||||
exit = "warn"
|
||||
get_unwrap = "warn"
|
||||
rc_buffer = "warn"
|
||||
rc_mutex = "warn"
|
||||
rest_pat_in_fully_bound_structs = "warn"
|
||||
|
||||
[profile.release]
|
||||
lto = "fat"
|
||||
codegen-units = 1
|
||||
|
||||
20
README.md
20
README.md
@@ -54,7 +54,7 @@ Ruff is extremely actively developed and used in major open-source projects like
|
||||
- [Pandas](https://github.com/pandas-dev/pandas)
|
||||
- [SciPy](https://github.com/scipy/scipy)
|
||||
|
||||
...and [many more](#whos-using-ruff).
|
||||
...and many more.
|
||||
|
||||
Ruff is backed by [Astral](https://astral.sh). Read the [launch post](https://astral.sh/blog/announcing-astral-the-company-behind-ruff),
|
||||
or the original [project announcement](https://notes.crmarsh.com/python-tooling-could-be-much-much-faster).
|
||||
@@ -150,7 +150,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.1.6
|
||||
rev: v0.1.5
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
@@ -377,8 +377,8 @@ Ruff is used by a number of major open-source projects and companies, including:
|
||||
- Anthropic ([Python SDK](https://github.com/anthropics/anthropic-sdk-python))
|
||||
- [Apache Airflow](https://github.com/apache/airflow)
|
||||
- AstraZeneca ([Magnus](https://github.com/AstraZeneca/magnus-core))
|
||||
- [Babel](https://github.com/python-babel/babel)
|
||||
- Benchling ([Refac](https://github.com/benchling/refac))
|
||||
- [Babel](https://github.com/python-babel/babel)
|
||||
- [Bokeh](https://github.com/bokeh/bokeh)
|
||||
- [Cryptography (PyCA)](https://github.com/pyca/cryptography)
|
||||
- [DVC](https://github.com/iterative/dvc)
|
||||
@@ -389,16 +389,15 @@ Ruff is used by a number of major open-source projects and companies, including:
|
||||
- [Gradio](https://github.com/gradio-app/gradio)
|
||||
- [Great Expectations](https://github.com/great-expectations/great_expectations)
|
||||
- [HTTPX](https://github.com/encode/httpx)
|
||||
- [Hatch](https://github.com/pypa/hatch)
|
||||
- [Home Assistant](https://github.com/home-assistant/core)
|
||||
- Hugging Face ([Transformers](https://github.com/huggingface/transformers),
|
||||
[Datasets](https://github.com/huggingface/datasets),
|
||||
[Diffusers](https://github.com/huggingface/diffusers))
|
||||
- [Hatch](https://github.com/pypa/hatch)
|
||||
- [Home Assistant](https://github.com/home-assistant/core)
|
||||
- ING Bank ([popmon](https://github.com/ing-bank/popmon), [probatus](https://github.com/ing-bank/probatus))
|
||||
- [Ibis](https://github.com/ibis-project/ibis)
|
||||
- [Jupyter](https://github.com/jupyter-server/jupyter_server)
|
||||
- [LangChain](https://github.com/hwchase17/langchain)
|
||||
- [Litestar](https://litestar.dev/)
|
||||
- [LlamaIndex](https://github.com/jerryjliu/llama_index)
|
||||
- Matrix ([Synapse](https://github.com/matrix-org/synapse))
|
||||
- [MegaLinter](https://github.com/oxsecurity/megalinter)
|
||||
@@ -423,21 +422,20 @@ Ruff is used by a number of major open-source projects and companies, including:
|
||||
- [PostHog](https://github.com/PostHog/posthog)
|
||||
- Prefect ([Python SDK](https://github.com/PrefectHQ/prefect), [Marvin](https://github.com/PrefectHQ/marvin))
|
||||
- [PyInstaller](https://github.com/pyinstaller/pyinstaller)
|
||||
- [PyMC-Marketing](https://github.com/pymc-labs/pymc-marketing)
|
||||
- [PyTorch](https://github.com/pytorch/pytorch)
|
||||
- [Pydantic](https://github.com/pydantic/pydantic)
|
||||
- [Pylint](https://github.com/PyCQA/pylint)
|
||||
- [PyMC-Marketing](https://github.com/pymc-labs/pymc-marketing)
|
||||
- [Reflex](https://github.com/reflex-dev/reflex)
|
||||
- [River](https://github.com/online-ml/river)
|
||||
- [Rippling](https://rippling.com)
|
||||
- [Robyn](https://github.com/sansyrox/robyn)
|
||||
- [Saleor](https://github.com/saleor/saleor)
|
||||
- Scale AI ([Launch SDK](https://github.com/scaleapi/launch-python-client))
|
||||
- [SciPy](https://github.com/scipy/scipy)
|
||||
- Snowflake ([SnowCLI](https://github.com/Snowflake-Labs/snowcli))
|
||||
- [Saleor](https://github.com/saleor/saleor)
|
||||
- [SciPy](https://github.com/scipy/scipy)
|
||||
- [Sphinx](https://github.com/sphinx-doc/sphinx)
|
||||
- [Stable Baselines3](https://github.com/DLR-RM/stable-baselines3)
|
||||
- [Starlette](https://github.com/encode/starlette)
|
||||
- [Litestar](https://litestar.dev/)
|
||||
- [The Algorithms](https://github.com/TheAlgorithms/Python)
|
||||
- [Vega-Altair](https://github.com/altair-viz/altair)
|
||||
- WordPress ([Openverse](https://github.com/WordPress/openverse))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.1.6"
|
||||
version = "0.1.5"
|
||||
description = """
|
||||
Convert Flake8 configuration files to Ruff configuration files.
|
||||
"""
|
||||
@@ -34,6 +34,3 @@ toml = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1.3.0"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -46,9 +46,6 @@ ruff_python_formatter = { path = "../ruff_python_formatter" }
|
||||
ruff_python_index = { path = "../ruff_python_index" }
|
||||
ruff_python_parser = { path = "../ruff_python_parser" }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[features]
|
||||
codspeed = ["codspeed-criterion-compat"]
|
||||
|
||||
|
||||
@@ -20,6 +20,3 @@ seahash = "4.1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
ruff_macros = { path = "../ruff_macros" }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_cli"
|
||||
version = "0.1.6"
|
||||
version = "0.1.5"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
@@ -28,7 +28,7 @@ ruff_python_trivia = { path = "../ruff_python_trivia" }
|
||||
ruff_workspace = { path = "../ruff_workspace" }
|
||||
ruff_text_size = { path = "../ruff_text_size" }
|
||||
|
||||
annotate-snippets = { version = "0.9.2", features = ["color"] }
|
||||
annotate-snippets = { version = "0.9.1", features = ["color"] }
|
||||
anyhow = { workspace = true }
|
||||
argfile = { version = "0.1.6" }
|
||||
bincode = { version = "1.3.3" }
|
||||
@@ -76,6 +76,3 @@ mimalloc = "0.1.39"
|
||||
|
||||
[target.'cfg(all(not(target_os = "windows"), not(target_os = "openbsd"), any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64")))'.dependencies]
|
||||
tikv-jemallocator = "0.5.0"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -1,413 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "4f8ce941-1492-4d4e-8ab5-70d733fe891a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%config ZMQInteractiveShell.ast_node_interactivity=\"last_expr_or_assign\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "721ec705-0c65-4bfb-9809-7ed8bc534186",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"1"
|
||||
]
|
||||
},
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Assignment statement without a semicolon\n",
|
||||
"x = 1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "de50e495-17e5-41cc-94bd-565757555d7e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Assignment statement with a semicolon\n",
|
||||
"x = 1;\n",
|
||||
"x = 1;"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "39e31201-23da-44eb-8684-41bba3663991",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"2"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Augmented assignment without a semicolon\n",
|
||||
"x += 1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "6b73d3dd-c73a-4697-9e97-e109a6c1fbab",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Augmented assignment without a semicolon\n",
|
||||
"x += 1;\n",
|
||||
"x += 1; # comment\n",
|
||||
"# comment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "2a3e5b86-aa5b-46ba-b9c6-0386d876f58c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Multiple assignment without a semicolon\n",
|
||||
"x = y = 1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "07f89e51-9357-4cfb-8fc5-76fb75e35949",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Multiple assignment with a semicolon\n",
|
||||
"x = y = 1;\n",
|
||||
"x = y = 1;"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "c22b539d-473e-48f8-a236-625e58c47a00",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Tuple unpacking without a semicolon\n",
|
||||
"x, y = 1, 2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "12c87940-a0d5-403b-a81c-7507eb06dc7e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Tuple unpacking with a semicolon (irrelevant)\n",
|
||||
"x, y = 1, 2;\n",
|
||||
"x, y = 1, 2; # comment\n",
|
||||
"# comment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"id": "5a768c76-6bc4-470c-b37e-8cc14bc6caf4",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"1"
|
||||
]
|
||||
},
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Annotated assignment statement without a semicolon\n",
|
||||
"x: int = 1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "21bfda82-1a9a-4ba1-9078-74ac480804b5",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Annotated assignment statement without a semicolon\n",
|
||||
"x: int = 1;\n",
|
||||
"x: int = 1; # comment\n",
|
||||
"# comment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"id": "09929999-ff29-4d10-ad2b-e665af15812d",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"1"
|
||||
]
|
||||
},
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Assignment expression without a semicolon\n",
|
||||
"(x := 1)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"id": "32a83217-1bad-4f61-855e-ffcdb119c763",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Assignment expression with a semicolon\n",
|
||||
"(x := 1);\n",
|
||||
"(x := 1); # comment\n",
|
||||
"# comment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"id": "61b81865-277e-4964-b03e-eb78f1f318eb",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"1"
|
||||
]
|
||||
},
|
||||
"execution_count": 14,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"x = 1\n",
|
||||
"# Expression without a semicolon\n",
|
||||
"x"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"id": "974c29be-67e1-4000-95fa-6ca118a63bad",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"x = 1\n",
|
||||
"# Expression with a semicolon\n",
|
||||
"x;"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 16,
|
||||
"id": "cfeb1757-46d6-4f13-969f-a283b6d0304f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"class Point:\n",
|
||||
" def __init__(self, x, y):\n",
|
||||
" self.x = x\n",
|
||||
" self.y = y\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"p = Point(0, 0);"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 17,
|
||||
"id": "2ee7f1a5-ccfe-4004-bfa4-ef834a58da97",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Assignment statement where the left is an attribute access doesn't\n",
|
||||
"# print the value.\n",
|
||||
"p.x = 1;"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 18,
|
||||
"id": "3e49370a-048b-474d-aa0a-3d1d4a73ad37",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"data = {}\n",
|
||||
"\n",
|
||||
"# Neither does the subscript node\n",
|
||||
"data[\"foo\"] = 1;"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 19,
|
||||
"id": "d594bdd3-eaa9-41ef-8cda-cf01bc273b2d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if (x := 1):\n",
|
||||
" # It should be the top level statement\n",
|
||||
" x"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 20,
|
||||
"id": "e532f0cf-80c7-42b7-8226-6002fcf74fb6",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"1"
|
||||
]
|
||||
},
|
||||
"execution_count": 20,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Parentheses with comments\n",
|
||||
"(\n",
|
||||
" x := 1 # comment\n",
|
||||
") # comment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 21,
|
||||
"id": "473c5d62-871b-46ed-8a34-27095243f462",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Parentheses with comments\n",
|
||||
"(\n",
|
||||
" x := 1 # comment\n",
|
||||
"); # comment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 22,
|
||||
"id": "8c3c2361-f49f-45fe-bbe3-7e27410a8a86",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'Hello world!'"
|
||||
]
|
||||
},
|
||||
"execution_count": 22,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"\"\"\"Hello world!\"\"\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 23,
|
||||
"id": "23dbe9b5-3f68-4890-ab2d-ab0dbfd0712a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"\"\"\"Hello world!\"\"\"; # comment\n",
|
||||
"# comment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 24,
|
||||
"id": "3ce33108-d95d-4c70-83d1-0d4fd36a2951",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'x = 1'"
|
||||
]
|
||||
},
|
||||
"execution_count": 24,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"x = 1\n",
|
||||
"f\"x = {x}\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 25,
|
||||
"id": "654a4a67-de43-4684-824a-9451c67db48f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"x = 1\n",
|
||||
"f\"x = {x}\";\n",
|
||||
"f\"x = {x}\"; # comment\n",
|
||||
"# comment"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python (ruff-playground)",
|
||||
"language": "python",
|
||||
"name": "ruff-playground"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -202,12 +202,12 @@ fn lint_path(
|
||||
match result {
|
||||
Ok(inner) => inner,
|
||||
Err(error) => {
|
||||
let message = r"This indicates a bug in Ruff. If you could open an issue at:
|
||||
let message = r#"This indicates a bug in Ruff. If you could open an issue at:
|
||||
|
||||
https://github.com/astral-sh/ruff/issues/new?title=%5BLinter%20panic%5D
|
||||
|
||||
...with the relevant file contents, the `pyproject.toml` settings, and the following stack trace, we'd be very appreciative!
|
||||
";
|
||||
"#;
|
||||
|
||||
error!(
|
||||
"{}{}{} {message}\n{error}",
|
||||
|
||||
@@ -8,7 +8,7 @@ use ruff_workspace::resolver::{match_exclusion, python_file_at_path, PyprojectCo
|
||||
|
||||
use crate::args::CliOverrides;
|
||||
use crate::diagnostics::{lint_stdin, Diagnostics};
|
||||
use crate::stdin::{parrot_stdin, read_from_stdin};
|
||||
use crate::stdin::read_from_stdin;
|
||||
|
||||
/// Run the linter over a single file, read from `stdin`.
|
||||
pub(crate) fn check_stdin(
|
||||
@@ -21,9 +21,6 @@ pub(crate) fn check_stdin(
|
||||
if pyproject_config.settings.file_resolver.force_exclude {
|
||||
if let Some(filename) = filename {
|
||||
if !python_file_at_path(filename, pyproject_config, overrides)? {
|
||||
if fix_mode.is_apply() {
|
||||
parrot_stdin()?;
|
||||
}
|
||||
return Ok(Diagnostics::default());
|
||||
}
|
||||
|
||||
@@ -32,17 +29,14 @@ pub(crate) fn check_stdin(
|
||||
.file_name()
|
||||
.is_some_and(|name| match_exclusion(filename, name, &lint_settings.exclude))
|
||||
{
|
||||
if fix_mode.is_apply() {
|
||||
parrot_stdin()?;
|
||||
}
|
||||
return Ok(Diagnostics::default());
|
||||
}
|
||||
}
|
||||
}
|
||||
let stdin = read_from_stdin()?;
|
||||
let package_root = filename.and_then(Path::parent).and_then(|path| {
|
||||
packaging::detect_package_root(path, &pyproject_config.settings.linter.namespace_packages)
|
||||
});
|
||||
let stdin = read_from_stdin()?;
|
||||
let mut diagnostics = lint_stdin(
|
||||
filename,
|
||||
package_root,
|
||||
|
||||
@@ -660,12 +660,12 @@ impl Display for FormatCommandError {
|
||||
}
|
||||
}
|
||||
Self::Panic(path, err) => {
|
||||
let message = r"This indicates a bug in Ruff. If you could open an issue at:
|
||||
let message = r#"This indicates a bug in Ruff. If you could open an issue at:
|
||||
|
||||
https://github.com/astral-sh/ruff/issues/new?title=%5BFormatter%20panic%5D
|
||||
|
||||
...with the relevant file contents, the `pyproject.toml` settings, and the following stack trace, we'd be very appreciative!
|
||||
";
|
||||
"#;
|
||||
if let Some(path) = path {
|
||||
write!(
|
||||
f,
|
||||
|
||||
@@ -15,7 +15,7 @@ use crate::commands::format::{
|
||||
FormatResult, FormattedSource,
|
||||
};
|
||||
use crate::resolve::resolve;
|
||||
use crate::stdin::{parrot_stdin, read_from_stdin};
|
||||
use crate::stdin::read_from_stdin;
|
||||
use crate::ExitStatus;
|
||||
|
||||
/// Run the formatter over a single file, read from `stdin`.
|
||||
@@ -34,9 +34,6 @@ pub(crate) fn format_stdin(cli: &FormatArguments, overrides: &CliOverrides) -> R
|
||||
if pyproject_config.settings.file_resolver.force_exclude {
|
||||
if let Some(filename) = cli.stdin_filename.as_deref() {
|
||||
if !python_file_at_path(filename, &pyproject_config, overrides)? {
|
||||
if mode.is_write() {
|
||||
parrot_stdin()?;
|
||||
}
|
||||
return Ok(ExitStatus::Success);
|
||||
}
|
||||
|
||||
@@ -45,9 +42,6 @@ pub(crate) fn format_stdin(cli: &FormatArguments, overrides: &CliOverrides) -> R
|
||||
.file_name()
|
||||
.is_some_and(|name| match_exclusion(filename, name, &format_settings.exclude))
|
||||
{
|
||||
if mode.is_write() {
|
||||
parrot_stdin()?;
|
||||
}
|
||||
return Ok(ExitStatus::Success);
|
||||
}
|
||||
}
|
||||
@@ -56,9 +50,6 @@ pub(crate) fn format_stdin(cli: &FormatArguments, overrides: &CliOverrides) -> R
|
||||
let path = cli.stdin_filename.as_deref();
|
||||
|
||||
let SourceType::Python(source_type) = path.map(SourceType::from).unwrap_or_default() else {
|
||||
if mode.is_write() {
|
||||
parrot_stdin()?;
|
||||
}
|
||||
return Ok(ExitStatus::Success);
|
||||
};
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ fn format_rule_text(rule: Rule) -> String {
|
||||
|
||||
if rule.is_preview() || rule.is_nursery() {
|
||||
output.push_str(
|
||||
r"This rule is in preview and is not stable. The `--preview` flag is required for use.",
|
||||
r#"This rule is in preview and is not stable. The `--preview` flag is required for use."#,
|
||||
);
|
||||
output.push('\n');
|
||||
output.push('\n');
|
||||
|
||||
@@ -43,7 +43,7 @@ pub fn resolve(
|
||||
{
|
||||
let settings = resolve_root_settings(&pyproject, Relativity::Cwd, overrides)?;
|
||||
debug!(
|
||||
"Using user-specified configuration file at: {}",
|
||||
"Using user specified pyproject.toml at {}",
|
||||
pyproject.display()
|
||||
);
|
||||
return Ok(PyprojectConfig::new(
|
||||
@@ -63,10 +63,7 @@ pub fn resolve(
|
||||
.as_ref()
|
||||
.unwrap_or(&path_dedot::CWD.as_path()),
|
||||
)? {
|
||||
debug!(
|
||||
"Using configuration file (via parent) at: {}",
|
||||
pyproject.display()
|
||||
);
|
||||
debug!("Using pyproject.toml (parent) at {}", pyproject.display());
|
||||
let settings = resolve_root_settings(&pyproject, Relativity::Parent, overrides)?;
|
||||
return Ok(PyprojectConfig::new(
|
||||
PyprojectDiscoveryStrategy::Hierarchical,
|
||||
@@ -80,10 +77,7 @@ pub fn resolve(
|
||||
// end up the "closest" `pyproject.toml` file for every Python file later on, so
|
||||
// these act as the "default" settings.)
|
||||
if let Some(pyproject) = pyproject::find_user_settings_toml() {
|
||||
debug!(
|
||||
"Using configuration file (via cwd) at: {}",
|
||||
pyproject.display()
|
||||
);
|
||||
debug!("Using pyproject.toml (cwd) at {}", pyproject.display());
|
||||
let settings = resolve_root_settings(&pyproject, Relativity::Cwd, overrides)?;
|
||||
return Ok(PyprojectConfig::new(
|
||||
PyprojectDiscoveryStrategy::Hierarchical,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::io;
|
||||
use std::io::{Read, Write};
|
||||
use std::io::Read;
|
||||
|
||||
/// Read a string from `stdin`.
|
||||
pub(crate) fn read_from_stdin() -> Result<String, io::Error> {
|
||||
@@ -7,11 +7,3 @@ pub(crate) fn read_from_stdin() -> Result<String, io::Error> {
|
||||
io::stdin().lock().read_to_string(&mut buffer)?;
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
/// Read bytes from `stdin` and write them to `stdout`.
|
||||
pub(crate) fn parrot_stdin() -> Result<(), io::Error> {
|
||||
let mut buffer = String::new();
|
||||
io::stdin().lock().read_to_string(&mut buffer)?;
|
||||
io::stdout().write_all(buffer.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -320,11 +320,6 @@ if __name__ == '__main__':
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
from test import say_hy
|
||||
|
||||
if __name__ == '__main__':
|
||||
say_hy("dear Ruff contributor")
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
Ok(())
|
||||
@@ -395,9 +390,9 @@ fn deprecated_options() -> Result<()> {
|
||||
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||
fs::write(
|
||||
&ruff_toml,
|
||||
r"
|
||||
r#"
|
||||
tab-size = 2
|
||||
",
|
||||
"#,
|
||||
)?;
|
||||
|
||||
insta::with_settings!({filters => vec![
|
||||
@@ -407,10 +402,10 @@ tab-size = 2
|
||||
.args(["format", "--config"])
|
||||
.arg(&ruff_toml)
|
||||
.arg("-")
|
||||
.pass_stdin(r"
|
||||
.pass_stdin(r#"
|
||||
if True:
|
||||
pass
|
||||
"), @r###"
|
||||
"#), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -443,9 +438,9 @@ format = "json"
|
||||
.args(["check", "--select", "F401", "--no-cache", "--config"])
|
||||
.arg(&ruff_toml)
|
||||
.arg("-")
|
||||
.pass_stdin(r"
|
||||
.pass_stdin(r#"
|
||||
import os
|
||||
"), @r###"
|
||||
"#), @r###"
|
||||
success: false
|
||||
exit_code: 2
|
||||
----- stdout -----
|
||||
@@ -818,432 +813,3 @@ fn test_diff_stdin_formatted() {
|
||||
----- stderr -----
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_notebook_trailing_semicolon() {
|
||||
let fixtures = Path::new("resources").join("test").join("fixtures");
|
||||
let unformatted = fs::read(fixtures.join("trailing_semicolon.ipynb")).unwrap();
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(["format", "--isolated", "--stdin-filename", "test.ipynb"])
|
||||
.arg("-")
|
||||
.pass_stdin(unformatted), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "4f8ce941-1492-4d4e-8ab5-70d733fe891a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%config ZMQInteractiveShell.ast_node_interactivity=\"last_expr_or_assign\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "721ec705-0c65-4bfb-9809-7ed8bc534186",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"1"
|
||||
]
|
||||
},
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Assignment statement without a semicolon\n",
|
||||
"x = 1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "de50e495-17e5-41cc-94bd-565757555d7e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Assignment statement with a semicolon\n",
|
||||
"x = 1\n",
|
||||
"x = 1;"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "39e31201-23da-44eb-8684-41bba3663991",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"2"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Augmented assignment without a semicolon\n",
|
||||
"x += 1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "6b73d3dd-c73a-4697-9e97-e109a6c1fbab",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Augmented assignment without a semicolon\n",
|
||||
"x += 1\n",
|
||||
"x += 1; # comment\n",
|
||||
"# comment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "2a3e5b86-aa5b-46ba-b9c6-0386d876f58c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Multiple assignment without a semicolon\n",
|
||||
"x = y = 1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "07f89e51-9357-4cfb-8fc5-76fb75e35949",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Multiple assignment with a semicolon\n",
|
||||
"x = y = 1\n",
|
||||
"x = y = 1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "c22b539d-473e-48f8-a236-625e58c47a00",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Tuple unpacking without a semicolon\n",
|
||||
"x, y = 1, 2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "12c87940-a0d5-403b-a81c-7507eb06dc7e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Tuple unpacking with a semicolon (irrelevant)\n",
|
||||
"x, y = 1, 2\n",
|
||||
"x, y = 1, 2 # comment\n",
|
||||
"# comment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"id": "5a768c76-6bc4-470c-b37e-8cc14bc6caf4",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"1"
|
||||
]
|
||||
},
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Annotated assignment statement without a semicolon\n",
|
||||
"x: int = 1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "21bfda82-1a9a-4ba1-9078-74ac480804b5",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Annotated assignment statement without a semicolon\n",
|
||||
"x: int = 1\n",
|
||||
"x: int = 1; # comment\n",
|
||||
"# comment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"id": "09929999-ff29-4d10-ad2b-e665af15812d",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"1"
|
||||
]
|
||||
},
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Assignment expression without a semicolon\n",
|
||||
"(x := 1)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"id": "32a83217-1bad-4f61-855e-ffcdb119c763",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Assignment expression with a semicolon\n",
|
||||
"(x := 1)\n",
|
||||
"(x := 1); # comment\n",
|
||||
"# comment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"id": "61b81865-277e-4964-b03e-eb78f1f318eb",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"1"
|
||||
]
|
||||
},
|
||||
"execution_count": 14,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"x = 1\n",
|
||||
"# Expression without a semicolon\n",
|
||||
"x"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"id": "974c29be-67e1-4000-95fa-6ca118a63bad",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"x = 1\n",
|
||||
"# Expression with a semicolon\n",
|
||||
"x;"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 16,
|
||||
"id": "cfeb1757-46d6-4f13-969f-a283b6d0304f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"class Point:\n",
|
||||
" def __init__(self, x, y):\n",
|
||||
" self.x = x\n",
|
||||
" self.y = y\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"p = Point(0, 0);"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 17,
|
||||
"id": "2ee7f1a5-ccfe-4004-bfa4-ef834a58da97",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Assignment statement where the left is an attribute access doesn't\n",
|
||||
"# print the value.\n",
|
||||
"p.x = 1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 18,
|
||||
"id": "3e49370a-048b-474d-aa0a-3d1d4a73ad37",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"data = {}\n",
|
||||
"\n",
|
||||
"# Neither does the subscript node\n",
|
||||
"data[\"foo\"] = 1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 19,
|
||||
"id": "d594bdd3-eaa9-41ef-8cda-cf01bc273b2d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if x := 1:\n",
|
||||
" # It should be the top level statement\n",
|
||||
" x"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 20,
|
||||
"id": "e532f0cf-80c7-42b7-8226-6002fcf74fb6",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"1"
|
||||
]
|
||||
},
|
||||
"execution_count": 20,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Parentheses with comments\n",
|
||||
"(\n",
|
||||
" x := 1 # comment\n",
|
||||
") # comment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 21,
|
||||
"id": "473c5d62-871b-46ed-8a34-27095243f462",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Parentheses with comments\n",
|
||||
"(\n",
|
||||
" x := 1 # comment\n",
|
||||
"); # comment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 22,
|
||||
"id": "8c3c2361-f49f-45fe-bbe3-7e27410a8a86",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'Hello world!'"
|
||||
]
|
||||
},
|
||||
"execution_count": 22,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"\"\"\"Hello world!\"\"\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 23,
|
||||
"id": "23dbe9b5-3f68-4890-ab2d-ab0dbfd0712a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"\"\"\"Hello world!\"\"\"; # comment\n",
|
||||
"# comment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 24,
|
||||
"id": "3ce33108-d95d-4c70-83d1-0d4fd36a2951",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'x = 1'"
|
||||
]
|
||||
},
|
||||
"execution_count": 24,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"x = 1\n",
|
||||
"f\"x = {x}\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 25,
|
||||
"id": "654a4a67-de43-4684-824a-9451c67db48f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"x = 1\n",
|
||||
"f\"x = {x}\"\n",
|
||||
"f\"x = {x}\"; # comment\n",
|
||||
"# comment"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python (ruff-playground)",
|
||||
"language": "python",
|
||||
"name": "ruff-playground"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
}
|
||||
|
||||
@@ -1564,62 +1564,3 @@ extend-safe-fixes = ["UP03"]
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_docstring_conventions_overrides() -> Result<()> {
|
||||
// But if we explicitly select it, we override the convention
|
||||
let tempdir = TempDir::new()?;
|
||||
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||
fs::write(
|
||||
&ruff_toml,
|
||||
r#"
|
||||
[lint.pydocstyle]
|
||||
convention = "numpy"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let stdin = r#"
|
||||
def log(x, base) -> float:
|
||||
"""Calculate natural log of a value
|
||||
|
||||
Parameters
|
||||
----------
|
||||
x :
|
||||
Hello
|
||||
"""
|
||||
return math.log(x)
|
||||
"#;
|
||||
|
||||
// If we only select the prefix, then everything passes
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(["check", "-", "--config"])
|
||||
.arg(&ruff_toml)
|
||||
.args(["--output-format", "text", "--no-cache", "--select", "D41"])
|
||||
.pass_stdin(stdin),
|
||||
@r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
"###
|
||||
);
|
||||
|
||||
// But if we select the exact code, we get an error
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(["check", "-", "--config"])
|
||||
.arg(&ruff_toml)
|
||||
.args(["--output-format", "text", "--no-cache", "--select", "D417"])
|
||||
.pass_stdin(stdin),
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:2:5: D417 Missing argument description in the docstring for `log`: `base`
|
||||
Found 1 error.
|
||||
|
||||
----- stderr -----
|
||||
"###
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -48,12 +48,9 @@ tracing-indicatif = { workspace = true }
|
||||
tracing-subscriber = { workspace = true, features = ["env-filter"] }
|
||||
imara-diff = "0.1.5"
|
||||
|
||||
[dev-dependencies]
|
||||
indoc = "2.0.4"
|
||||
|
||||
[features]
|
||||
# Turn off rayon for profiling
|
||||
singlethreaded = []
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
[dev-dependencies]
|
||||
indoc = "2.0.4"
|
||||
|
||||
@@ -49,7 +49,7 @@ pub(crate) fn main(args: &Args) -> Result<()> {
|
||||
|
||||
if rule.is_preview() || rule.is_nursery() {
|
||||
output.push_str(
|
||||
r"This rule is unstable and in [preview](../preview.md). The `--preview` flag is required for use.",
|
||||
r#"This rule is unstable and in [preview](../preview.md). The `--preview` flag is required for use."#,
|
||||
);
|
||||
output.push('\n');
|
||||
output.push('\n');
|
||||
|
||||
@@ -129,12 +129,12 @@ fn emit_field(output: &mut String, name: &str, field: &OptionField, parent_set:
|
||||
output.push_str("**Example usage**:\n\n");
|
||||
output.push_str(&format_tab(
|
||||
"pyproject.toml",
|
||||
&format_header(field.scope, parent_set, ConfigurationFile::PyprojectToml),
|
||||
&format_header(parent_set, ConfigurationFile::PyprojectToml),
|
||||
field.example,
|
||||
));
|
||||
output.push_str(&format_tab(
|
||||
"ruff.toml",
|
||||
&format_header(field.scope, parent_set, ConfigurationFile::RuffToml),
|
||||
&format_header(parent_set, ConfigurationFile::RuffToml),
|
||||
field.example,
|
||||
));
|
||||
output.push('\n');
|
||||
@@ -149,53 +149,23 @@ fn format_tab(tab_name: &str, header: &str, content: &str) -> String {
|
||||
)
|
||||
}
|
||||
|
||||
/// Format the TOML header for the example usage for a given option.
|
||||
///
|
||||
/// For example: `[tool.ruff.format]` or `[tool.ruff.lint.isort]`.
|
||||
fn format_header(
|
||||
scope: Option<&str>,
|
||||
parent_set: &Set,
|
||||
configuration: ConfigurationFile,
|
||||
) -> String {
|
||||
match configuration {
|
||||
ConfigurationFile::PyprojectToml => {
|
||||
let mut header = if let Some(set_name) = parent_set.name() {
|
||||
if set_name == "format" {
|
||||
String::from("tool.ruff.format")
|
||||
} else {
|
||||
format!("tool.ruff.lint.{set_name}")
|
||||
}
|
||||
} else {
|
||||
"tool.ruff".to_string()
|
||||
};
|
||||
if let Some(scope) = scope {
|
||||
if !header.is_empty() {
|
||||
header.push('.');
|
||||
}
|
||||
header.push_str(scope);
|
||||
}
|
||||
format!("[{header}]")
|
||||
fn format_header(parent_set: &Set, configuration: ConfigurationFile) -> String {
|
||||
let fmt = if let Some(set_name) = parent_set.name() {
|
||||
if set_name == "format" {
|
||||
String::from(".format")
|
||||
} else {
|
||||
format!(".lint.{set_name}")
|
||||
}
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
match configuration {
|
||||
ConfigurationFile::PyprojectToml => format!("[tool.ruff{fmt}]"),
|
||||
ConfigurationFile::RuffToml => {
|
||||
let mut header = if let Some(set_name) = parent_set.name() {
|
||||
if set_name == "format" {
|
||||
String::from("format")
|
||||
} else {
|
||||
format!("lint.{set_name}")
|
||||
}
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
if let Some(scope) = scope {
|
||||
if !header.is_empty() {
|
||||
header.push('.');
|
||||
}
|
||||
header.push_str(scope);
|
||||
}
|
||||
if header.is_empty() {
|
||||
if fmt.is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
format!("[{header}]")
|
||||
format!("[{}]", fmt.strip_prefix('.').unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,3 @@ insta = { workspace = true }
|
||||
[features]
|
||||
serde = ["dep:serde", "ruff_text_size/serde"]
|
||||
schemars = ["dep:schemars", "ruff_text_size/schemars"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -1711,14 +1711,14 @@ mod tests {
|
||||
));
|
||||
|
||||
assert_eq!(
|
||||
"a
|
||||
r#"a
|
||||
b
|
||||
c
|
||||
d
|
||||
d
|
||||
c
|
||||
b
|
||||
a",
|
||||
a"#,
|
||||
formatted.as_code()
|
||||
);
|
||||
}
|
||||
@@ -2047,10 +2047,10 @@ two lines`,
|
||||
|
||||
assert_eq!(
|
||||
printed.as_code(),
|
||||
"Group with id-2
|
||||
r#"Group with id-2
|
||||
Group with id-1 does not fit on the line because it exceeds the line width of 80 characters by
|
||||
Group 2 fits
|
||||
Group 1 breaks"
|
||||
Group 1 breaks"#
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,3 @@ ruff_macros = { path = "../ruff_macros" }
|
||||
|
||||
[dev-dependencies]
|
||||
static_assertions = "1.1.0"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_linter"
|
||||
version = "0.1.6"
|
||||
version = "0.1.5"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
@@ -30,7 +30,7 @@ ruff_source_file = { path = "../ruff_source_file", features = ["serde"] }
|
||||
ruff_text_size = { path = "../ruff_text_size" }
|
||||
|
||||
aho-corasick = { version = "1.1.2" }
|
||||
annotate-snippets = { version = "0.9.2", features = ["color"] }
|
||||
annotate-snippets = { version = "0.9.1", features = ["color"] }
|
||||
anyhow = { workspace = true }
|
||||
bitflags = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
@@ -53,8 +53,8 @@ path-absolutize = { workspace = true, features = [
|
||||
] }
|
||||
pathdiff = { version = "0.2.1" }
|
||||
pep440_rs = { version = "0.3.12", features = ["serde"] }
|
||||
pyproject-toml = { version = "0.8.1" }
|
||||
quick-junit = { version = "0.3.5" }
|
||||
pyproject-toml = { version = "0.8.0" }
|
||||
quick-junit = { version = "0.3.2" }
|
||||
regex = { workspace = true }
|
||||
result-like = { version = "0.4.6" }
|
||||
rustc-hash = { workspace = true }
|
||||
@@ -86,6 +86,3 @@ default = []
|
||||
schemars = ["dep:schemars"]
|
||||
# Enables the UnreachableCode rule
|
||||
unreachable-code = []
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
3
crates/ruff_linter/resources/test/fixtures/README.md
vendored
Normal file
3
crates/ruff_linter/resources/test/fixtures/README.md
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# fixtures
|
||||
|
||||
Fixture files used for snapshot testing.
|
||||
@@ -1,32 +0,0 @@
|
||||
def func():
|
||||
return 1
|
||||
|
||||
|
||||
def func():
|
||||
return 1.5
|
||||
|
||||
|
||||
def func(x: int):
|
||||
if x > 0:
|
||||
return 1
|
||||
else:
|
||||
return 1.5
|
||||
|
||||
|
||||
def func():
|
||||
return True
|
||||
|
||||
|
||||
def func(x: int):
|
||||
if x > 0:
|
||||
return None
|
||||
else:
|
||||
return
|
||||
|
||||
|
||||
def func(x: int):
|
||||
return 1 or 2.5 if x > 0 else 1.5 or "str"
|
||||
|
||||
|
||||
def func(x: int):
|
||||
return 1 + 2.5 if x > 0 else 1.5 or "str"
|
||||
@@ -91,18 +91,3 @@ class Registry:
|
||||
|
||||
def foo(self) -> None:
|
||||
object.__setattr__(self, "flag", True)
|
||||
|
||||
|
||||
from typing import Optional, Union
|
||||
|
||||
|
||||
def func(x: Union[list, Optional[int | str | float | bool]]):
|
||||
pass
|
||||
|
||||
|
||||
def func(x: bool | str):
|
||||
pass
|
||||
|
||||
|
||||
def func(x: int | str):
|
||||
pass
|
||||
|
||||
@@ -639,18 +639,3 @@ foo = namedtuple(
|
||||
:20
|
||||
],
|
||||
)
|
||||
|
||||
# F-strings
|
||||
kwargs.pop("remove", f"this {trailing_comma}",)
|
||||
|
||||
raise Exception(
|
||||
"first", extra=f"Add trailing comma here ->"
|
||||
)
|
||||
|
||||
assert False, f"<- This is not a trailing comma"
|
||||
|
||||
f"""This is a test. {
|
||||
"Another sentence."
|
||||
if True else
|
||||
"Don't add a trailing comma here ->"
|
||||
}"""
|
||||
|
||||
@@ -148,32 +148,3 @@ for i in range(10):
|
||||
for i in range(10):
|
||||
pass # comment
|
||||
pass
|
||||
|
||||
|
||||
def foo():
|
||||
print("foo")
|
||||
...
|
||||
|
||||
|
||||
def foo():
|
||||
"""A docstring."""
|
||||
print("foo")
|
||||
...
|
||||
|
||||
|
||||
for i in range(10):
|
||||
...
|
||||
...
|
||||
|
||||
for i in range(10):
|
||||
...
|
||||
|
||||
...
|
||||
|
||||
for i in range(10):
|
||||
... # comment
|
||||
...
|
||||
|
||||
for i in range(10):
|
||||
...
|
||||
pass
|
||||
|
||||
@@ -38,15 +38,3 @@ class User:
|
||||
foo: bool = BooleanField()
|
||||
# ...
|
||||
bar = StringField() # PIE794
|
||||
|
||||
|
||||
class Person:
|
||||
name = "Foo"
|
||||
name = name + " Bar"
|
||||
name = "Bar" # PIE794
|
||||
|
||||
|
||||
class Person:
|
||||
name: str = "Foo"
|
||||
name: str = name + " Bar"
|
||||
name: str = "Bar" # PIE794
|
||||
|
||||
@@ -1,21 +1,9 @@
|
||||
{"foo": 1, **{"bar": 1}} # PIE800
|
||||
|
||||
{**{"bar": 10}, "a": "b"} # PIE800
|
||||
|
||||
foo({**foo, **{"bar": True}}) # PIE800
|
||||
|
||||
{**foo, **{"bar": 10}} # PIE800
|
||||
|
||||
{ # PIE800
|
||||
"a": "b",
|
||||
# Preserve
|
||||
**{
|
||||
# all
|
||||
"bar": 10, # the
|
||||
# comments
|
||||
},
|
||||
}
|
||||
|
||||
{**foo, **buzz, **{bar: 10}} # PIE800
|
||||
|
||||
{**foo, "bar": True } # OK
|
||||
|
||||
@@ -1,37 +1,27 @@
|
||||
@dataclass
|
||||
class Foo:
|
||||
foo: List[str] = field(default_factory=lambda: []) # PIE807
|
||||
bar: Dict[str, int] = field(default_factory=lambda: {}) # PIE807
|
||||
|
||||
|
||||
class FooTable(BaseTable):
|
||||
foo = fields.ListField(default=lambda: []) # PIE807
|
||||
bar = fields.ListField(default=lambda: {}) # PIE807
|
||||
bar = fields.ListField(default=lambda: []) # PIE807
|
||||
|
||||
|
||||
class FooTable(BaseTable):
|
||||
foo = fields.ListField(lambda: []) # PIE807
|
||||
bar = fields.ListField(default=lambda: {}) # PIE807
|
||||
bar = fields.ListField(lambda: []) # PIE807
|
||||
|
||||
|
||||
@dataclass
|
||||
class Foo:
|
||||
foo: List[str] = field(default_factory=list)
|
||||
bar: Dict[str, int] = field(default_factory=dict)
|
||||
|
||||
|
||||
class FooTable(BaseTable):
|
||||
foo = fields.ListField(list)
|
||||
bar = fields.ListField(dict)
|
||||
bar = fields.ListField(list)
|
||||
|
||||
|
||||
lambda *args, **kwargs: []
|
||||
lambda *args, **kwargs: {}
|
||||
|
||||
lambda *args: []
|
||||
lambda *args: {}
|
||||
|
||||
lambda **kwargs: []
|
||||
lambda **kwargs: {}
|
||||
|
||||
lambda: {**unwrap}
|
||||
|
||||
@@ -3,11 +3,9 @@
|
||||
import abc
|
||||
import builtins
|
||||
import collections.abc
|
||||
import enum
|
||||
import typing
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from abc import abstractmethod
|
||||
from collections.abc import AsyncIterable, AsyncIterator, Iterable, Iterator
|
||||
from enum import EnumMeta
|
||||
from typing import Any, overload
|
||||
|
||||
import typing_extensions
|
||||
@@ -201,31 +199,6 @@ class AsyncIteratorReturningAsyncIterable:
|
||||
... # Y045 "__aiter__" methods should return an AsyncIterator, not an AsyncIterable
|
||||
|
||||
|
||||
class MetaclassInWhichSelfCannotBeUsed(type):
|
||||
def __new__(cls) -> MetaclassInWhichSelfCannotBeUsed: ...
|
||||
def __enter__(self) -> MetaclassInWhichSelfCannotBeUsed: ...
|
||||
async def __aenter__(self) -> MetaclassInWhichSelfCannotBeUsed: ...
|
||||
def __isub__(self, other: MetaclassInWhichSelfCannotBeUsed) -> MetaclassInWhichSelfCannotBeUsed: ...
|
||||
|
||||
class MetaclassInWhichSelfCannotBeUsed2(EnumMeta):
|
||||
def __new__(cls) -> MetaclassInWhichSelfCannotBeUsed2: ...
|
||||
def __enter__(self) -> MetaclassInWhichSelfCannotBeUsed2: ...
|
||||
async def __aenter__(self) -> MetaclassInWhichSelfCannotBeUsed2: ...
|
||||
def __isub__(self, other: MetaclassInWhichSelfCannotBeUsed2) -> MetaclassInWhichSelfCannotBeUsed2: ...
|
||||
|
||||
class MetaclassInWhichSelfCannotBeUsed3(enum.EnumType):
|
||||
def __new__(cls) -> MetaclassInWhichSelfCannotBeUsed3: ...
|
||||
def __enter__(self) -> MetaclassInWhichSelfCannotBeUsed3: ...
|
||||
async def __aenter__(self) -> MetaclassInWhichSelfCannotBeUsed3: ...
|
||||
def __isub__(self, other: MetaclassInWhichSelfCannotBeUsed3) -> MetaclassInWhichSelfCannotBeUsed3: ...
|
||||
|
||||
class MetaclassInWhichSelfCannotBeUsed4(ABCMeta):
|
||||
def __new__(cls) -> MetaclassInWhichSelfCannotBeUsed4: ...
|
||||
def __enter__(self) -> MetaclassInWhichSelfCannotBeUsed4: ...
|
||||
async def __aenter__(self) -> MetaclassInWhichSelfCannotBeUsed4: ...
|
||||
def __isub__(self, other: MetaclassInWhichSelfCannotBeUsed4) -> MetaclassInWhichSelfCannotBeUsed4: ...
|
||||
|
||||
|
||||
class Abstract(Iterator[str]):
|
||||
@abstractmethod
|
||||
def __iter__(self) -> Iterator[str]:
|
||||
|
||||
@@ -3,11 +3,9 @@
|
||||
import abc
|
||||
import builtins
|
||||
import collections.abc
|
||||
import enum
|
||||
import typing
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from abc import abstractmethod
|
||||
from collections.abc import AsyncIterable, AsyncIterator, Iterable, Iterator
|
||||
from enum import EnumMeta
|
||||
from typing import Any, overload
|
||||
|
||||
import typing_extensions
|
||||
@@ -154,30 +152,6 @@ class AsyncIteratorReturningAsyncIterable:
|
||||
str
|
||||
]: ... # Y045 "__aiter__" methods should return an AsyncIterator, not an AsyncIterable
|
||||
|
||||
class MetaclassInWhichSelfCannotBeUsed(type):
|
||||
def __new__(cls) -> MetaclassInWhichSelfCannotBeUsed: ...
|
||||
def __enter__(self) -> MetaclassInWhichSelfCannotBeUsed: ...
|
||||
async def __aenter__(self) -> MetaclassInWhichSelfCannotBeUsed: ...
|
||||
def __isub__(self, other: MetaclassInWhichSelfCannotBeUsed) -> MetaclassInWhichSelfCannotBeUsed: ...
|
||||
|
||||
class MetaclassInWhichSelfCannotBeUsed2(EnumMeta):
|
||||
def __new__(cls) -> MetaclassInWhichSelfCannotBeUsed2: ...
|
||||
def __enter__(self) -> MetaclassInWhichSelfCannotBeUsed2: ...
|
||||
async def __aenter__(self) -> MetaclassInWhichSelfCannotBeUsed2: ...
|
||||
def __isub__(self, other: MetaclassInWhichSelfCannotBeUsed2) -> MetaclassInWhichSelfCannotBeUsed2: ...
|
||||
|
||||
class MetaclassInWhichSelfCannotBeUsed3(enum.EnumType):
|
||||
def __new__(cls) -> MetaclassInWhichSelfCannotBeUsed3: ...
|
||||
def __enter__(self) -> MetaclassInWhichSelfCannotBeUsed3: ...
|
||||
async def __aenter__(self) -> MetaclassInWhichSelfCannotBeUsed3: ...
|
||||
def __isub__(self, other: MetaclassInWhichSelfCannotBeUsed3) -> MetaclassInWhichSelfCannotBeUsed3: ...
|
||||
|
||||
class MetaclassInWhichSelfCannotBeUsed4(ABCMeta):
|
||||
def __new__(cls) -> MetaclassInWhichSelfCannotBeUsed4: ...
|
||||
def __enter__(self) -> MetaclassInWhichSelfCannotBeUsed4: ...
|
||||
async def __aenter__(self) -> MetaclassInWhichSelfCannotBeUsed4: ...
|
||||
def __isub__(self, other: MetaclassInWhichSelfCannotBeUsed4) -> MetaclassInWhichSelfCannotBeUsed4: ...
|
||||
|
||||
class Abstract(Iterator[str]):
|
||||
@abstractmethod
|
||||
def __iter__(self) -> Iterator[str]: ...
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
this_should_raise_Q004 = 'This is a \"string\"'
|
||||
this_should_raise_Q004 = 'This is \\ a \\\"string\"'
|
||||
this_is_fine = '"This" is a \"string\"'
|
||||
this_is_fine = "This is a 'string'"
|
||||
this_is_fine = "\"This\" is a 'string'"
|
||||
this_is_fine = r'This is a \"string\"'
|
||||
this_is_fine = R'This is a \"string\"'
|
||||
this_should_raise_Q004 = (
|
||||
'This is a'
|
||||
'\"string\"'
|
||||
)
|
||||
|
||||
# Same as above, but with f-strings
|
||||
f'This is a \"string\"' # Q004
|
||||
f'This is \\ a \\\"string\"' # Q004
|
||||
f'"This" is a \"string\"'
|
||||
f"This is a 'string'"
|
||||
f"\"This\" is a 'string'"
|
||||
fr'This is a \"string\"'
|
||||
fR'This is a \"string\"'
|
||||
this_should_raise_Q004 = (
|
||||
f'This is a'
|
||||
f'\"string\"' # Q004
|
||||
)
|
||||
|
||||
# Nested f-strings (Python 3.12+)
|
||||
#
|
||||
# The first one is interesting because the fix for it is valid pre 3.12:
|
||||
#
|
||||
# f"'foo' {'nested'}"
|
||||
#
|
||||
# but as the actual string itself is invalid pre 3.12, we don't catch it.
|
||||
f'\"foo\" {'nested'}' # Q004
|
||||
f'\"foo\" {f'nested'}' # Q004
|
||||
f'\"foo\" {f'\"nested\"'} \"\"' # Q004
|
||||
|
||||
f'normal {f'nested'} normal'
|
||||
f'\"normal\" {f'nested'} normal' # Q004
|
||||
f'\"normal\" {f'nested'} "double quotes"'
|
||||
f'\"normal\" {f'\"nested\" {'other'} normal'} "double quotes"' # Q004
|
||||
f'\"normal\" {f'\"nested\" {'other'} "double quotes"'} normal' # Q004
|
||||
|
||||
# Make sure we do not unescape quotes
|
||||
this_is_fine = 'This is an \\"escaped\\" quote'
|
||||
this_should_raise_Q004 = 'This is an \\\"escaped\\\" quote with an extra backslash'
|
||||
@@ -1,43 +0,0 @@
|
||||
this_should_raise_Q004 = "This is a \'string\'"
|
||||
this_should_raise_Q004 = "'This' is a \'string\'"
|
||||
this_is_fine = 'This is a "string"'
|
||||
this_is_fine = '\'This\' is a "string"'
|
||||
this_is_fine = r"This is a \'string\'"
|
||||
this_is_fine = R"This is a \'string\'"
|
||||
this_should_raise_Q004 = (
|
||||
"This is a"
|
||||
"\'string\'"
|
||||
)
|
||||
|
||||
# Same as above, but with f-strings
|
||||
f"This is a \'string\'" # Q004
|
||||
f"'This' is a \'string\'" # Q004
|
||||
f'This is a "string"'
|
||||
f'\'This\' is a "string"'
|
||||
fr"This is a \'string\'"
|
||||
fR"This is a \'string\'"
|
||||
this_should_raise_Q004 = (
|
||||
f"This is a"
|
||||
f"\'string\'" # Q004
|
||||
)
|
||||
|
||||
# Nested f-strings (Python 3.12+)
|
||||
#
|
||||
# The first one is interesting because the fix for it is valid pre 3.12:
|
||||
#
|
||||
# f'"foo" {"nested"}'
|
||||
#
|
||||
# but as the actual string itself is invalid pre 3.12, we don't catch it.
|
||||
f"\'foo\' {"foo"}" # Q004
|
||||
f"\'foo\' {f"foo"}" # Q004
|
||||
f"\'foo\' {f"\'foo\'"} \'\'" # Q004
|
||||
|
||||
f"normal {f"nested"} normal"
|
||||
f"\'normal\' {f"nested"} normal" # Q004
|
||||
f"\'normal\' {f"nested"} 'single quotes'"
|
||||
f"\'normal\' {f"\'nested\' {"other"} normal"} 'single quotes'" # Q004
|
||||
f"\'normal\' {f"\'nested\' {"other"} 'single quotes'"} normal" # Q004
|
||||
|
||||
# Make sure we do not unescape quotes
|
||||
this_is_fine = "This is an \\'escaped\\' quote"
|
||||
this_should_raise_Q004 = "This is an \\\'escaped\\\' quote with an extra backslash"
|
||||
@@ -1,7 +1,8 @@
|
||||
async def func():
|
||||
import trio
|
||||
from trio import sleep
|
||||
import trio
|
||||
from trio import sleep
|
||||
|
||||
|
||||
async def func():
|
||||
await trio.sleep(0) # TRIO115
|
||||
await trio.sleep(1) # OK
|
||||
await trio.sleep(0, 1) # OK
|
||||
@@ -20,11 +21,8 @@ async def func():
|
||||
trio.sleep(bar)
|
||||
|
||||
|
||||
trio.sleep(0) # TRIO115
|
||||
|
||||
|
||||
def func():
|
||||
trio.run(trio.sleep(0)) # TRIO115
|
||||
|
||||
|
||||
from trio import Event, sleep
|
||||
|
||||
def func():
|
||||
sleep(0) # TRIO115
|
||||
|
||||
@@ -172,14 +172,3 @@ def f():
|
||||
from module import Member
|
||||
|
||||
x: Member = 1
|
||||
|
||||
|
||||
def f():
|
||||
from typing_extensions import TYPE_CHECKING
|
||||
|
||||
from pandas import y
|
||||
|
||||
if TYPE_CHECKING:
|
||||
_type = x
|
||||
elif True:
|
||||
_type = y
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing_extensions import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pandas import DataFrame
|
||||
|
||||
|
||||
def example() -> DataFrame:
|
||||
pass
|
||||
@@ -1,10 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing_extensions import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pandas import DataFrame
|
||||
|
||||
|
||||
def example() -> DataFrame:
|
||||
x = DataFrame()
|
||||
@@ -37,9 +37,3 @@ if False:
|
||||
|
||||
if 0:
|
||||
x: List
|
||||
|
||||
|
||||
from typing_extensions import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass # TCH005
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing_extensions import Self
|
||||
|
||||
|
||||
def func():
|
||||
from pandas import DataFrame
|
||||
|
||||
df: DataFrame
|
||||
@@ -1,9 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import typing_extensions
|
||||
|
||||
|
||||
def func():
|
||||
from pandas import DataFrame
|
||||
|
||||
df: DataFrame
|
||||
@@ -1,5 +0,0 @@
|
||||
import encodings
|
||||
from datetime import timezone as tz
|
||||
from datetime import timedelta
|
||||
import datetime as dt
|
||||
import datetime
|
||||
@@ -1,8 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import django.settings
|
||||
import os
|
||||
import pytz
|
||||
import sys
|
||||
from . import local
|
||||
from library import foo
|
||||
@@ -63,33 +63,3 @@ class PosOnlyClass:
|
||||
|
||||
def bad_method_pos_only(this, blah, /, self, something: str):
|
||||
pass
|
||||
|
||||
|
||||
class ModelClass:
|
||||
@hybrid_property
|
||||
def bad(cls):
|
||||
pass
|
||||
|
||||
@bad.expression
|
||||
def bad(self):
|
||||
pass
|
||||
|
||||
@bad.wtf
|
||||
def bad(cls):
|
||||
pass
|
||||
|
||||
@hybrid_property
|
||||
def good(self):
|
||||
pass
|
||||
|
||||
@good.expression
|
||||
def good(cls):
|
||||
pass
|
||||
|
||||
@good.wtf
|
||||
def good(self):
|
||||
pass
|
||||
|
||||
@foobar.thisisstatic
|
||||
def badstatic(foo):
|
||||
pass
|
||||
|
||||
@@ -89,49 +89,3 @@ x = [ #
|
||||
f"{ {'a': 1} }"
|
||||
f"{[ { {'a': 1} } ]}"
|
||||
f"normal { {f"{ { [1, 2] } }" } } normal"
|
||||
|
||||
#: Okay
|
||||
ham[lower + offset : upper + offset]
|
||||
|
||||
#: Okay
|
||||
ham[(lower + offset) : upper + offset]
|
||||
|
||||
#: E203:1:19
|
||||
ham{lower + offset : upper + offset}
|
||||
|
||||
#: E203:1:19
|
||||
ham[lower + offset : upper + offset]
|
||||
|
||||
#: Okay
|
||||
release_lines = history_file_lines[history_file_lines.index('## Unreleased') + 1: -1]
|
||||
|
||||
#: Okay
|
||||
release_lines = history_file_lines[history_file_lines.index('## Unreleased') + 1 : -1]
|
||||
|
||||
#: Okay
|
||||
ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:]
|
||||
ham[lower:upper], ham[lower:upper:], ham[lower::step]
|
||||
ham[lower+offset : upper+offset]
|
||||
ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]
|
||||
ham[lower + offset : upper + offset]
|
||||
|
||||
#: E201:1:5
|
||||
ham[ : upper]
|
||||
|
||||
#: Okay
|
||||
ham[lower + offset :: upper + offset]
|
||||
|
||||
#: Okay
|
||||
ham[(lower + offset) :: upper + offset]
|
||||
|
||||
#: Okay
|
||||
ham[lower + offset::upper + offset]
|
||||
|
||||
#: E203:1:21
|
||||
ham[lower + offset : : upper + offset]
|
||||
|
||||
#: E203:1:20
|
||||
ham[lower + offset: :upper + offset]
|
||||
|
||||
#: E203:1:20
|
||||
ham[{lower + offset : upper + offset} : upper + offset]
|
||||
|
||||
@@ -16,48 +16,6 @@ if False == None: # E711, E712 (fix)
|
||||
if None == False: # E711, E712 (fix)
|
||||
pass
|
||||
|
||||
named_var = []
|
||||
if [] is []: # F632 (fix)
|
||||
pass
|
||||
if named_var is []: # F632 (fix)
|
||||
pass
|
||||
if [] is named_var: # F632 (fix)
|
||||
pass
|
||||
if named_var is [1]: # F632 (fix)
|
||||
pass
|
||||
if [1] is named_var: # F632 (fix)
|
||||
pass
|
||||
if named_var is [i for i in [1]]: # F632 (fix)
|
||||
pass
|
||||
|
||||
named_var = {}
|
||||
if {} is {}: # F632 (fix)
|
||||
pass
|
||||
if named_var is {}: # F632 (fix)
|
||||
pass
|
||||
if {} is named_var: # F632 (fix)
|
||||
pass
|
||||
if named_var is {1}: # F632 (fix)
|
||||
pass
|
||||
if {1} is named_var: # F632 (fix)
|
||||
pass
|
||||
if named_var is {i for i in [1]}: # F632 (fix)
|
||||
pass
|
||||
|
||||
named_var = {1: 1}
|
||||
if {1: 1} is {1: 1}: # F632 (fix)
|
||||
pass
|
||||
if named_var is {1: 1}: # F632 (fix)
|
||||
pass
|
||||
if {1: 1} is named_var: # F632 (fix)
|
||||
pass
|
||||
if named_var is {1: 1}: # F632 (fix)
|
||||
pass
|
||||
if {1: 1} is named_var: # F632 (fix)
|
||||
pass
|
||||
if named_var is {i: 1 for i in [1]}: # F632 (fix)
|
||||
pass
|
||||
|
||||
###
|
||||
# Non-errors
|
||||
###
|
||||
@@ -75,45 +33,3 @@ if False is None:
|
||||
pass
|
||||
if None is False:
|
||||
pass
|
||||
|
||||
named_var = []
|
||||
if [] == []:
|
||||
pass
|
||||
if named_var == []:
|
||||
pass
|
||||
if [] == named_var:
|
||||
pass
|
||||
if named_var == [1]:
|
||||
pass
|
||||
if [1] == named_var:
|
||||
pass
|
||||
if named_var == [i for i in [1]]:
|
||||
pass
|
||||
|
||||
named_var = {}
|
||||
if {} == {}:
|
||||
pass
|
||||
if named_var == {}:
|
||||
pass
|
||||
if {} == named_var:
|
||||
pass
|
||||
if named_var == {1}:
|
||||
pass
|
||||
if {1} == named_var:
|
||||
pass
|
||||
if named_var == {i for i in [1]}:
|
||||
pass
|
||||
|
||||
named_var = {1: 1}
|
||||
if {1: 1} == {1: 1}:
|
||||
pass
|
||||
if named_var == {1: 1}:
|
||||
pass
|
||||
if {1: 1} == named_var:
|
||||
pass
|
||||
if named_var == {1: 1}:
|
||||
pass
|
||||
if {1: 1} == named_var:
|
||||
pass
|
||||
if named_var == {i: 1 for i in [1]}:
|
||||
pass
|
||||
|
||||
@@ -663,55 +663,3 @@ class CommentAfterDocstring:
|
||||
def newline_after_closing_quote(self):
|
||||
"We enforce a newline after the closing quote for a multi-line docstring \
|
||||
but continuations shouldn't be considered multi-line"
|
||||
|
||||
|
||||
|
||||
|
||||
def retain_extra_whitespace():
|
||||
"""Summary.
|
||||
|
||||
This is overindented
|
||||
And so is this, but it we should preserve the extra space on this line relative
|
||||
to the one before
|
||||
"""
|
||||
|
||||
|
||||
def retain_extra_whitespace_multiple():
|
||||
"""Summary.
|
||||
|
||||
This is overindented
|
||||
And so is this, but it we should preserve the extra space on this line relative
|
||||
to the one before
|
||||
This is also overindented
|
||||
And so is this, but it we should preserve the extra space on this line relative
|
||||
to the one before
|
||||
"""
|
||||
|
||||
|
||||
|
||||
def retain_extra_whitespace_deeper():
|
||||
"""Summary.
|
||||
|
||||
This is overindented
|
||||
And so is this, but it we should preserve the extra space on this line relative
|
||||
to the one before
|
||||
And the relative indent here should be preserved too
|
||||
"""
|
||||
|
||||
def retain_extra_whitespace_followed_by_same_offset():
|
||||
"""Summary.
|
||||
|
||||
This is overindented
|
||||
And so is this, but it we should preserve the extra space on this line relative
|
||||
This is overindented
|
||||
This is overindented
|
||||
"""
|
||||
|
||||
|
||||
def retain_extra_whitespace_not_overindented():
|
||||
"""Summary.
|
||||
|
||||
This is not overindented
|
||||
This is overindented, but since one line is not overindented this should not raise
|
||||
And so is this, but it we should preserve the extra space on this line relative
|
||||
"""
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
"""Test for IPython-only builtins."""
|
||||
|
||||
x = 1
|
||||
display(x)
|
||||
@@ -1,74 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "af8fee97-f9aa-47c2-b34c-b109a5f083d6",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"1"
|
||||
]
|
||||
},
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "acd5eb1e-6991-42b8-806f-20d17d7e571f",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"1"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"display(1)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "f8f3f599-030e-48b3-bcd3-31d97f725c68",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.5"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
# No Errors
|
||||
def func(a):
|
||||
for b in range(1):
|
||||
...
|
||||
|
||||
|
||||
def func(a):
|
||||
try:
|
||||
...
|
||||
except ValueError:
|
||||
...
|
||||
except KeyError:
|
||||
...
|
||||
|
||||
|
||||
if True:
|
||||
def func(a):
|
||||
...
|
||||
else:
|
||||
for a in range(1):
|
||||
print(a)
|
||||
|
||||
|
||||
# Errors
|
||||
def func(a):
|
||||
for a in range(1):
|
||||
...
|
||||
|
||||
|
||||
def func(i):
|
||||
for i in range(10):
|
||||
print(i)
|
||||
|
||||
|
||||
def func(e):
|
||||
try:
|
||||
...
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
def func(f):
|
||||
with open('', ) as f:
|
||||
print(f)
|
||||
|
||||
|
||||
def func(a, b):
|
||||
with context() as (a, b, c):
|
||||
print(a, b, c)
|
||||
|
||||
|
||||
def func(a, b):
|
||||
with context() as [a, b, c]:
|
||||
print(a, b, c)
|
||||
|
||||
|
||||
def func(a):
|
||||
with open('foo.py', ) as f, open('bar.py') as a:
|
||||
...
|
||||
|
||||
|
||||
def func(a):
|
||||
def bar(b):
|
||||
for a in range(1):
|
||||
print(a)
|
||||
|
||||
|
||||
def func(a):
|
||||
def bar(b):
|
||||
for b in range(1):
|
||||
print(b)
|
||||
|
||||
|
||||
def func(a=1):
|
||||
def bar(b=2):
|
||||
for a in range(1):
|
||||
print(a)
|
||||
for b in range(1):
|
||||
print(b)
|
||||
10
crates/ruff_linter/resources/test/fixtures/pyproject.toml
vendored
Normal file
10
crates/ruff_linter/resources/test/fixtures/pyproject.toml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
[tool.ruff]
|
||||
line-length = 88
|
||||
extend-exclude = [
|
||||
"excluded_file.py",
|
||||
"migrations",
|
||||
"with_excluded_file/other_excluded_file.py",
|
||||
]
|
||||
|
||||
[tool.ruff.lint]
|
||||
per-file-ignores = { "__init__.py" = ["F401"] }
|
||||
@@ -114,8 +114,3 @@ class ServiceRefOrValue:
|
||||
class Collection(Protocol[*_B0]):
|
||||
def __iter__(self) -> Iterator[Union[*_B0]]:
|
||||
...
|
||||
|
||||
|
||||
# Regression test for: https://github.com/astral-sh/ruff/issues/8609
|
||||
def f(x: Union[int, str, bytes]) -> None:
|
||||
...
|
||||
|
||||
@@ -25,6 +25,3 @@ u = u
|
||||
|
||||
def hello():
|
||||
return"Hello"
|
||||
|
||||
f"foo"u"bar"
|
||||
f"foo" u"bar"
|
||||
|
||||
@@ -207,39 +207,3 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
# The fixed string will exceed the line length, but it's still smaller than the
|
||||
# existing line length, so it's fine.
|
||||
"<Customer: {}, {}, {}, {}, {}>".format(self.internal_ids, self.external_ids, self.properties, self.tags, self.others)
|
||||
|
||||
# When fixing, trim the trailing empty string.
|
||||
raise ValueError("Conflicting configuration dicts: {!r} {!r}"
|
||||
"".format(new_dict, d))
|
||||
|
||||
# When fixing, trim the trailing empty string.
|
||||
raise ValueError("Conflicting configuration dicts: {!r} {!r}"
|
||||
.format(new_dict, d))
|
||||
|
||||
raise ValueError(
|
||||
"Conflicting configuration dicts: {!r} {!r}"
|
||||
"".format(new_dict, d)
|
||||
)
|
||||
|
||||
raise ValueError(
|
||||
"Conflicting configuration dicts: {!r} {!r}"
|
||||
"".format(new_dict, d)
|
||||
|
||||
)
|
||||
|
||||
# The first string will be converted to an f-string and the curly braces in the second should be converted to be unescaped
|
||||
(
|
||||
"{}"
|
||||
"{{}}"
|
||||
).format(a)
|
||||
|
||||
("{}" "{{}}").format(a)
|
||||
|
||||
|
||||
# Both strings will be converted to an f-string and the curly braces in the second should left escaped
|
||||
(
|
||||
"{}"
|
||||
"{{{}}}"
|
||||
).format(a, b)
|
||||
|
||||
("{}" "{{{}}}").format(a, b)
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
x = 1
|
||||
y = 2
|
||||
|
||||
x if x > y else y # FURB136
|
||||
|
||||
x if x >= y else y # FURB136
|
||||
|
||||
x if x < y else y # FURB136
|
||||
|
||||
x if x <= y else y # FURB136
|
||||
|
||||
y if x > y else x # FURB136
|
||||
|
||||
y if x >= y else x # FURB136
|
||||
|
||||
y if x < y else x # FURB136
|
||||
|
||||
y if x <= y else x # FURB136
|
||||
|
||||
x + y if x > y else y # OK
|
||||
|
||||
x if (
|
||||
x
|
||||
> y
|
||||
) else y # FURB136
|
||||
@@ -1,7 +0,0 @@
|
||||
r = 3.1 # OK
|
||||
|
||||
A = 3.14 * r ** 2 # FURB152
|
||||
|
||||
C = 6.28 * r # FURB152
|
||||
|
||||
e = 2.71 # FURB152
|
||||
@@ -18,8 +18,8 @@ pub(crate) fn deferred_lambdas(checker: &mut Checker) {
|
||||
if checker.enabled(Rule::UnnecessaryLambda) {
|
||||
pylint::rules::unnecessary_lambda(checker, lambda);
|
||||
}
|
||||
if checker.enabled(Rule::ReimplementedContainerBuiltin) {
|
||||
flake8_pie::rules::reimplemented_container_builtin(checker, lambda);
|
||||
if checker.enabled(Rule::ReimplementedListBuiltin) {
|
||||
flake8_pie::rules::reimplemented_list_builtin(checker, lambda);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
|
||||
if !checker.any_enabled(&[
|
||||
Rule::GlobalVariableNotAssigned,
|
||||
Rule::ImportShadowedByLoopVar,
|
||||
Rule::RedefinedArgumentFromLocal,
|
||||
Rule::RedefinedWhileUnused,
|
||||
Rule::RuntimeImportInTypeCheckingBlock,
|
||||
Rule::TypingOnlyFirstPartyImport,
|
||||
@@ -90,32 +89,6 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
|
||||
if checker.enabled(Rule::RedefinedArgumentFromLocal) {
|
||||
for (name, binding_id) in scope.bindings() {
|
||||
for shadow in checker.semantic.shadowed_bindings(scope_id, binding_id) {
|
||||
let binding = &checker.semantic.bindings[shadow.binding_id()];
|
||||
if !matches!(
|
||||
binding.kind,
|
||||
BindingKind::LoopVar
|
||||
| BindingKind::BoundException
|
||||
| BindingKind::WithItemVar
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
let shadowed = &checker.semantic.bindings[shadow.shadowed_id()];
|
||||
if !shadowed.kind.is_argument() {
|
||||
continue;
|
||||
}
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
pylint::rules::RedefinedArgumentFromLocal {
|
||||
name: name.to_string(),
|
||||
},
|
||||
binding.range(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if checker.enabled(Rule::ImportShadowedByLoopVar) {
|
||||
for (name, binding_id) in scope.bindings() {
|
||||
for shadow in checker.semantic.shadowed_bindings(scope_id, binding_id) {
|
||||
|
||||
@@ -936,7 +936,13 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
flake8_trio::rules::zero_sleep_call(checker, call);
|
||||
}
|
||||
}
|
||||
Expr::Dict(dict) => {
|
||||
Expr::Dict(
|
||||
dict @ ast::ExprDict {
|
||||
keys,
|
||||
values,
|
||||
range: _,
|
||||
},
|
||||
) => {
|
||||
if checker.any_enabled(&[
|
||||
Rule::MultiValueRepeatedKeyLiteral,
|
||||
Rule::MultiValueRepeatedKeyVariable,
|
||||
@@ -944,7 +950,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
pyflakes::rules::repeated_keys(checker, dict);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessarySpread) {
|
||||
flake8_pie::rules::unnecessary_spread(checker, dict);
|
||||
flake8_pie::rules::unnecessary_spread(checker, keys, values);
|
||||
}
|
||||
}
|
||||
Expr::Set(ast::ExprSet { elts, range: _ }) => {
|
||||
@@ -1245,13 +1251,10 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
refurb::rules::single_item_membership_test(checker, expr, left, ops, comparators);
|
||||
}
|
||||
}
|
||||
Expr::NumberLiteral(number_literal @ ast::ExprNumberLiteral { .. }) => {
|
||||
Expr::NumberLiteral(_) => {
|
||||
if checker.source_type.is_stub() && checker.enabled(Rule::NumericLiteralTooLong) {
|
||||
flake8_pyi::rules::numeric_literal_too_long(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::MathConstant) {
|
||||
refurb::rules::math_constant(checker, number_literal);
|
||||
}
|
||||
}
|
||||
Expr::BytesLiteral(_) => {
|
||||
if checker.source_type.is_stub() && checker.enabled(Rule::StringOrBytesTooLong) {
|
||||
@@ -1269,20 +1272,21 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::HardcodedTempFile) {
|
||||
flake8_bandit::rules::hardcoded_tmp_directory(checker, string);
|
||||
}
|
||||
if checker.enabled(Rule::UnicodeKindPrefix) {
|
||||
pyupgrade::rules::unicode_kind_prefix(checker, string);
|
||||
}
|
||||
if checker.source_type.is_stub() {
|
||||
if checker.enabled(Rule::StringOrBytesTooLong) {
|
||||
flake8_pyi::rules::string_or_bytes_too_long(checker, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
Expr::IfExp(
|
||||
if_exp @ ast::ExprIfExp {
|
||||
test,
|
||||
body,
|
||||
orelse,
|
||||
range: _,
|
||||
},
|
||||
) => {
|
||||
Expr::IfExp(ast::ExprIfExp {
|
||||
test,
|
||||
body,
|
||||
orelse,
|
||||
range: _,
|
||||
}) => {
|
||||
if checker.enabled(Rule::IfElseBlockInsteadOfDictGet) {
|
||||
flake8_simplify::rules::if_exp_instead_of_dict_get(
|
||||
checker, expr, test, body, orelse,
|
||||
@@ -1297,9 +1301,6 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::IfExprWithTwistedArms) {
|
||||
flake8_simplify::rules::twisted_arms_in_ifexpr(checker, expr, test, body, orelse);
|
||||
}
|
||||
if checker.enabled(Rule::IfExprMinMax) {
|
||||
refurb::rules::if_expr_min_max(checker, if_exp);
|
||||
}
|
||||
}
|
||||
Expr::ListComp(
|
||||
comp @ ast::ExprListComp {
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::rules::flake8_pie;
|
||||
|
||||
/// Run lint rules over a suite of [`Stmt`] syntax nodes.
|
||||
pub(crate) fn suite(suite: &[Stmt], checker: &mut Checker) {
|
||||
if checker.enabled(Rule::UnnecessaryPlaceholder) {
|
||||
flake8_pie::rules::unnecessary_placeholder(checker, suite);
|
||||
if checker.enabled(Rule::UnnecessaryPass) {
|
||||
flake8_pie::rules::no_unnecessary_pass(checker, suite);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ use ruff_python_semantic::{
|
||||
ModuleKind, NodeId, ScopeId, ScopeKind, SemanticModel, SemanticModelFlags, Snapshot,
|
||||
StarImport, SubmoduleImport,
|
||||
};
|
||||
use ruff_python_stdlib::builtins::{IPYTHON_BUILTINS, MAGIC_GLOBALS, PYTHON_BUILTINS};
|
||||
use ruff_python_stdlib::builtins::{BUILTINS, MAGIC_GLOBALS};
|
||||
use ruff_source_file::Locator;
|
||||
|
||||
use crate::checkers::ast::deferred::Deferred;
|
||||
@@ -1592,16 +1592,9 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
|
||||
fn bind_builtins(&mut self) {
|
||||
for builtin in PYTHON_BUILTINS
|
||||
for builtin in BUILTINS
|
||||
.iter()
|
||||
.chain(MAGIC_GLOBALS.iter())
|
||||
.chain(
|
||||
self.source_type
|
||||
.is_ipynb()
|
||||
.then_some(IPYTHON_BUILTINS)
|
||||
.into_iter()
|
||||
.flatten(),
|
||||
)
|
||||
.copied()
|
||||
.chain(self.settings.builtins.iter().map(String::as_str))
|
||||
{
|
||||
@@ -1622,28 +1615,38 @@ impl<'a> Checker<'a> {
|
||||
fn handle_node_store(&mut self, id: &'a str, expr: &Expr) {
|
||||
let parent = self.semantic.current_statement();
|
||||
|
||||
let mut flags = BindingFlags::empty();
|
||||
if helpers::is_unpacking_assignment(parent, expr) {
|
||||
flags.insert(BindingFlags::UNPACKED_ASSIGNMENT);
|
||||
}
|
||||
|
||||
// Match the left-hand side of an annotated assignment, like `x` in `x: int`.
|
||||
if matches!(
|
||||
parent,
|
||||
Stmt::AnnAssign(ast::StmtAnnAssign { value: None, .. })
|
||||
) && !self.semantic.in_annotation()
|
||||
{
|
||||
self.add_binding(id, expr.range(), BindingKind::Annotation, flags);
|
||||
self.add_binding(
|
||||
id,
|
||||
expr.range(),
|
||||
BindingKind::Annotation,
|
||||
BindingFlags::empty(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if parent.is_for_stmt() {
|
||||
self.add_binding(id, expr.range(), BindingKind::LoopVar, flags);
|
||||
self.add_binding(
|
||||
id,
|
||||
expr.range(),
|
||||
BindingKind::LoopVar,
|
||||
BindingFlags::empty(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if parent.is_with_stmt() {
|
||||
self.add_binding(id, expr.range(), BindingKind::WithItemVar, flags);
|
||||
if helpers::is_unpacking_assignment(parent, expr) {
|
||||
self.add_binding(
|
||||
id,
|
||||
expr.range(),
|
||||
BindingKind::UnpackedAssignment,
|
||||
BindingFlags::empty(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1678,6 +1681,7 @@ impl<'a> Checker<'a> {
|
||||
let (all_names, all_flags) =
|
||||
extract_all_names(parent, |name| self.semantic.is_builtin(name));
|
||||
|
||||
let mut flags = BindingFlags::empty();
|
||||
if all_flags.intersects(DunderAllFlags::INVALID_OBJECT) {
|
||||
flags |= BindingFlags::INVALID_ALL_OBJECT;
|
||||
}
|
||||
@@ -1701,11 +1705,21 @@ impl<'a> Checker<'a> {
|
||||
.current_expressions()
|
||||
.any(Expr::is_named_expr_expr)
|
||||
{
|
||||
self.add_binding(id, expr.range(), BindingKind::NamedExprAssignment, flags);
|
||||
self.add_binding(
|
||||
id,
|
||||
expr.range(),
|
||||
BindingKind::NamedExprAssignment,
|
||||
BindingFlags::empty(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
self.add_binding(id, expr.range(), BindingKind::Assignment, flags);
|
||||
self.add_binding(
|
||||
id,
|
||||
expr.range(),
|
||||
BindingKind::Assignment,
|
||||
BindingFlags::empty(),
|
||||
);
|
||||
}
|
||||
|
||||
fn handle_node_delete(&mut self, expr: &'a Expr) {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use std::path::Path;
|
||||
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_source_file::Locator;
|
||||
|
||||
use crate::registry::Rule;
|
||||
use crate::rules::flake8_no_pep420::rules::implicit_namespace_package;
|
||||
@@ -12,22 +10,15 @@ use crate::settings::LinterSettings;
|
||||
pub(crate) fn check_file_path(
|
||||
path: &Path,
|
||||
package: Option<&Path>,
|
||||
locator: &Locator,
|
||||
indexer: &Indexer,
|
||||
settings: &LinterSettings,
|
||||
) -> Vec<Diagnostic> {
|
||||
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||
|
||||
// flake8-no-pep420
|
||||
if settings.rules.enabled(Rule::ImplicitNamespacePackage) {
|
||||
if let Some(diagnostic) = implicit_namespace_package(
|
||||
path,
|
||||
package,
|
||||
locator,
|
||||
indexer,
|
||||
&settings.project_root,
|
||||
&settings.src,
|
||||
) {
|
||||
if let Some(diagnostic) =
|
||||
implicit_namespace_package(path, package, &settings.project_root, &settings.src)
|
||||
{
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,10 +91,6 @@ pub(crate) fn check_tokens(
|
||||
pycodestyle::rules::tab_indentation(&mut diagnostics, tokens, locator, indexer);
|
||||
}
|
||||
|
||||
if settings.rules.enabled(Rule::UnicodeKindPrefix) {
|
||||
pyupgrade::rules::unicode_kind_prefix(&mut diagnostics, tokens);
|
||||
}
|
||||
|
||||
if settings.rules.any_enabled(&[
|
||||
Rule::InvalidCharacterBackspace,
|
||||
Rule::InvalidCharacterSub,
|
||||
@@ -119,10 +115,6 @@ pub(crate) fn check_tokens(
|
||||
flake8_quotes::rules::avoidable_escaped_quote(&mut diagnostics, tokens, locator, settings);
|
||||
}
|
||||
|
||||
if settings.rules.enabled(Rule::UnnecessaryEscapedQuote) {
|
||||
flake8_quotes::rules::unnecessary_escaped_quote(&mut diagnostics, tokens, locator);
|
||||
}
|
||||
|
||||
if settings.rules.any_enabled(&[
|
||||
Rule::BadQuotesInlineString,
|
||||
Rule::BadQuotesMultilineString,
|
||||
@@ -149,7 +141,7 @@ pub(crate) fn check_tokens(
|
||||
Rule::TrailingCommaOnBareTuple,
|
||||
Rule::ProhibitedTrailingComma,
|
||||
]) {
|
||||
flake8_commas::rules::trailing_commas(&mut diagnostics, tokens, locator, indexer);
|
||||
flake8_commas::rules::trailing_commas(&mut diagnostics, tokens, locator);
|
||||
}
|
||||
|
||||
if settings.rules.enabled(Rule::ExtraneousParentheses) {
|
||||
@@ -167,7 +159,7 @@ pub(crate) fn check_tokens(
|
||||
Rule::ShebangNotFirstLine,
|
||||
Rule::ShebangMissingPython,
|
||||
]) {
|
||||
flake8_executable::rules::from_tokens(&mut diagnostics, path, locator, indexer);
|
||||
flake8_executable::rules::from_tokens(tokens, path, locator, &mut diagnostics);
|
||||
}
|
||||
|
||||
if settings.rules.any_enabled(&[
|
||||
|
||||
@@ -252,7 +252,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Pylint, "R0915") => (RuleGroup::Stable, rules::pylint::rules::TooManyStatements),
|
||||
(Pylint, "R0916") => (RuleGroup::Preview, rules::pylint::rules::TooManyBooleanExpressions),
|
||||
(Pylint, "R1701") => (RuleGroup::Stable, rules::pylint::rules::RepeatedIsinstanceCalls),
|
||||
(Pylint, "R1704") => (RuleGroup::Preview, rules::pylint::rules::RedefinedArgumentFromLocal),
|
||||
(Pylint, "R1711") => (RuleGroup::Stable, rules::pylint::rules::UselessReturn),
|
||||
(Pylint, "R1714") => (RuleGroup::Stable, rules::pylint::rules::RepeatedEqualityComparison),
|
||||
(Pylint, "R1706") => (RuleGroup::Preview, rules::pylint::rules::AndOrTernary),
|
||||
@@ -403,7 +402,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Flake8Quotes, "001") => (RuleGroup::Stable, rules::flake8_quotes::rules::BadQuotesMultilineString),
|
||||
(Flake8Quotes, "002") => (RuleGroup::Stable, rules::flake8_quotes::rules::BadQuotesDocstring),
|
||||
(Flake8Quotes, "003") => (RuleGroup::Stable, rules::flake8_quotes::rules::AvoidableEscapedQuote),
|
||||
(Flake8Quotes, "004") => (RuleGroup::Preview, rules::flake8_quotes::rules::UnnecessaryEscapedQuote),
|
||||
|
||||
// flake8-annotations
|
||||
(Flake8Annotations, "001") => (RuleGroup::Stable, rules::flake8_annotations::rules::MissingTypeFunctionArgument),
|
||||
@@ -769,12 +767,12 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Flake8PytestStyle, "027") => (RuleGroup::Stable, rules::flake8_pytest_style::rules::PytestUnittestRaisesAssertion),
|
||||
|
||||
// flake8-pie
|
||||
(Flake8Pie, "790") => (RuleGroup::Stable, rules::flake8_pie::rules::UnnecessaryPlaceholder),
|
||||
(Flake8Pie, "790") => (RuleGroup::Stable, rules::flake8_pie::rules::UnnecessaryPass),
|
||||
(Flake8Pie, "794") => (RuleGroup::Stable, rules::flake8_pie::rules::DuplicateClassFieldDefinition),
|
||||
(Flake8Pie, "796") => (RuleGroup::Stable, rules::flake8_pie::rules::NonUniqueEnums),
|
||||
(Flake8Pie, "800") => (RuleGroup::Stable, rules::flake8_pie::rules::UnnecessarySpread),
|
||||
(Flake8Pie, "804") => (RuleGroup::Stable, rules::flake8_pie::rules::UnnecessaryDictKwargs),
|
||||
(Flake8Pie, "807") => (RuleGroup::Stable, rules::flake8_pie::rules::ReimplementedContainerBuiltin),
|
||||
(Flake8Pie, "807") => (RuleGroup::Stable, rules::flake8_pie::rules::ReimplementedListBuiltin),
|
||||
(Flake8Pie, "808") => (RuleGroup::Stable, rules::flake8_pie::rules::UnnecessaryRangeStart),
|
||||
(Flake8Pie, "810") => (RuleGroup::Stable, rules::flake8_pie::rules::MultipleStartsEndsWith),
|
||||
|
||||
@@ -947,11 +945,9 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Refurb, "131") => (RuleGroup::Nursery, rules::refurb::rules::DeleteFullSlice),
|
||||
#[allow(deprecated)]
|
||||
(Refurb, "132") => (RuleGroup::Nursery, rules::refurb::rules::CheckAndRemoveFromSet),
|
||||
(Refurb, "136") => (RuleGroup::Preview, rules::refurb::rules::IfExprMinMax),
|
||||
(Refurb, "140") => (RuleGroup::Preview, rules::refurb::rules::ReimplementedStarmap),
|
||||
(Refurb, "145") => (RuleGroup::Preview, rules::refurb::rules::SliceCopy),
|
||||
(Refurb, "148") => (RuleGroup::Preview, rules::refurb::rules::UnnecessaryEnumerate),
|
||||
(Refurb, "152") => (RuleGroup::Preview, rules::refurb::rules::MathConstant),
|
||||
(Refurb, "168") => (RuleGroup::Preview, rules::refurb::rules::IsinstanceTypeNone),
|
||||
(Refurb, "169") => (RuleGroup::Preview, rules::refurb::rules::TypeNoneComparison),
|
||||
(Refurb, "171") => (RuleGroup::Preview, rules::refurb::rules::SingleItemMembershipTest),
|
||||
|
||||
@@ -171,7 +171,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn empty_file() {
|
||||
let locator = Locator::new(r"");
|
||||
let locator = Locator::new(r#""#);
|
||||
let diagnostics = create_diagnostics([]);
|
||||
let FixResult {
|
||||
code,
|
||||
@@ -225,10 +225,10 @@ print("hello world")
|
||||
#[test]
|
||||
fn apply_one_replacement() {
|
||||
let locator = Locator::new(
|
||||
r"
|
||||
r#"
|
||||
class A(object):
|
||||
...
|
||||
"
|
||||
"#
|
||||
.trim(),
|
||||
);
|
||||
let diagnostics = create_diagnostics([Edit::replacement(
|
||||
@@ -243,10 +243,10 @@ class A(object):
|
||||
} = apply_fixes(diagnostics.iter(), &locator);
|
||||
assert_eq!(
|
||||
code,
|
||||
r"
|
||||
r#"
|
||||
class A(Bar):
|
||||
...
|
||||
"
|
||||
"#
|
||||
.trim(),
|
||||
);
|
||||
assert_eq!(fixes.values().sum::<usize>(), 1);
|
||||
@@ -262,10 +262,10 @@ class A(Bar):
|
||||
#[test]
|
||||
fn apply_one_removal() {
|
||||
let locator = Locator::new(
|
||||
r"
|
||||
r#"
|
||||
class A(object):
|
||||
...
|
||||
"
|
||||
"#
|
||||
.trim(),
|
||||
);
|
||||
let diagnostics = create_diagnostics([Edit::deletion(TextSize::new(7), TextSize::new(15))]);
|
||||
@@ -276,10 +276,10 @@ class A(object):
|
||||
} = apply_fixes(diagnostics.iter(), &locator);
|
||||
assert_eq!(
|
||||
code,
|
||||
r"
|
||||
r#"
|
||||
class A:
|
||||
...
|
||||
"
|
||||
"#
|
||||
.trim()
|
||||
);
|
||||
assert_eq!(fixes.values().sum::<usize>(), 1);
|
||||
@@ -295,10 +295,10 @@ class A:
|
||||
#[test]
|
||||
fn apply_two_removals() {
|
||||
let locator = Locator::new(
|
||||
r"
|
||||
r#"
|
||||
class A(object, object, object):
|
||||
...
|
||||
"
|
||||
"#
|
||||
.trim(),
|
||||
);
|
||||
let diagnostics = create_diagnostics([
|
||||
@@ -313,10 +313,10 @@ class A(object, object, object):
|
||||
|
||||
assert_eq!(
|
||||
code,
|
||||
r"
|
||||
r#"
|
||||
class A(object):
|
||||
...
|
||||
"
|
||||
"#
|
||||
.trim()
|
||||
);
|
||||
assert_eq!(fixes.values().sum::<usize>(), 2);
|
||||
@@ -334,10 +334,10 @@ class A(object):
|
||||
#[test]
|
||||
fn ignore_overlapping_fixes() {
|
||||
let locator = Locator::new(
|
||||
r"
|
||||
r#"
|
||||
class A(object):
|
||||
...
|
||||
"
|
||||
"#
|
||||
.trim(),
|
||||
);
|
||||
let diagnostics = create_diagnostics([
|
||||
@@ -351,10 +351,10 @@ class A(object):
|
||||
} = apply_fixes(diagnostics.iter(), &locator);
|
||||
assert_eq!(
|
||||
code,
|
||||
r"
|
||||
r#"
|
||||
class A:
|
||||
...
|
||||
"
|
||||
"#
|
||||
.trim(),
|
||||
);
|
||||
assert_eq!(fixes.values().sum::<usize>(), 1);
|
||||
|
||||
@@ -373,18 +373,18 @@ mod tests {
|
||||
Insertion::own_line("", TextSize::from(40), "\n")
|
||||
);
|
||||
|
||||
let contents = r"
|
||||
let contents = r#"
|
||||
x = 1
|
||||
"
|
||||
"#
|
||||
.trim_start();
|
||||
assert_eq!(
|
||||
insert(contents)?,
|
||||
Insertion::own_line("", TextSize::from(0), "\n")
|
||||
);
|
||||
|
||||
let contents = r"
|
||||
let contents = r#"
|
||||
#!/usr/bin/env python3
|
||||
"
|
||||
"#
|
||||
.trim_start();
|
||||
assert_eq!(
|
||||
insert(contents)?,
|
||||
@@ -457,10 +457,10 @@ x = 1
|
||||
Insertion::inline("", TextSize::from(9), "; ")
|
||||
);
|
||||
|
||||
let contents = r"
|
||||
let contents = r#"
|
||||
if True:
|
||||
pass
|
||||
"
|
||||
"#
|
||||
.trim_start();
|
||||
assert_eq!(
|
||||
insert(contents, TextSize::from(0)),
|
||||
|
||||
@@ -7,7 +7,7 @@ use std::error::Error;
|
||||
|
||||
use anyhow::Result;
|
||||
use libcst_native::{ImportAlias, Name, NameOrAttribute};
|
||||
use ruff_python_ast::{self as ast, PySourceType, Stmt};
|
||||
use ruff_python_ast::{self as ast, PySourceType, Stmt, Suite};
|
||||
use ruff_text_size::{Ranged, TextSize};
|
||||
|
||||
use ruff_diagnostics::Edit;
|
||||
@@ -26,7 +26,7 @@ mod insertion;
|
||||
|
||||
pub(crate) struct Importer<'a> {
|
||||
/// The Python AST to which we are adding imports.
|
||||
python_ast: &'a [Stmt],
|
||||
python_ast: &'a Suite,
|
||||
/// The [`Locator`] for the Python AST.
|
||||
locator: &'a Locator<'a>,
|
||||
/// The [`Stylist`] for the Python AST.
|
||||
@@ -39,7 +39,7 @@ pub(crate) struct Importer<'a> {
|
||||
|
||||
impl<'a> Importer<'a> {
|
||||
pub(crate) fn new(
|
||||
python_ast: &'a [Stmt],
|
||||
python_ast: &'a Suite,
|
||||
locator: &'a Locator<'a>,
|
||||
stylist: &'a Stylist<'a>,
|
||||
) -> Self {
|
||||
@@ -132,7 +132,11 @@ impl<'a> Importer<'a> {
|
||||
)?;
|
||||
|
||||
// Import the `TYPE_CHECKING` symbol from the typing module.
|
||||
let (type_checking_edit, type_checking) = self.get_or_import_type_checking(at, semantic)?;
|
||||
let (type_checking_edit, type_checking) = self.get_or_import_symbol(
|
||||
&ImportRequest::import_from("typing", "TYPE_CHECKING"),
|
||||
at,
|
||||
semantic,
|
||||
)?;
|
||||
|
||||
// Add the import to a `TYPE_CHECKING` block.
|
||||
let add_import_edit = if let Some(block) = self.preceding_type_checking_block(at) {
|
||||
@@ -157,30 +161,6 @@ impl<'a> Importer<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Generate an [`Edit`] to reference `typing.TYPE_CHECKING`. Returns the [`Edit`] necessary to
|
||||
/// make the symbol available in the current scope along with the bound name of the symbol.
|
||||
fn get_or_import_type_checking(
|
||||
&self,
|
||||
at: TextSize,
|
||||
semantic: &SemanticModel,
|
||||
) -> Result<(Edit, String), ResolutionError> {
|
||||
for module in semantic.typing_modules() {
|
||||
if let Some((edit, name)) = self.get_symbol(
|
||||
&ImportRequest::import_from(module, "TYPE_CHECKING"),
|
||||
at,
|
||||
semantic,
|
||||
)? {
|
||||
return Ok((edit, name));
|
||||
}
|
||||
}
|
||||
|
||||
self.import_symbol(
|
||||
&ImportRequest::import_from("typing", "TYPE_CHECKING"),
|
||||
at,
|
||||
semantic,
|
||||
)
|
||||
}
|
||||
|
||||
/// Generate an [`Edit`] to reference the given symbol. Returns the [`Edit`] necessary to make
|
||||
/// the symbol available in the current scope along with the bound name of the symbol.
|
||||
///
|
||||
|
||||
@@ -117,7 +117,7 @@ pub fn check_path(
|
||||
.iter_enabled()
|
||||
.any(|rule_code| rule_code.lint_source().is_filesystem())
|
||||
{
|
||||
diagnostics.extend(check_file_path(path, package, locator, indexer, settings));
|
||||
diagnostics.extend(check_file_path(path, package, settings));
|
||||
}
|
||||
|
||||
// Run the logical line-based rules.
|
||||
@@ -639,7 +639,7 @@ mod tests {
|
||||
|
||||
use crate::registry::Rule;
|
||||
use crate::source_kind::SourceKind;
|
||||
use crate::test::{assert_notebook_path, test_contents, TestedNotebook};
|
||||
use crate::test::{test_contents, test_notebook_path, TestedNotebook};
|
||||
use crate::{assert_messages, settings};
|
||||
|
||||
/// Construct a path to a Jupyter notebook in the `resources/test/fixtures/jupyter` directory.
|
||||
@@ -655,7 +655,7 @@ mod tests {
|
||||
messages,
|
||||
source_notebook,
|
||||
..
|
||||
} = assert_notebook_path(
|
||||
} = test_notebook_path(
|
||||
&actual,
|
||||
expected,
|
||||
&settings::LinterSettings::for_rule(Rule::UnsortedImports),
|
||||
@@ -672,7 +672,7 @@ mod tests {
|
||||
messages,
|
||||
source_notebook,
|
||||
..
|
||||
} = assert_notebook_path(
|
||||
} = test_notebook_path(
|
||||
&actual,
|
||||
expected,
|
||||
&settings::LinterSettings::for_rule(Rule::UnusedImport),
|
||||
@@ -689,7 +689,7 @@ mod tests {
|
||||
messages,
|
||||
source_notebook,
|
||||
..
|
||||
} = assert_notebook_path(
|
||||
} = test_notebook_path(
|
||||
&actual,
|
||||
expected,
|
||||
&settings::LinterSettings::for_rule(Rule::UnusedVariable),
|
||||
@@ -706,7 +706,7 @@ mod tests {
|
||||
let TestedNotebook {
|
||||
linted_notebook: fixed_notebook,
|
||||
..
|
||||
} = assert_notebook_path(
|
||||
} = test_notebook_path(
|
||||
actual_path,
|
||||
&expected_path,
|
||||
&settings::LinterSettings::for_rule(Rule::UnusedImport),
|
||||
|
||||
@@ -199,7 +199,7 @@ def fibonacci(n):
|
||||
TextSize::from(99),
|
||||
)));
|
||||
|
||||
let file_2 = r"if a == 1: pass";
|
||||
let file_2 = r#"if a == 1: pass"#;
|
||||
|
||||
let undefined_name = Diagnostic::new(
|
||||
DiagnosticKind {
|
||||
|
||||
@@ -297,7 +297,6 @@ impl Rule {
|
||||
| Rule::TabIndentation
|
||||
| Rule::TrailingCommaOnBareTuple
|
||||
| Rule::TypeCommentInStub
|
||||
| Rule::UnicodeKindPrefix
|
||||
| Rule::UselessSemicolon
|
||||
| Rule::UTF8EncodingDeclaration => LintSource::Tokens,
|
||||
Rule::IOError => LintSource::Io,
|
||||
|
||||
@@ -245,10 +245,10 @@ impl Renamer {
|
||||
| BindingKind::Argument
|
||||
| BindingKind::TypeParam
|
||||
| BindingKind::NamedExprAssignment
|
||||
| BindingKind::UnpackedAssignment
|
||||
| BindingKind::Assignment
|
||||
| BindingKind::BoundException
|
||||
| BindingKind::LoopVar
|
||||
| BindingKind::WithItemVar
|
||||
| BindingKind::Global
|
||||
| BindingKind::Nonlocal(_)
|
||||
| BindingKind::ClassDefinition(_)
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
use itertools::Itertools;
|
||||
|
||||
use ruff_python_ast::helpers::{pep_604_union, ReturnStatementVisitor};
|
||||
use ruff_python_ast::visitor::Visitor;
|
||||
use ruff_python_ast::{self as ast, Expr, ExprContext};
|
||||
use ruff_python_semantic::analyze::type_inference::{NumberLike, PythonType, ResolvedPythonType};
|
||||
use ruff_python_semantic::analyze::visibility;
|
||||
use ruff_python_semantic::{Definition, SemanticModel};
|
||||
use ruff_text_size::TextRange;
|
||||
|
||||
use crate::settings::types::PythonVersion;
|
||||
|
||||
/// Return the name of the function, if it's overloaded.
|
||||
pub(crate) fn overloaded_name(definition: &Definition, semantic: &SemanticModel) -> Option<String> {
|
||||
@@ -36,81 +27,3 @@ pub(crate) fn is_overload_impl(
|
||||
function.name.as_str() == overloaded_name
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a function, guess its return type.
|
||||
pub(crate) fn auto_return_type(
|
||||
function: &ast::StmtFunctionDef,
|
||||
target_version: PythonVersion,
|
||||
) -> Option<Expr> {
|
||||
// Collect all the `return` statements.
|
||||
let returns = {
|
||||
let mut visitor = ReturnStatementVisitor::default();
|
||||
visitor.visit_body(&function.body);
|
||||
if visitor.is_generator {
|
||||
return None;
|
||||
}
|
||||
visitor.returns
|
||||
};
|
||||
|
||||
// Determine the return type of the first `return` statement.
|
||||
let (return_statement, returns) = returns.split_first()?;
|
||||
let mut return_type = return_statement.value.as_deref().map_or(
|
||||
ResolvedPythonType::Atom(PythonType::None),
|
||||
ResolvedPythonType::from,
|
||||
);
|
||||
|
||||
// Merge the return types of the remaining `return` statements.
|
||||
for return_statement in returns {
|
||||
return_type = return_type.union(return_statement.value.as_deref().map_or(
|
||||
ResolvedPythonType::Atom(PythonType::None),
|
||||
ResolvedPythonType::from,
|
||||
));
|
||||
}
|
||||
|
||||
match return_type {
|
||||
ResolvedPythonType::Atom(python_type) => type_expr(python_type),
|
||||
ResolvedPythonType::Union(python_types) if target_version >= PythonVersion::Py310 => {
|
||||
// Aggregate all the individual types (e.g., `int`, `float`).
|
||||
let names = python_types
|
||||
.iter()
|
||||
.sorted_unstable()
|
||||
.filter_map(|python_type| type_expr(*python_type))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Wrap in a bitwise union (e.g., `int | float`).
|
||||
Some(pep_604_union(&names))
|
||||
}
|
||||
ResolvedPythonType::Union(_) => None,
|
||||
ResolvedPythonType::Unknown => None,
|
||||
ResolvedPythonType::TypeError => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a [`PythonType`], return an [`Expr`] that resolves to that type.
|
||||
fn type_expr(python_type: PythonType) -> Option<Expr> {
|
||||
fn name(name: &str) -> Expr {
|
||||
Expr::Name(ast::ExprName {
|
||||
id: name.into(),
|
||||
range: TextRange::default(),
|
||||
ctx: ExprContext::Load,
|
||||
})
|
||||
}
|
||||
|
||||
match python_type {
|
||||
PythonType::String => Some(name("str")),
|
||||
PythonType::Bytes => Some(name("bytes")),
|
||||
PythonType::Number(number) => match number {
|
||||
NumberLike::Integer => Some(name("int")),
|
||||
NumberLike::Float => Some(name("float")),
|
||||
NumberLike::Complex => Some(name("complex")),
|
||||
NumberLike::Bool => Some(name("bool")),
|
||||
},
|
||||
PythonType::None => Some(name("None")),
|
||||
PythonType::Ellipsis => None,
|
||||
PythonType::Dict => None,
|
||||
PythonType::List => None,
|
||||
PythonType::Set => None,
|
||||
PythonType::Tuple => None,
|
||||
PythonType::Generator => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,24 +110,6 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auto_return_type() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
Path::new("flake8_annotations/auto_return_type.py"),
|
||||
&LinterSettings {
|
||||
..LinterSettings::for_rules(vec![
|
||||
Rule::MissingReturnTypeUndocumentedPublicFunction,
|
||||
Rule::MissingReturnTypePrivateFunction,
|
||||
Rule::MissingReturnTypeSpecialMethod,
|
||||
Rule::MissingReturnTypeStaticMethod,
|
||||
Rule::MissingReturnTypeClassMethod,
|
||||
])
|
||||
},
|
||||
)?;
|
||||
assert_messages!(diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn suppress_none_returning() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::helpers::ReturnStatementVisitor;
|
||||
use ruff_python_ast::identifier::Identifier;
|
||||
use ruff_python_ast::visitor::Visitor;
|
||||
use ruff_python_ast::statement_visitor::StatementVisitor;
|
||||
use ruff_python_ast::{self as ast, Expr, ParameterWithDefault, Stmt};
|
||||
use ruff_python_parser::typing::parse_type_annotation;
|
||||
use ruff_python_semantic::analyze::visibility;
|
||||
@@ -12,7 +12,6 @@ use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::Rule;
|
||||
use crate::rules::flake8_annotations::helpers::auto_return_type;
|
||||
use crate::rules::ruff::typing::type_hint_resolves_to_any;
|
||||
|
||||
/// ## What it does
|
||||
@@ -42,7 +41,7 @@ pub struct MissingTypeFunctionArgument {
|
||||
impl Violation for MissingTypeFunctionArgument {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let Self { name } = self;
|
||||
let MissingTypeFunctionArgument { name } = self;
|
||||
format!("Missing type annotation for function argument `{name}`")
|
||||
}
|
||||
}
|
||||
@@ -74,7 +73,7 @@ pub struct MissingTypeArgs {
|
||||
impl Violation for MissingTypeArgs {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let Self { name } = self;
|
||||
let MissingTypeArgs { name } = self;
|
||||
format!("Missing type annotation for `*{name}`")
|
||||
}
|
||||
}
|
||||
@@ -106,7 +105,7 @@ pub struct MissingTypeKwargs {
|
||||
impl Violation for MissingTypeKwargs {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let Self { name } = self;
|
||||
let MissingTypeKwargs { name } = self;
|
||||
format!("Missing type annotation for `**{name}`")
|
||||
}
|
||||
}
|
||||
@@ -143,7 +142,7 @@ pub struct MissingTypeSelf {
|
||||
impl Violation for MissingTypeSelf {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let Self { name } = self;
|
||||
let MissingTypeSelf { name } = self;
|
||||
format!("Missing type annotation for `{name}` in method")
|
||||
}
|
||||
}
|
||||
@@ -182,7 +181,7 @@ pub struct MissingTypeCls {
|
||||
impl Violation for MissingTypeCls {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let Self { name } = self;
|
||||
let MissingTypeCls { name } = self;
|
||||
format!("Missing type annotation for `{name}` in classmethod")
|
||||
}
|
||||
}
|
||||
@@ -209,26 +208,14 @@ impl Violation for MissingTypeCls {
|
||||
#[violation]
|
||||
pub struct MissingReturnTypeUndocumentedPublicFunction {
|
||||
name: String,
|
||||
annotation: Option<String>,
|
||||
}
|
||||
|
||||
impl Violation for MissingReturnTypeUndocumentedPublicFunction {
|
||||
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let Self { name, .. } = self;
|
||||
let MissingReturnTypeUndocumentedPublicFunction { name } = self;
|
||||
format!("Missing return type annotation for public function `{name}`")
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> Option<String> {
|
||||
let Self { annotation, .. } = self;
|
||||
if let Some(annotation) = annotation {
|
||||
Some(format!("Add return type annotation: `{annotation}`"))
|
||||
} else {
|
||||
Some(format!("Add return type annotation"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
@@ -253,26 +240,14 @@ impl Violation for MissingReturnTypeUndocumentedPublicFunction {
|
||||
#[violation]
|
||||
pub struct MissingReturnTypePrivateFunction {
|
||||
name: String,
|
||||
annotation: Option<String>,
|
||||
}
|
||||
|
||||
impl Violation for MissingReturnTypePrivateFunction {
|
||||
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let Self { name, .. } = self;
|
||||
let MissingReturnTypePrivateFunction { name } = self;
|
||||
format!("Missing return type annotation for private function `{name}`")
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> Option<String> {
|
||||
let Self { annotation, .. } = self;
|
||||
if let Some(annotation) = annotation {
|
||||
Some(format!("Add return type annotation: `{annotation}`"))
|
||||
} else {
|
||||
Some(format!("Add return type annotation"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
@@ -310,25 +285,17 @@ impl Violation for MissingReturnTypePrivateFunction {
|
||||
#[violation]
|
||||
pub struct MissingReturnTypeSpecialMethod {
|
||||
name: String,
|
||||
annotation: Option<String>,
|
||||
}
|
||||
|
||||
impl Violation for MissingReturnTypeSpecialMethod {
|
||||
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
|
||||
|
||||
impl AlwaysFixableViolation for MissingReturnTypeSpecialMethod {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let Self { name, .. } = self;
|
||||
let MissingReturnTypeSpecialMethod { name } = self;
|
||||
format!("Missing return type annotation for special method `{name}`")
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> Option<String> {
|
||||
let Self { annotation, .. } = self;
|
||||
if let Some(annotation) = annotation {
|
||||
Some(format!("Add return type annotation: `{annotation}`"))
|
||||
} else {
|
||||
Some(format!("Add return type annotation"))
|
||||
}
|
||||
fn fix_title(&self) -> String {
|
||||
"Add `None` return type".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,26 +325,14 @@ impl Violation for MissingReturnTypeSpecialMethod {
|
||||
#[violation]
|
||||
pub struct MissingReturnTypeStaticMethod {
|
||||
name: String,
|
||||
annotation: Option<String>,
|
||||
}
|
||||
|
||||
impl Violation for MissingReturnTypeStaticMethod {
|
||||
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let Self { name, .. } = self;
|
||||
let MissingReturnTypeStaticMethod { name } = self;
|
||||
format!("Missing return type annotation for staticmethod `{name}`")
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> Option<String> {
|
||||
let Self { annotation, .. } = self;
|
||||
if let Some(annotation) = annotation {
|
||||
Some(format!("Add return type annotation: `{annotation}`"))
|
||||
} else {
|
||||
Some(format!("Add return type annotation"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
@@ -406,26 +361,14 @@ impl Violation for MissingReturnTypeStaticMethod {
|
||||
#[violation]
|
||||
pub struct MissingReturnTypeClassMethod {
|
||||
name: String,
|
||||
annotation: Option<String>,
|
||||
}
|
||||
|
||||
impl Violation for MissingReturnTypeClassMethod {
|
||||
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let Self { name, .. } = self;
|
||||
let MissingReturnTypeClassMethod { name } = self;
|
||||
format!("Missing return type annotation for classmethod `{name}`")
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> Option<String> {
|
||||
let Self { annotation, .. } = self;
|
||||
if let Some(annotation) = annotation {
|
||||
Some(format!("Add return type annotation: `{annotation}`"))
|
||||
} else {
|
||||
Some(format!("Add return type annotation"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
@@ -478,7 +421,7 @@ pub struct AnyType {
|
||||
impl Violation for AnyType {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let Self { name } = self;
|
||||
let AnyType { name } = self;
|
||||
format!("Dynamically typed expressions (typing.Any) are disallowed in `{name}`")
|
||||
}
|
||||
}
|
||||
@@ -730,41 +673,21 @@ pub(crate) fn definition(
|
||||
) {
|
||||
if is_method && visibility::is_classmethod(decorator_list, checker.semantic()) {
|
||||
if checker.enabled(Rule::MissingReturnTypeClassMethod) {
|
||||
let return_type = auto_return_type(function, checker.settings.target_version)
|
||||
.map(|return_type| checker.generator().expr(&return_type));
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
diagnostics.push(Diagnostic::new(
|
||||
MissingReturnTypeClassMethod {
|
||||
name: name.to_string(),
|
||||
annotation: return_type.clone(),
|
||||
},
|
||||
function.identifier(),
|
||||
);
|
||||
if let Some(return_type) = return_type {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
|
||||
format!(" -> {return_type}"),
|
||||
function.parameters.range().end(),
|
||||
)));
|
||||
}
|
||||
diagnostics.push(diagnostic);
|
||||
));
|
||||
}
|
||||
} else if is_method && visibility::is_staticmethod(decorator_list, checker.semantic()) {
|
||||
if checker.enabled(Rule::MissingReturnTypeStaticMethod) {
|
||||
let return_type = auto_return_type(function, checker.settings.target_version)
|
||||
.map(|return_type| checker.generator().expr(&return_type));
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
diagnostics.push(Diagnostic::new(
|
||||
MissingReturnTypeStaticMethod {
|
||||
name: name.to_string(),
|
||||
annotation: return_type.clone(),
|
||||
},
|
||||
function.identifier(),
|
||||
);
|
||||
if let Some(return_type) = return_type {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
|
||||
format!(" -> {return_type}"),
|
||||
function.parameters.range().end(),
|
||||
)));
|
||||
}
|
||||
diagnostics.push(diagnostic);
|
||||
));
|
||||
}
|
||||
} else if is_method && visibility::is_init(name) {
|
||||
// Allow omission of return annotation in `__init__` functions, as long as at
|
||||
@@ -774,7 +697,6 @@ pub(crate) fn definition(
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
MissingReturnTypeSpecialMethod {
|
||||
name: name.to_string(),
|
||||
annotation: Some("None".to_string()),
|
||||
},
|
||||
function.identifier(),
|
||||
);
|
||||
@@ -787,15 +709,13 @@ pub(crate) fn definition(
|
||||
}
|
||||
} else if is_method && visibility::is_magic(name) {
|
||||
if checker.enabled(Rule::MissingReturnTypeSpecialMethod) {
|
||||
let return_type = simple_magic_return_type(name);
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
MissingReturnTypeSpecialMethod {
|
||||
name: name.to_string(),
|
||||
annotation: return_type.map(ToString::to_string),
|
||||
},
|
||||
function.identifier(),
|
||||
);
|
||||
if let Some(return_type) = return_type {
|
||||
if let Some(return_type) = simple_magic_return_type(name) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
|
||||
format!(" -> {return_type}"),
|
||||
function.parameters.range().end(),
|
||||
@@ -807,44 +727,22 @@ pub(crate) fn definition(
|
||||
match visibility {
|
||||
visibility::Visibility::Public => {
|
||||
if checker.enabled(Rule::MissingReturnTypeUndocumentedPublicFunction) {
|
||||
let return_type =
|
||||
auto_return_type(function, checker.settings.target_version)
|
||||
.map(|return_type| checker.generator().expr(&return_type));
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
diagnostics.push(Diagnostic::new(
|
||||
MissingReturnTypeUndocumentedPublicFunction {
|
||||
name: name.to_string(),
|
||||
annotation: return_type.clone(),
|
||||
},
|
||||
function.identifier(),
|
||||
);
|
||||
if let Some(return_type) = return_type {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
|
||||
format!(" -> {return_type}"),
|
||||
function.parameters.range().end(),
|
||||
)));
|
||||
}
|
||||
diagnostics.push(diagnostic);
|
||||
));
|
||||
}
|
||||
}
|
||||
visibility::Visibility::Private => {
|
||||
if checker.enabled(Rule::MissingReturnTypePrivateFunction) {
|
||||
let return_type =
|
||||
auto_return_type(function, checker.settings.target_version)
|
||||
.map(|return_type| checker.generator().expr(&return_type));
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
diagnostics.push(Diagnostic::new(
|
||||
MissingReturnTypePrivateFunction {
|
||||
name: name.to_string(),
|
||||
annotation: return_type.clone(),
|
||||
},
|
||||
function.identifier(),
|
||||
);
|
||||
if let Some(return_type) = return_type {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
|
||||
format!(" -> {return_type}"),
|
||||
function.parameters.range().end(),
|
||||
)));
|
||||
}
|
||||
diagnostics.push(diagnostic);
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,5 @@ allow_overload.py:29:9: ANN201 Missing return type annotation for public functio
|
||||
| ^^^ ANN201
|
||||
30 | return i
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_annotations/mod.rs
|
||||
---
|
||||
auto_return_type.py:1:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
1 | def func():
|
||||
| ^^^^ ANN201
|
||||
2 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |-def func():
|
||||
1 |+def func() -> int:
|
||||
2 2 | return 1
|
||||
3 3 |
|
||||
4 4 |
|
||||
|
||||
auto_return_type.py:5:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
5 | def func():
|
||||
| ^^^^ ANN201
|
||||
6 | return 1.5
|
||||
|
|
||||
= help: Add return type annotation: `float`
|
||||
|
||||
ℹ Unsafe fix
|
||||
2 2 | return 1
|
||||
3 3 |
|
||||
4 4 |
|
||||
5 |-def func():
|
||||
5 |+def func() -> float:
|
||||
6 6 | return 1.5
|
||||
7 7 |
|
||||
8 8 |
|
||||
|
||||
auto_return_type.py:9:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
9 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
10 | if x > 0:
|
||||
11 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `float`
|
||||
|
||||
ℹ Unsafe fix
|
||||
6 6 | return 1.5
|
||||
7 7 |
|
||||
8 8 |
|
||||
9 |-def func(x: int):
|
||||
9 |+def func(x: int) -> float:
|
||||
10 10 | if x > 0:
|
||||
11 11 | return 1
|
||||
12 12 | else:
|
||||
|
||||
auto_return_type.py:16:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
16 | def func():
|
||||
| ^^^^ ANN201
|
||||
17 | return True
|
||||
|
|
||||
= help: Add return type annotation: `bool`
|
||||
|
||||
ℹ Unsafe fix
|
||||
13 13 | return 1.5
|
||||
14 14 |
|
||||
15 15 |
|
||||
16 |-def func():
|
||||
16 |+def func() -> bool:
|
||||
17 17 | return True
|
||||
18 18 |
|
||||
19 19 |
|
||||
|
||||
auto_return_type.py:20:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
20 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
21 | if x > 0:
|
||||
22 | return None
|
||||
|
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
17 17 | return True
|
||||
18 18 |
|
||||
19 19 |
|
||||
20 |-def func(x: int):
|
||||
20 |+def func(x: int) -> None:
|
||||
21 21 | if x > 0:
|
||||
22 22 | return None
|
||||
23 23 | else:
|
||||
|
||||
auto_return_type.py:27:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
27 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
28 | return 1 or 2.5 if x > 0 else 1.5 or "str"
|
||||
|
|
||||
= help: Add return type annotation: `str | float`
|
||||
|
||||
ℹ Unsafe fix
|
||||
24 24 | return
|
||||
25 25 |
|
||||
26 26 |
|
||||
27 |-def func(x: int):
|
||||
27 |+def func(x: int) -> str | float:
|
||||
28 28 | return 1 or 2.5 if x > 0 else 1.5 or "str"
|
||||
29 29 |
|
||||
30 30 |
|
||||
|
||||
auto_return_type.py:31:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
31 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
32 | return 1 + 2.5 if x > 0 else 1.5 or "str"
|
||||
|
|
||||
= help: Add return type annotation: `str | float`
|
||||
|
||||
ℹ Unsafe fix
|
||||
28 28 | return 1 or 2.5 if x > 0 else 1.5 or "str"
|
||||
29 29 |
|
||||
30 30 |
|
||||
31 |-def func(x: int):
|
||||
31 |+def func(x: int) -> str | float:
|
||||
32 32 | return 1 + 2.5 if x > 0 else 1.5 or "str"
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ annotation_presence.py:5:5: ANN201 Missing return type annotation for public fun
|
||||
| ^^^ ANN201
|
||||
6 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
annotation_presence.py:5:9: ANN001 Missing type annotation for function argument `a`
|
||||
|
|
||||
@@ -33,7 +32,6 @@ annotation_presence.py:10:5: ANN201 Missing return type annotation for public fu
|
||||
| ^^^ ANN201
|
||||
11 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
annotation_presence.py:10:17: ANN001 Missing type annotation for function argument `b`
|
||||
|
|
||||
@@ -58,7 +56,6 @@ annotation_presence.py:20:5: ANN201 Missing return type annotation for public fu
|
||||
| ^^^ ANN201
|
||||
21 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
annotation_presence.py:25:5: ANN201 Missing return type annotation for public function `foo`
|
||||
|
|
||||
@@ -67,7 +64,6 @@ annotation_presence.py:25:5: ANN201 Missing return type annotation for public fu
|
||||
| ^^^ ANN201
|
||||
26 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
annotation_presence.py:45:12: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `a`
|
||||
|
|
||||
@@ -254,7 +250,7 @@ annotation_presence.py:159:9: ANN204 [*] Missing return type annotation for spec
|
||||
| ^^^^^^^^ ANN204
|
||||
160 | ...
|
||||
|
|
||||
= help: Add return type annotation: `None`
|
||||
= help: Add `None` return type
|
||||
|
||||
ℹ Unsafe fix
|
||||
156 156 |
|
||||
@@ -274,7 +270,7 @@ annotation_presence.py:165:9: ANN204 [*] Missing return type annotation for spec
|
||||
| ^^^^^^^^ ANN204
|
||||
166 | print(f"{self.attr=}")
|
||||
|
|
||||
= help: Add return type annotation: `None`
|
||||
= help: Add `None` return type
|
||||
|
||||
ℹ Unsafe fix
|
||||
162 162 |
|
||||
|
||||
@@ -7,7 +7,6 @@ ignore_fully_untyped.py:24:5: ANN201 Missing return type annotation for public f
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ ANN201
|
||||
25 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
ignore_fully_untyped.py:24:37: ANN001 Missing type annotation for function argument `b`
|
||||
|
|
||||
@@ -29,7 +28,6 @@ ignore_fully_untyped.py:32:5: ANN201 Missing return type annotation for public f
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ ANN201
|
||||
33 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
ignore_fully_untyped.py:43:9: ANN201 Missing return type annotation for public function `error_typed_self`
|
||||
|
|
||||
@@ -39,6 +37,5 @@ ignore_fully_untyped.py:43:9: ANN201 Missing return type annotation for public f
|
||||
| ^^^^^^^^^^^^^^^^ ANN201
|
||||
44 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ mypy_init_return.py:5:9: ANN204 [*] Missing return type annotation for special m
|
||||
| ^^^^^^^^ ANN204
|
||||
6 | ...
|
||||
|
|
||||
= help: Add return type annotation: `None`
|
||||
= help: Add `None` return type
|
||||
|
||||
ℹ Unsafe fix
|
||||
2 2 |
|
||||
@@ -29,7 +29,7 @@ mypy_init_return.py:11:9: ANN204 [*] Missing return type annotation for special
|
||||
| ^^^^^^^^ ANN204
|
||||
12 | ...
|
||||
|
|
||||
= help: Add return type annotation: `None`
|
||||
= help: Add `None` return type
|
||||
|
||||
ℹ Unsafe fix
|
||||
8 8 |
|
||||
@@ -48,7 +48,6 @@ mypy_init_return.py:40:5: ANN202 Missing return type annotation for private func
|
||||
| ^^^^^^^^ ANN202
|
||||
41 | ...
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
mypy_init_return.py:47:9: ANN204 [*] Missing return type annotation for special method `__init__`
|
||||
|
|
||||
@@ -58,7 +57,7 @@ mypy_init_return.py:47:9: ANN204 [*] Missing return type annotation for special
|
||||
| ^^^^^^^^ ANN204
|
||||
48 | ...
|
||||
|
|
||||
= help: Add return type annotation: `None`
|
||||
= help: Add `None` return type
|
||||
|
||||
ℹ Unsafe fix
|
||||
44 44 | # Error – used to be ok for a moment since the mere presence
|
||||
|
||||
@@ -8,7 +8,7 @@ simple_magic_methods.py:2:9: ANN204 [*] Missing return type annotation for speci
|
||||
| ^^^^^^^ ANN204
|
||||
3 | ...
|
||||
|
|
||||
= help: Add return type annotation: `str`
|
||||
= help: Add `None` return type
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 1 | class Foo:
|
||||
@@ -26,7 +26,7 @@ simple_magic_methods.py:5:9: ANN204 [*] Missing return type annotation for speci
|
||||
| ^^^^^^^^ ANN204
|
||||
6 | ...
|
||||
|
|
||||
= help: Add return type annotation: `str`
|
||||
= help: Add `None` return type
|
||||
|
||||
ℹ Unsafe fix
|
||||
2 2 | def __str__(self):
|
||||
@@ -46,7 +46,7 @@ simple_magic_methods.py:8:9: ANN204 [*] Missing return type annotation for speci
|
||||
| ^^^^^^^ ANN204
|
||||
9 | ...
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
= help: Add `None` return type
|
||||
|
||||
ℹ Unsafe fix
|
||||
5 5 | def __repr__(self):
|
||||
@@ -66,7 +66,7 @@ simple_magic_methods.py:11:9: ANN204 [*] Missing return type annotation for spec
|
||||
| ^^^^^^^^^^^^^^^ ANN204
|
||||
12 | ...
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
= help: Add `None` return type
|
||||
|
||||
ℹ Unsafe fix
|
||||
8 8 | def __len__(self):
|
||||
@@ -86,7 +86,7 @@ simple_magic_methods.py:14:9: ANN204 [*] Missing return type annotation for spec
|
||||
| ^^^^^^^^ ANN204
|
||||
15 | ...
|
||||
|
|
||||
= help: Add return type annotation: `None`
|
||||
= help: Add `None` return type
|
||||
|
||||
ℹ Unsafe fix
|
||||
11 11 | def __length_hint__(self):
|
||||
@@ -106,7 +106,7 @@ simple_magic_methods.py:17:9: ANN204 [*] Missing return type annotation for spec
|
||||
| ^^^^^^^ ANN204
|
||||
18 | ...
|
||||
|
|
||||
= help: Add return type annotation: `None`
|
||||
= help: Add `None` return type
|
||||
|
||||
ℹ Unsafe fix
|
||||
14 14 | def __init__(self):
|
||||
@@ -126,7 +126,7 @@ simple_magic_methods.py:20:9: ANN204 [*] Missing return type annotation for spec
|
||||
| ^^^^^^^^ ANN204
|
||||
21 | ...
|
||||
|
|
||||
= help: Add return type annotation: `bool`
|
||||
= help: Add `None` return type
|
||||
|
||||
ℹ Unsafe fix
|
||||
17 17 | def __del__(self):
|
||||
@@ -146,7 +146,7 @@ simple_magic_methods.py:23:9: ANN204 [*] Missing return type annotation for spec
|
||||
| ^^^^^^^^^ ANN204
|
||||
24 | ...
|
||||
|
|
||||
= help: Add return type annotation: `bytes`
|
||||
= help: Add `None` return type
|
||||
|
||||
ℹ Unsafe fix
|
||||
20 20 | def __bool__(self):
|
||||
@@ -166,7 +166,7 @@ simple_magic_methods.py:26:9: ANN204 [*] Missing return type annotation for spec
|
||||
| ^^^^^^^^^^ ANN204
|
||||
27 | ...
|
||||
|
|
||||
= help: Add return type annotation: `str`
|
||||
= help: Add `None` return type
|
||||
|
||||
ℹ Unsafe fix
|
||||
23 23 | def __bytes__(self):
|
||||
@@ -186,7 +186,7 @@ simple_magic_methods.py:29:9: ANN204 [*] Missing return type annotation for spec
|
||||
| ^^^^^^^^^^^^ ANN204
|
||||
30 | ...
|
||||
|
|
||||
= help: Add return type annotation: `bool`
|
||||
= help: Add `None` return type
|
||||
|
||||
ℹ Unsafe fix
|
||||
26 26 | def __format__(self, format_spec):
|
||||
@@ -206,7 +206,7 @@ simple_magic_methods.py:32:9: ANN204 [*] Missing return type annotation for spec
|
||||
| ^^^^^^^^^^^ ANN204
|
||||
33 | ...
|
||||
|
|
||||
= help: Add return type annotation: `complex`
|
||||
= help: Add `None` return type
|
||||
|
||||
ℹ Unsafe fix
|
||||
29 29 | def __contains__(self, item):
|
||||
@@ -226,7 +226,7 @@ simple_magic_methods.py:35:9: ANN204 [*] Missing return type annotation for spec
|
||||
| ^^^^^^^ ANN204
|
||||
36 | ...
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
= help: Add `None` return type
|
||||
|
||||
ℹ Unsafe fix
|
||||
32 32 | def __complex__(self):
|
||||
@@ -246,7 +246,7 @@ simple_magic_methods.py:38:9: ANN204 [*] Missing return type annotation for spec
|
||||
| ^^^^^^^^^ ANN204
|
||||
39 | ...
|
||||
|
|
||||
= help: Add return type annotation: `float`
|
||||
= help: Add `None` return type
|
||||
|
||||
ℹ Unsafe fix
|
||||
35 35 | def __int__(self):
|
||||
@@ -266,7 +266,7 @@ simple_magic_methods.py:41:9: ANN204 [*] Missing return type annotation for spec
|
||||
| ^^^^^^^^^ ANN204
|
||||
42 | ...
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
= help: Add `None` return type
|
||||
|
||||
ℹ Unsafe fix
|
||||
38 38 | def __float__(self):
|
||||
|
||||
@@ -1,26 +1,15 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_annotations/mod.rs
|
||||
---
|
||||
suppress_none_returning.py:45:5: ANN201 [*] Missing return type annotation for public function `foo`
|
||||
suppress_none_returning.py:45:5: ANN201 Missing return type annotation for public function `foo`
|
||||
|
|
||||
44 | # Error
|
||||
45 | def foo():
|
||||
| ^^^ ANN201
|
||||
46 | return True
|
||||
|
|
||||
= help: Add return type annotation: `bool`
|
||||
|
||||
ℹ Unsafe fix
|
||||
42 42 |
|
||||
43 43 |
|
||||
44 44 | # Error
|
||||
45 |-def foo():
|
||||
45 |+def foo() -> bool:
|
||||
46 46 | return True
|
||||
47 47 |
|
||||
48 48 |
|
||||
|
||||
suppress_none_returning.py:50:5: ANN201 [*] Missing return type annotation for public function `foo`
|
||||
suppress_none_returning.py:50:5: ANN201 Missing return type annotation for public function `foo`
|
||||
|
|
||||
49 | # Error
|
||||
50 | def foo():
|
||||
@@ -28,17 +17,6 @@ suppress_none_returning.py:50:5: ANN201 [*] Missing return type annotation for p
|
||||
51 | a = 2 + 2
|
||||
52 | if a == 4:
|
||||
|
|
||||
= help: Add return type annotation: `bool | None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
47 47 |
|
||||
48 48 |
|
||||
49 49 | # Error
|
||||
50 |-def foo():
|
||||
50 |+def foo() -> bool | None:
|
||||
51 51 | a = 2 + 2
|
||||
52 52 | if a == 4:
|
||||
53 53 | return True
|
||||
|
||||
suppress_none_returning.py:59:9: ANN001 Missing type annotation for function argument `a`
|
||||
|
|
||||
|
||||
@@ -10,7 +10,6 @@ mod tests {
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::registry::Rule;
|
||||
use crate::settings::types::PreviewMode;
|
||||
use crate::test::test_path;
|
||||
use crate::{assert_messages, settings};
|
||||
|
||||
@@ -26,22 +25,4 @@ mod tests {
|
||||
assert_messages!(snapshot, diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(Rule::BooleanTypeHintPositionalArgument, Path::new("FBT.py"))]
|
||||
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!(
|
||||
"preview__{}_{}",
|
||||
rule_code.noqa_code(),
|
||||
path.to_string_lossy()
|
||||
);
|
||||
let diagnostics = test_path(
|
||||
Path::new("flake8_boolean_trap").join(path).as_path(),
|
||||
&settings::LinterSettings {
|
||||
preview: PreviewMode::Enabled,
|
||||
..settings::LinterSettings::for_rule(rule_code)
|
||||
},
|
||||
)?;
|
||||
assert_messages!(snapshot, diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ use ruff_diagnostics::Diagnostic;
|
||||
use ruff_diagnostics::Violation;
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::call_path::collect_call_path;
|
||||
use ruff_python_semantic::SemanticModel;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
@@ -27,9 +26,6 @@ use crate::rules::flake8_boolean_trap::helpers::is_allowed_func_def;
|
||||
/// keyword-only argument, to force callers to be explicit when providing
|
||||
/// the argument.
|
||||
///
|
||||
/// In [preview], this rule will also flag annotations that include boolean
|
||||
/// variants, like `bool | int`.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// from math import ceil, floor
|
||||
@@ -90,8 +86,6 @@ use crate::rules::flake8_boolean_trap::helpers::is_allowed_func_def;
|
||||
/// ## References
|
||||
/// - [Python documentation: Calls](https://docs.python.org/3/reference/expressions.html#calls)
|
||||
/// - [_How to Avoid “The Boolean Trap”_ by Adam Johnson](https://adamj.eu/tech/2021/07/10/python-type-hints-how-to-avoid-the-boolean-trap/)
|
||||
///
|
||||
/// [preview]: https://docs.astral.sh/ruff/preview/
|
||||
#[violation]
|
||||
pub struct BooleanTypeHintPositionalArgument;
|
||||
|
||||
@@ -102,7 +96,6 @@ impl Violation for BooleanTypeHintPositionalArgument {
|
||||
}
|
||||
}
|
||||
|
||||
/// FBT001
|
||||
pub(crate) fn boolean_type_hint_positional_argument(
|
||||
checker: &mut Checker,
|
||||
name: &str,
|
||||
@@ -129,17 +122,15 @@ pub(crate) fn boolean_type_hint_positional_argument(
|
||||
let Some(annotation) = parameter.annotation.as_ref() else {
|
||||
continue;
|
||||
};
|
||||
if checker.settings.preview.is_enabled() {
|
||||
if !match_annotation_to_complex_bool(annotation, checker.semantic()) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if !match_annotation_to_literal_bool(annotation) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if !checker.semantic().is_builtin("bool") {
|
||||
return;
|
||||
|
||||
// check for both bool (python class) and 'bool' (string annotation)
|
||||
let hint = match annotation.as_ref() {
|
||||
Expr::Name(name) => &name.id == "bool",
|
||||
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => value == "bool",
|
||||
_ => false,
|
||||
};
|
||||
if !hint || !checker.semantic().is_builtin("bool") {
|
||||
continue;
|
||||
}
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
BooleanTypeHintPositionalArgument,
|
||||
@@ -147,52 +138,3 @@ pub(crate) fn boolean_type_hint_positional_argument(
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the annotation is a boolean type hint (e.g., `bool`).
|
||||
fn match_annotation_to_literal_bool(annotation: &Expr) -> bool {
|
||||
match annotation {
|
||||
// Ex) `True`
|
||||
Expr::Name(name) => &name.id == "bool",
|
||||
// Ex) `"True"`
|
||||
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => value == "bool",
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the annotation is a boolean type hint (e.g., `bool`), or a type hint that
|
||||
/// includes boolean as a variant (e.g., `bool | int`).
|
||||
fn match_annotation_to_complex_bool(annotation: &Expr, semantic: &SemanticModel) -> bool {
|
||||
match annotation {
|
||||
// Ex) `bool`
|
||||
Expr::Name(name) => &name.id == "bool",
|
||||
// Ex) `"bool"`
|
||||
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => value == "bool",
|
||||
// Ex) `bool | int`
|
||||
Expr::BinOp(ast::ExprBinOp {
|
||||
left,
|
||||
op: ast::Operator::BitOr,
|
||||
right,
|
||||
..
|
||||
}) => {
|
||||
match_annotation_to_complex_bool(left, semantic)
|
||||
|| match_annotation_to_complex_bool(right, semantic)
|
||||
}
|
||||
// Ex) `typing.Union[bool, int]`
|
||||
Expr::Subscript(ast::ExprSubscript { value, slice, .. }) => {
|
||||
if semantic.match_typing_expr(value, "Union") {
|
||||
if let Expr::Tuple(ast::ExprTuple { elts, .. }) = slice.as_ref() {
|
||||
elts.iter()
|
||||
.any(|elt| match_annotation_to_complex_bool(elt, semantic))
|
||||
} else {
|
||||
// Union with a single type is an invalid type annotation
|
||||
false
|
||||
}
|
||||
} else if semantic.match_typing_expr(value, "Optional") {
|
||||
match_annotation_to_complex_bool(slice, semantic)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_boolean_trap/mod.rs
|
||||
---
|
||||
FBT.py:4:5: FBT001 Boolean-typed positional argument in function definition
|
||||
|
|
||||
2 | posonly_nohint,
|
||||
3 | posonly_nonboolhint: int,
|
||||
4 | posonly_boolhint: bool,
|
||||
| ^^^^^^^^^^^^^^^^ FBT001
|
||||
5 | posonly_boolstrhint: "bool",
|
||||
6 | /,
|
||||
|
|
||||
|
||||
FBT.py:5:5: FBT001 Boolean-typed positional argument in function definition
|
||||
|
|
||||
3 | posonly_nonboolhint: int,
|
||||
4 | posonly_boolhint: bool,
|
||||
5 | posonly_boolstrhint: "bool",
|
||||
| ^^^^^^^^^^^^^^^^^^^ FBT001
|
||||
6 | /,
|
||||
7 | offset,
|
||||
|
|
||||
|
||||
FBT.py:10:5: FBT001 Boolean-typed positional argument in function definition
|
||||
|
|
||||
8 | posorkw_nonvalued_nohint,
|
||||
9 | posorkw_nonvalued_nonboolhint: int,
|
||||
10 | posorkw_nonvalued_boolhint: bool,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ FBT001
|
||||
11 | posorkw_nonvalued_boolstrhint: "bool",
|
||||
12 | posorkw_boolvalued_nohint=True,
|
||||
|
|
||||
|
||||
FBT.py:11:5: FBT001 Boolean-typed positional argument in function definition
|
||||
|
|
||||
9 | posorkw_nonvalued_nonboolhint: int,
|
||||
10 | posorkw_nonvalued_boolhint: bool,
|
||||
11 | posorkw_nonvalued_boolstrhint: "bool",
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FBT001
|
||||
12 | posorkw_boolvalued_nohint=True,
|
||||
13 | posorkw_boolvalued_nonboolhint: int = True,
|
||||
|
|
||||
|
||||
FBT.py:14:5: FBT001 Boolean-typed positional argument in function definition
|
||||
|
|
||||
12 | posorkw_boolvalued_nohint=True,
|
||||
13 | posorkw_boolvalued_nonboolhint: int = True,
|
||||
14 | posorkw_boolvalued_boolhint: bool = True,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ FBT001
|
||||
15 | posorkw_boolvalued_boolstrhint: "bool" = True,
|
||||
16 | posorkw_nonboolvalued_nohint=1,
|
||||
|
|
||||
|
||||
FBT.py:15:5: FBT001 Boolean-typed positional argument in function definition
|
||||
|
|
||||
13 | posorkw_boolvalued_nonboolhint: int = True,
|
||||
14 | posorkw_boolvalued_boolhint: bool = True,
|
||||
15 | posorkw_boolvalued_boolstrhint: "bool" = True,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FBT001
|
||||
16 | posorkw_nonboolvalued_nohint=1,
|
||||
17 | posorkw_nonboolvalued_nonboolhint: int = 2,
|
||||
|
|
||||
|
||||
FBT.py:18:5: FBT001 Boolean-typed positional argument in function definition
|
||||
|
|
||||
16 | posorkw_nonboolvalued_nohint=1,
|
||||
17 | posorkw_nonboolvalued_nonboolhint: int = 2,
|
||||
18 | posorkw_nonboolvalued_boolhint: bool = 3,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FBT001
|
||||
19 | posorkw_nonboolvalued_boolstrhint: "bool" = 4,
|
||||
20 | *,
|
||||
|
|
||||
|
||||
FBT.py:19:5: FBT001 Boolean-typed positional argument in function definition
|
||||
|
|
||||
17 | posorkw_nonboolvalued_nonboolhint: int = 2,
|
||||
18 | posorkw_nonboolvalued_boolhint: bool = 3,
|
||||
19 | posorkw_nonboolvalued_boolstrhint: "bool" = 4,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ FBT001
|
||||
20 | *,
|
||||
21 | kwonly_nonvalued_nohint,
|
||||
|
|
||||
|
||||
FBT.py:89:19: FBT001 Boolean-typed positional argument in function definition
|
||||
|
|
||||
88 | # FBT001: Boolean positional arg in function definition
|
||||
89 | def foo(self, value: bool) -> None:
|
||||
| ^^^^^ FBT001
|
||||
90 | pass
|
||||
|
|
||||
|
||||
FBT.py:99:10: FBT001 Boolean-typed positional argument in function definition
|
||||
|
|
||||
99 | def func(x: Union[list, Optional[int | str | float | bool]]):
|
||||
| ^ FBT001
|
||||
100 | pass
|
||||
|
|
||||
|
||||
FBT.py:103:10: FBT001 Boolean-typed positional argument in function definition
|
||||
|
|
||||
103 | def func(x: bool | str):
|
||||
| ^ FBT001
|
||||
104 | pass
|
||||
|
|
||||
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
use ruff_python_ast::PySourceType;
|
||||
use ruff_python_stdlib::builtins::{is_ipython_builtin, is_python_builtin};
|
||||
use ruff_python_stdlib::builtins::is_builtin;
|
||||
|
||||
pub(super) fn shadows_builtin(
|
||||
name: &str,
|
||||
ignorelist: &[String],
|
||||
source_type: PySourceType,
|
||||
) -> bool {
|
||||
if is_python_builtin(name) || source_type.is_ipynb() && is_ipython_builtin(name) {
|
||||
ignorelist.iter().all(|ignore| ignore != name)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
pub(super) fn shadows_builtin(name: &str, ignorelist: &[String]) -> bool {
|
||||
is_builtin(name) && ignorelist.iter().all(|ignore| ignore != name)
|
||||
}
|
||||
|
||||
@@ -67,7 +67,6 @@ pub(crate) fn builtin_argument_shadowing(checker: &mut Checker, parameter: &Para
|
||||
if shadows_builtin(
|
||||
parameter.name.as_str(),
|
||||
&checker.settings.flake8_builtins.builtins_ignorelist,
|
||||
checker.source_type,
|
||||
) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
BuiltinArgumentShadowing {
|
||||
|
||||
@@ -74,11 +74,7 @@ pub(crate) fn builtin_attribute_shadowing(
|
||||
name: &str,
|
||||
range: TextRange,
|
||||
) {
|
||||
if shadows_builtin(
|
||||
name,
|
||||
&checker.settings.flake8_builtins.builtins_ignorelist,
|
||||
checker.source_type,
|
||||
) {
|
||||
if shadows_builtin(name, &checker.settings.flake8_builtins.builtins_ignorelist) {
|
||||
// Ignore shadowing within `TypedDict` definitions, since these are only accessible through
|
||||
// subscripting and not through attribute access.
|
||||
if class_def
|
||||
@@ -106,11 +102,7 @@ pub(crate) fn builtin_method_shadowing(
|
||||
decorator_list: &[Decorator],
|
||||
range: TextRange,
|
||||
) {
|
||||
if shadows_builtin(
|
||||
name,
|
||||
&checker.settings.flake8_builtins.builtins_ignorelist,
|
||||
checker.source_type,
|
||||
) {
|
||||
if shadows_builtin(name, &checker.settings.flake8_builtins.builtins_ignorelist) {
|
||||
// Ignore some standard-library methods. Ideally, we'd ignore all overridden methods, since
|
||||
// those should be flagged on the superclass, but that's more difficult.
|
||||
if is_standard_library_override(name, class_def, checker.semantic()) {
|
||||
|
||||
@@ -60,11 +60,7 @@ impl Violation for BuiltinVariableShadowing {
|
||||
|
||||
/// A001
|
||||
pub(crate) fn builtin_variable_shadowing(checker: &mut Checker, name: &str, range: TextRange) {
|
||||
if shadows_builtin(
|
||||
name,
|
||||
&checker.settings.flake8_builtins.builtins_ignorelist,
|
||||
checker.source_type,
|
||||
) {
|
||||
if shadows_builtin(name, &checker.settings.flake8_builtins.builtins_ignorelist) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
BuiltinVariableShadowing {
|
||||
name: name.to_string(),
|
||||
|
||||
@@ -3,7 +3,6 @@ use itertools::Itertools;
|
||||
use ruff_diagnostics::{AlwaysFixableViolation, Violation};
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_python_parser::lexer::{LexResult, Spanned};
|
||||
use ruff_python_parser::Tok;
|
||||
use ruff_source_file::Locator;
|
||||
@@ -30,33 +29,22 @@ enum TokenType {
|
||||
|
||||
/// Simplified token specialized for the task.
|
||||
#[derive(Copy, Clone)]
|
||||
struct Token {
|
||||
r#type: TokenType,
|
||||
range: TextRange,
|
||||
struct Token<'tok> {
|
||||
type_: TokenType,
|
||||
// Underlying token.
|
||||
spanned: Option<&'tok Spanned>,
|
||||
}
|
||||
|
||||
impl Ranged for Token {
|
||||
fn range(&self) -> TextRange {
|
||||
self.range
|
||||
}
|
||||
}
|
||||
|
||||
impl Token {
|
||||
fn new(r#type: TokenType, range: TextRange) -> Self {
|
||||
Self { r#type, range }
|
||||
}
|
||||
|
||||
fn irrelevant() -> Token {
|
||||
impl<'tok> Token<'tok> {
|
||||
const fn irrelevant() -> Token<'static> {
|
||||
Token {
|
||||
r#type: TokenType::Irrelevant,
|
||||
range: TextRange::default(),
|
||||
type_: TokenType::Irrelevant,
|
||||
spanned: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Spanned> for Token {
|
||||
fn from(spanned: &Spanned) -> Self {
|
||||
let r#type = match &spanned.0 {
|
||||
const fn from_spanned(spanned: &'tok Spanned) -> Token<'tok> {
|
||||
let type_ = match &spanned.0 {
|
||||
Tok::NonLogicalNewline => TokenType::NonLogicalNewline,
|
||||
Tok::Newline => TokenType::Newline,
|
||||
Tok::For => TokenType::For,
|
||||
@@ -75,8 +63,8 @@ impl From<&Spanned> for Token {
|
||||
_ => TokenType::Irrelevant,
|
||||
};
|
||||
Self {
|
||||
range: spanned.1,
|
||||
r#type,
|
||||
spanned: Some(spanned),
|
||||
type_,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,14 +92,14 @@ enum ContextType {
|
||||
/// Comma context - described a comma-delimited "situation".
|
||||
#[derive(Copy, Clone)]
|
||||
struct Context {
|
||||
r#type: ContextType,
|
||||
type_: ContextType,
|
||||
num_commas: u32,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
const fn new(r#type: ContextType) -> Self {
|
||||
const fn new(type_: ContextType) -> Self {
|
||||
Self {
|
||||
r#type,
|
||||
type_,
|
||||
num_commas: 0,
|
||||
}
|
||||
}
|
||||
@@ -234,49 +222,21 @@ pub(crate) fn trailing_commas(
|
||||
diagnostics: &mut Vec<Diagnostic>,
|
||||
tokens: &[LexResult],
|
||||
locator: &Locator,
|
||||
indexer: &Indexer,
|
||||
) {
|
||||
let mut fstrings = 0u32;
|
||||
let tokens = tokens
|
||||
.iter()
|
||||
.flatten()
|
||||
.filter_map(|spanned @ (tok, tok_range)| match tok {
|
||||
// Completely ignore comments -- they just interfere with the logic.
|
||||
Tok::Comment(_) => None,
|
||||
// F-strings are handled as `String` token type with the complete range
|
||||
// of the outermost f-string. This means that the expression inside the
|
||||
// f-string is not checked for trailing commas.
|
||||
Tok::FStringStart => {
|
||||
fstrings = fstrings.saturating_add(1);
|
||||
None
|
||||
}
|
||||
Tok::FStringEnd => {
|
||||
fstrings = fstrings.saturating_sub(1);
|
||||
if fstrings == 0 {
|
||||
indexer
|
||||
.fstring_ranges()
|
||||
.outermost(tok_range.start())
|
||||
.map(|range| Token::new(TokenType::String, range))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if fstrings == 0 {
|
||||
Some(Token::from(spanned))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
});
|
||||
// Completely ignore comments -- they just interfere with the logic.
|
||||
.filter(|&r| !matches!(r, (Tok::Comment(_), _)))
|
||||
.map(Token::from_spanned);
|
||||
let tokens = [Token::irrelevant(), Token::irrelevant()]
|
||||
.into_iter()
|
||||
.chain(tokens);
|
||||
// Collapse consecutive newlines to the first one -- trailing commas are
|
||||
// added before the first newline.
|
||||
let tokens = tokens.coalesce(|previous, current| {
|
||||
if previous.r#type == TokenType::NonLogicalNewline
|
||||
&& current.r#type == TokenType::NonLogicalNewline
|
||||
if previous.type_ == TokenType::NonLogicalNewline
|
||||
&& current.type_ == TokenType::NonLogicalNewline
|
||||
{
|
||||
Ok(previous)
|
||||
} else {
|
||||
@@ -289,8 +249,8 @@ pub(crate) fn trailing_commas(
|
||||
|
||||
for (prev_prev, prev, token) in tokens.tuple_windows() {
|
||||
// Update the comma context stack.
|
||||
match token.r#type {
|
||||
TokenType::OpeningBracket => match (prev.r#type, prev_prev.r#type) {
|
||||
match token.type_ {
|
||||
TokenType::OpeningBracket => match (prev.type_, prev_prev.type_) {
|
||||
(TokenType::Named, TokenType::Def) => {
|
||||
stack.push(Context::new(ContextType::FunctionParameters));
|
||||
}
|
||||
@@ -301,7 +261,7 @@ pub(crate) fn trailing_commas(
|
||||
stack.push(Context::new(ContextType::Tuple));
|
||||
}
|
||||
},
|
||||
TokenType::OpeningSquareBracket => match prev.r#type {
|
||||
TokenType::OpeningSquareBracket => match prev.type_ {
|
||||
TokenType::ClosingBracket | TokenType::Named | TokenType::String => {
|
||||
stack.push(Context::new(ContextType::Subscript));
|
||||
}
|
||||
@@ -328,8 +288,8 @@ pub(crate) fn trailing_commas(
|
||||
let context = &stack[stack.len() - 1];
|
||||
|
||||
// Is it allowed to have a trailing comma before this token?
|
||||
let comma_allowed = token.r#type == TokenType::ClosingBracket
|
||||
&& match context.r#type {
|
||||
let comma_allowed = token.type_ == TokenType::ClosingBracket
|
||||
&& match context.type_ {
|
||||
ContextType::No => false,
|
||||
ContextType::FunctionParameters => true,
|
||||
ContextType::CallArguments => true,
|
||||
@@ -344,21 +304,22 @@ pub(crate) fn trailing_commas(
|
||||
};
|
||||
|
||||
// Is prev a prohibited trailing comma?
|
||||
let comma_prohibited = prev.r#type == TokenType::Comma && {
|
||||
let comma_prohibited = prev.type_ == TokenType::Comma && {
|
||||
// Is `(1,)` or `x[1,]`?
|
||||
let is_singleton_tuplish =
|
||||
matches!(context.r#type, ContextType::Subscript | ContextType::Tuple)
|
||||
matches!(context.type_, ContextType::Subscript | ContextType::Tuple)
|
||||
&& context.num_commas <= 1;
|
||||
// There was no non-logical newline, so prohibit (except in `(1,)` or `x[1,]`).
|
||||
if comma_allowed && !is_singleton_tuplish {
|
||||
true
|
||||
// Lambdas not handled by comma_allowed so handle it specially.
|
||||
} else {
|
||||
context.r#type == ContextType::LambdaParameters && token.r#type == TokenType::Colon
|
||||
context.type_ == ContextType::LambdaParameters && token.type_ == TokenType::Colon
|
||||
}
|
||||
};
|
||||
if comma_prohibited {
|
||||
let mut diagnostic = Diagnostic::new(ProhibitedTrailingComma, prev.range());
|
||||
let comma = prev.spanned.unwrap();
|
||||
let mut diagnostic = Diagnostic::new(ProhibitedTrailingComma, comma.1);
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(diagnostic.range())));
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
@@ -366,9 +327,10 @@ pub(crate) fn trailing_commas(
|
||||
// Is prev a prohibited trailing comma on a bare tuple?
|
||||
// Approximation: any comma followed by a statement-ending newline.
|
||||
let bare_comma_prohibited =
|
||||
prev.r#type == TokenType::Comma && token.r#type == TokenType::Newline;
|
||||
prev.type_ == TokenType::Comma && token.type_ == TokenType::Newline;
|
||||
if bare_comma_prohibited {
|
||||
diagnostics.push(Diagnostic::new(TrailingCommaOnBareTuple, prev.range()));
|
||||
let comma = prev.spanned.unwrap();
|
||||
diagnostics.push(Diagnostic::new(TrailingCommaOnBareTuple, comma.1));
|
||||
}
|
||||
|
||||
// Comma is required if:
|
||||
@@ -377,37 +339,40 @@ pub(crate) fn trailing_commas(
|
||||
// - Not already present,
|
||||
// - Not on an empty (), {}, [].
|
||||
let comma_required = comma_allowed
|
||||
&& prev.r#type == TokenType::NonLogicalNewline
|
||||
&& prev.type_ == TokenType::NonLogicalNewline
|
||||
&& !matches!(
|
||||
prev_prev.r#type,
|
||||
prev_prev.type_,
|
||||
TokenType::Comma
|
||||
| TokenType::OpeningBracket
|
||||
| TokenType::OpeningSquareBracket
|
||||
| TokenType::OpeningCurlyBracket
|
||||
);
|
||||
if comma_required {
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(MissingTrailingComma, TextRange::empty(prev_prev.end()));
|
||||
let missing_comma = prev_prev.spanned.unwrap();
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
MissingTrailingComma,
|
||||
TextRange::empty(missing_comma.1.end()),
|
||||
);
|
||||
// Create a replacement that includes the final bracket (or other token),
|
||||
// rather than just inserting a comma at the end. This prevents the UP034 fix
|
||||
// removing any brackets in the same linter pass - doing both at the same time could
|
||||
// lead to a syntax error.
|
||||
let contents = locator.slice(prev_prev.range());
|
||||
let contents = locator.slice(missing_comma.1);
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
format!("{contents},"),
|
||||
prev_prev.range(),
|
||||
missing_comma.1,
|
||||
)));
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
// Pop the current context if the current token ended it.
|
||||
// The top context is never popped (if unbalanced closing brackets).
|
||||
let pop_context = match context.r#type {
|
||||
let pop_context = match context.type_ {
|
||||
// Lambda terminated by `:`.
|
||||
ContextType::LambdaParameters => token.r#type == TokenType::Colon,
|
||||
ContextType::LambdaParameters => token.type_ == TokenType::Colon,
|
||||
// All others terminated by a closing bracket.
|
||||
// flake8-commas doesn't verify that it matches the opening...
|
||||
_ => token.r#type == TokenType::ClosingBracket,
|
||||
_ => token.type_ == TokenType::ClosingBracket,
|
||||
};
|
||||
if pop_context && stack.len() > 1 {
|
||||
stack.pop();
|
||||
|
||||
@@ -939,43 +939,4 @@ COM81.py:632:42: COM812 [*] Trailing comma missing
|
||||
634 634 |
|
||||
635 635 | foo = namedtuple(
|
||||
|
||||
COM81.py:644:46: COM819 [*] Trailing comma prohibited
|
||||
|
|
||||
643 | # F-strings
|
||||
644 | kwargs.pop("remove", f"this {trailing_comma}",)
|
||||
| ^ COM819
|
||||
645 |
|
||||
646 | raise Exception(
|
||||
|
|
||||
= help: Remove trailing comma
|
||||
|
||||
ℹ Safe fix
|
||||
641 641 | )
|
||||
642 642 |
|
||||
643 643 | # F-strings
|
||||
644 |-kwargs.pop("remove", f"this {trailing_comma}",)
|
||||
644 |+kwargs.pop("remove", f"this {trailing_comma}")
|
||||
645 645 |
|
||||
646 646 | raise Exception(
|
||||
647 647 | "first", extra=f"Add trailing comma here ->"
|
||||
|
||||
COM81.py:647:49: COM812 [*] Trailing comma missing
|
||||
|
|
||||
646 | raise Exception(
|
||||
647 | "first", extra=f"Add trailing comma here ->"
|
||||
| COM812
|
||||
648 | )
|
||||
|
|
||||
= help: Add trailing comma
|
||||
|
||||
ℹ Safe fix
|
||||
644 644 | kwargs.pop("remove", f"this {trailing_comma}",)
|
||||
645 645 |
|
||||
646 646 | raise Exception(
|
||||
647 |- "first", extra=f"Add trailing comma here ->"
|
||||
647 |+ "first", extra=f"Add trailing comma here ->",
|
||||
648 648 | )
|
||||
649 649 |
|
||||
650 650 | assert False, f"<- This is not a trailing comma"
|
||||
|
||||
|
||||
|
||||
@@ -12,11 +12,11 @@ mod tests {
|
||||
#[test]
|
||||
fn notice() {
|
||||
let diagnostics = test_snippet(
|
||||
r"
|
||||
r#"
|
||||
# Copyright 2023
|
||||
|
||||
import os
|
||||
"
|
||||
"#
|
||||
.trim(),
|
||||
&settings::LinterSettings::for_rules(vec![Rule::MissingCopyrightNotice]),
|
||||
);
|
||||
@@ -26,11 +26,11 @@ import os
|
||||
#[test]
|
||||
fn notice_with_c() {
|
||||
let diagnostics = test_snippet(
|
||||
r"
|
||||
r#"
|
||||
# Copyright (C) 2023
|
||||
|
||||
import os
|
||||
"
|
||||
"#
|
||||
.trim(),
|
||||
&settings::LinterSettings::for_rules(vec![Rule::MissingCopyrightNotice]),
|
||||
);
|
||||
@@ -40,11 +40,11 @@ import os
|
||||
#[test]
|
||||
fn notice_with_caps() {
|
||||
let diagnostics = test_snippet(
|
||||
r"
|
||||
r#"
|
||||
# COPYRIGHT (C) 2023
|
||||
|
||||
import os
|
||||
"
|
||||
"#
|
||||
.trim(),
|
||||
&settings::LinterSettings::for_rules(vec![Rule::MissingCopyrightNotice]),
|
||||
);
|
||||
@@ -54,11 +54,11 @@ import os
|
||||
#[test]
|
||||
fn notice_with_range() {
|
||||
let diagnostics = test_snippet(
|
||||
r"
|
||||
r#"
|
||||
# Copyright (C) 2021-2023
|
||||
|
||||
import os
|
||||
"
|
||||
"#
|
||||
.trim(),
|
||||
&settings::LinterSettings::for_rules(vec![Rule::MissingCopyrightNotice]),
|
||||
);
|
||||
@@ -68,11 +68,11 @@ import os
|
||||
#[test]
|
||||
fn valid_author() {
|
||||
let diagnostics = test_snippet(
|
||||
r"
|
||||
r#"
|
||||
# Copyright (C) 2023 Ruff
|
||||
|
||||
import os
|
||||
"
|
||||
"#
|
||||
.trim(),
|
||||
&settings::LinterSettings {
|
||||
flake8_copyright: super::settings::Settings {
|
||||
@@ -88,11 +88,11 @@ import os
|
||||
#[test]
|
||||
fn invalid_author() {
|
||||
let diagnostics = test_snippet(
|
||||
r"
|
||||
r#"
|
||||
# Copyright (C) 2023 Some Author
|
||||
|
||||
import os
|
||||
"
|
||||
"#
|
||||
.trim(),
|
||||
&settings::LinterSettings {
|
||||
flake8_copyright: super::settings::Settings {
|
||||
@@ -108,9 +108,9 @@ import os
|
||||
#[test]
|
||||
fn small_file() {
|
||||
let diagnostics = test_snippet(
|
||||
r"
|
||||
r#"
|
||||
import os
|
||||
"
|
||||
"#
|
||||
.trim(),
|
||||
&settings::LinterSettings {
|
||||
flake8_copyright: super::settings::Settings {
|
||||
@@ -126,7 +126,7 @@ import os
|
||||
#[test]
|
||||
fn late_notice() {
|
||||
let diagnostics = test_snippet(
|
||||
r"
|
||||
r#"
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
@@ -149,7 +149,7 @@ import os
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
|
||||
# Copyright 2023
|
||||
"
|
||||
"#
|
||||
.trim(),
|
||||
&settings::LinterSettings::for_rules(vec![Rule::MissingCopyrightNotice]),
|
||||
);
|
||||
@@ -159,8 +159,8 @@ import os
|
||||
#[test]
|
||||
fn char_boundary() {
|
||||
let diagnostics = test_snippet(
|
||||
r"কককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককক
|
||||
"
|
||||
r#"কককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককক
|
||||
"#
|
||||
.trim(),
|
||||
&settings::LinterSettings::for_rules(vec![Rule::MissingCopyrightNotice]),
|
||||
);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use std::path::Path;
|
||||
|
||||
use ruff_python_parser::lexer::LexResult;
|
||||
use ruff_python_parser::Tok;
|
||||
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_source_file::Locator;
|
||||
pub(crate) use shebang_leading_whitespace::*;
|
||||
pub(crate) use shebang_missing_executable_file::*;
|
||||
@@ -18,31 +20,32 @@ mod shebang_not_executable;
|
||||
mod shebang_not_first_line;
|
||||
|
||||
pub(crate) fn from_tokens(
|
||||
diagnostics: &mut Vec<Diagnostic>,
|
||||
tokens: &[LexResult],
|
||||
path: &Path,
|
||||
locator: &Locator,
|
||||
indexer: &Indexer,
|
||||
diagnostics: &mut Vec<Diagnostic>,
|
||||
) {
|
||||
let mut has_any_shebang = false;
|
||||
for range in indexer.comment_ranges() {
|
||||
let comment = locator.slice(*range);
|
||||
if let Some(shebang) = ShebangDirective::try_extract(comment) {
|
||||
has_any_shebang = true;
|
||||
for (tok, range) in tokens.iter().flatten() {
|
||||
if let Tok::Comment(comment) = tok {
|
||||
if let Some(shebang) = ShebangDirective::try_extract(comment) {
|
||||
has_any_shebang = true;
|
||||
|
||||
if let Some(diagnostic) = shebang_missing_python(*range, &shebang) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
if let Some(diagnostic) = shebang_missing_python(*range, &shebang) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
if let Some(diagnostic) = shebang_not_executable(path, *range) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
if let Some(diagnostic) = shebang_not_executable(path, *range) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
if let Some(diagnostic) = shebang_leading_whitespace(*range, locator) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
if let Some(diagnostic) = shebang_leading_whitespace(*range, locator) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
if let Some(diagnostic) = shebang_not_first_line(*range, locator) {
|
||||
diagnostics.push(diagnostic);
|
||||
if let Some(diagnostic) = shebang_not_first_line(*range, locator) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
use ruff_python_ast::Expr;
|
||||
use std::fmt;
|
||||
|
||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Fix};
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::imports::{AnyImport, ImportFrom};
|
||||
use ruff_python_ast::Expr;
|
||||
use ruff_text_size::{Ranged, TextSize};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::importer::Importer;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of PEP 585- and PEP 604-style type annotations in Python
|
||||
@@ -44,10 +42,6 @@ use crate::importer::Importer;
|
||||
/// ...
|
||||
/// ```
|
||||
///
|
||||
/// ## Fix safety
|
||||
/// This rule's fix is marked as unsafe, as adding `from __future__ import annotations`
|
||||
/// may change the semantics of the program.
|
||||
///
|
||||
/// ## Options
|
||||
/// - `target-version`
|
||||
#[violation]
|
||||
@@ -72,28 +66,18 @@ impl fmt::Display for Reason {
|
||||
}
|
||||
}
|
||||
|
||||
impl AlwaysFixableViolation for FutureRequiredTypeAnnotation {
|
||||
impl Violation for FutureRequiredTypeAnnotation {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let FutureRequiredTypeAnnotation { reason } = self;
|
||||
format!("Missing `from __future__ import annotations`, but uses {reason}")
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> String {
|
||||
format!("Add `from __future__ import annotations`")
|
||||
}
|
||||
}
|
||||
|
||||
/// FA102
|
||||
pub(crate) fn future_required_type_annotation(checker: &mut Checker, expr: &Expr, reason: Reason) {
|
||||
let mut diagnostic = Diagnostic::new(FutureRequiredTypeAnnotation { reason }, expr.range());
|
||||
if let Some(python_ast) = checker.semantic().definitions.python_ast() {
|
||||
let required_import =
|
||||
AnyImport::ImportFrom(ImportFrom::member("__future__", "annotations"));
|
||||
diagnostic.set_fix(Fix::unsafe_edit(
|
||||
Importer::new(python_ast, checker.locator(), checker.stylist())
|
||||
.add_import(&required_import, TextSize::default()),
|
||||
));
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
FutureRequiredTypeAnnotation { reason },
|
||||
expr.range(),
|
||||
));
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user