Compare commits
38 Commits
SIM300-CON
...
zb/fixable
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3980c1113 | ||
|
|
4f6e03ced8 | ||
|
|
29513398d2 | ||
|
|
fa78d2d97c | ||
|
|
8b70240fa2 | ||
|
|
e85e0d45f3 | ||
|
|
9b43203575 | ||
|
|
bae3fa435d | ||
|
|
6e65601055 | ||
|
|
9d6444138b | ||
|
|
6d0c9c4e95 | ||
|
|
20def33fb7 | ||
|
|
506ffade6c | ||
|
|
5040fb8cec | ||
|
|
09ac0f9e72 | ||
|
|
34d7584ca3 | ||
|
|
097d0a4322 | ||
|
|
9a672ec112 | ||
|
|
7a109164a6 | ||
|
|
74dba3ee59 | ||
|
|
cdea7d71a3 | ||
|
|
bb86d359d4 | ||
|
|
9cc257ee7d | ||
|
|
a06723da2b | ||
|
|
fa2c37b411 | ||
|
|
3cc719bd74 | ||
|
|
d835b28d01 | ||
|
|
1e7bc1dffe | ||
|
|
e241c1c5df | ||
|
|
b0ae1199e8 | ||
|
|
a9ceef5b5d | ||
|
|
a3e06e5a9d | ||
|
|
3c2b800d26 | ||
|
|
0263f2715e | ||
|
|
c6d8076034 | ||
|
|
8cb7950102 | ||
|
|
ef4bd8d5ff | ||
|
|
5d41c84ef7 |
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
@@ -5,6 +5,10 @@ updates:
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
labels: ["internal"]
|
||||
groups:
|
||||
actions:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: "cargo"
|
||||
directory: "/"
|
||||
|
||||
4
.github/workflows/ci.yaml
vendored
4
.github/workflows/ci.yaml
vendored
@@ -95,9 +95,9 @@ jobs:
|
||||
rustup target add wasm32-unknown-unknown
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "Clippy"
|
||||
run: cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||
run: cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
|
||||
- name: "Clippy (wasm)"
|
||||
run: cargo clippy -p ruff_wasm --target wasm32-unknown-unknown --all-features -- -D warnings
|
||||
run: cargo clippy -p ruff_wasm --target wasm32-unknown-unknown --all-features --locked -- -D warnings
|
||||
|
||||
cargo-test-linux:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
14
.github/workflows/release.yaml
vendored
14
.github/workflows/release.yaml
vendored
@@ -73,7 +73,7 @@ jobs:
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: x86_64
|
||||
args: --release --out dist
|
||||
args: --release --locked --out dist
|
||||
- name: "Test wheel - x86_64"
|
||||
run: |
|
||||
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
|
||||
@@ -112,7 +112,7 @@ jobs:
|
||||
- name: "Build wheels - universal2"
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
args: --release --target universal2-apple-darwin --out dist
|
||||
args: --release --locked --target universal2-apple-darwin --out dist
|
||||
- name: "Test wheel - universal2"
|
||||
run: |
|
||||
pip install dist/${{ env.PACKAGE_NAME }}-*universal2.whl --force-reinstall
|
||||
@@ -161,7 +161,7 @@ jobs:
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.platform.target }}
|
||||
args: --release --out dist
|
||||
args: --release --locked --out dist
|
||||
- name: "Test wheel"
|
||||
if: ${{ !startsWith(matrix.platform.target, 'aarch64') }}
|
||||
shell: bash
|
||||
@@ -210,7 +210,7 @@ jobs:
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: auto
|
||||
args: --release --out dist
|
||||
args: --release --locked --out dist
|
||||
- name: "Test wheel"
|
||||
if: ${{ startsWith(matrix.target, 'x86_64') }}
|
||||
run: |
|
||||
@@ -269,7 +269,7 @@ jobs:
|
||||
target: ${{ matrix.platform.target }}
|
||||
manylinux: auto
|
||||
docker-options: ${{ matrix.platform.maturin_docker_options }}
|
||||
args: --release --out dist
|
||||
args: --release --locked --out dist
|
||||
- uses: uraimo/run-on-arch-action@v2
|
||||
if: matrix.platform.arch != 'ppc64'
|
||||
name: Test wheel
|
||||
@@ -324,7 +324,7 @@ jobs:
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: musllinux_1_2
|
||||
args: --release --out dist
|
||||
args: --release --locked --out dist
|
||||
- name: "Test wheel"
|
||||
if: matrix.target == 'x86_64-unknown-linux-musl'
|
||||
uses: addnab/docker-run-action@v3
|
||||
@@ -379,7 +379,7 @@ jobs:
|
||||
with:
|
||||
target: ${{ matrix.platform.target }}
|
||||
manylinux: musllinux_1_2
|
||||
args: --release --out dist
|
||||
args: --release --locked --out dist
|
||||
docker-options: ${{ matrix.platform.maturin_docker_options }}
|
||||
- uses: uraimo/run-on-arch-action@v2
|
||||
name: Test wheel
|
||||
|
||||
56
CHANGELOG.md
56
CHANGELOG.md
@@ -1,5 +1,61 @@
|
||||
# Changelog
|
||||
|
||||
## 0.1.9
|
||||
|
||||
### Breaking changes
|
||||
|
||||
- Add site-packages to default exclusions ([#9188](https://github.com/astral-sh/ruff/pull/9188))
|
||||
|
||||
### Preview features
|
||||
|
||||
- Fix: Avoid parenthesizing subscript targets and values ([#9209](https://github.com/astral-sh/ruff/pull/9209))
|
||||
- \[`pylint`\] Implement `too-many-locals` (`PLR0914`) ([#9163](https://github.com/astral-sh/ruff/pull/9163))
|
||||
- Implement `reimplemented_operator` (FURB118) ([#9171](https://github.com/astral-sh/ruff/pull/9171))
|
||||
- Add a rule to detect string members in runtime-evaluated unions ([#9143](https://github.com/astral-sh/ruff/pull/9143))
|
||||
- Implement `no_blank_line_before_class_docstring` preview style ([#9154](https://github.com/astral-sh/ruff/pull/9154))
|
||||
|
||||
### Rule changes
|
||||
|
||||
- `CONSTANT_CASE` variables are improperly flagged for yoda violation (`SIM300`) ([#9164](https://github.com/astral-sh/ruff/pull/9164))
|
||||
- \[`flake8-pyi`\] Cover ParamSpecs and TypeVarTuples (`PYI018`) ([#9198](https://github.com/astral-sh/ruff/pull/9198))
|
||||
- \[`flake8-bugbear`\] Add fix for `zip-without-explicit-strict` (`B905`) ([#9176](https://github.com/astral-sh/ruff/pull/9176))
|
||||
- Add fix to automatically remove `print` and `pprint` statements (`T201`, `T203`) ([#9208](https://github.com/astral-sh/ruff/pull/9208))
|
||||
- Prefer `Never` to `NoReturn` in auto-typing in Python >= 3.11 (`ANN201`) ([#9213](https://github.com/astral-sh/ruff/pull/9213))
|
||||
|
||||
### Formatter
|
||||
|
||||
- `can_omit_optional_parentheses`: Exit early for unparenthesized expressions ([#9125](https://github.com/astral-sh/ruff/pull/9125))
|
||||
- Fix `dynamic` mode with doctests so that it doesn't exceed configured line width ([#9129](https://github.com/astral-sh/ruff/pull/9129))
|
||||
- Fix `can_omit_optional_parentheses` for expressions with a right most fstring ([#9124](https://github.com/astral-sh/ruff/pull/9124))
|
||||
- Add `target_version` to formatter options ([#9220](https://github.com/astral-sh/ruff/pull/9220))
|
||||
|
||||
### CLI
|
||||
|
||||
- Update `ruff format --check` to display message for already formatted files ([#9153](https://github.com/astral-sh/ruff/pull/9153))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Reverse order of arguments for `operator.contains` ([#9192](https://github.com/astral-sh/ruff/pull/9192))
|
||||
- Iterate over lambdas in deferred type annotations ([#9175](https://github.com/astral-sh/ruff/pull/9175))
|
||||
- Fix panic in `D208` with multibyte indent ([#9147](https://github.com/astral-sh/ruff/pull/9147))
|
||||
- Add support for `NoReturn` in auto-return-typing ([#9206](https://github.com/astral-sh/ruff/pull/9206))
|
||||
- Allow removal of `typing` from `exempt-modules` ([#9214](https://github.com/astral-sh/ruff/pull/9214))
|
||||
- Avoid `mutable-class-default` violations for Pydantic subclasses ([#9187](https://github.com/astral-sh/ruff/pull/9187))
|
||||
- Fix dropped union expressions for piped non-types in `PYI055` autofix ([#9161](https://github.com/astral-sh/ruff/pull/9161))
|
||||
- Enable annotation quoting for multi-line expressions ([#9142](https://github.com/astral-sh/ruff/pull/9142))
|
||||
- Deduplicate edits when quoting annotations ([#9140](https://github.com/astral-sh/ruff/pull/9140))
|
||||
- Prevent invalid utf8 indexing in cell magic detection ([#9146](https://github.com/astral-sh/ruff/pull/9146))
|
||||
- Avoid nested quotations in auto-quoting fix ([#9168](https://github.com/astral-sh/ruff/pull/9168))
|
||||
- Add base-class inheritance detection to flake8-django rules ([#9151](https://github.com/astral-sh/ruff/pull/9151))
|
||||
- Avoid `asyncio-dangling-task` violations on shadowed bindings ([#9215](https://github.com/astral-sh/ruff/pull/9215))
|
||||
|
||||
### Documentation
|
||||
|
||||
- Fix blog post URL in changelog ([#9119](https://github.com/astral-sh/ruff/pull/9119))
|
||||
- Add error suppression hint for multi-line strings ([#9205](https://github.com/astral-sh/ruff/pull/9205))
|
||||
- Fix typo in SemanticModel.parent_expression docstring ([#9167](https://github.com/astral-sh/ruff/pull/9167))
|
||||
- Document link between import sorting and formatter ([#9117](https://github.com/astral-sh/ruff/pull/9117))
|
||||
|
||||
## 0.1.8
|
||||
|
||||
This release includes opt-in support for formatting Python snippets within
|
||||
|
||||
@@ -326,16 +326,18 @@ We use an experimental in-house tool for managing releases.
|
||||
- Often labels will be missing from pull requests they will need to be manually organized into the proper section
|
||||
- Changes should be edited to be user-facing descriptions, avoiding internal details
|
||||
1. Highlight any breaking changes in `BREAKING_CHANGES.md`
|
||||
1. Run `cargo check`. This should update the lock file with new versions.
|
||||
1. Create a pull request with the changelog and version updates
|
||||
1. Merge the PR
|
||||
1. Run the release workflow with the version number (without starting `v`) as input. Make sure
|
||||
main has your merged PR as last commit
|
||||
1. Run the [release workflow](https://github.com/astral-sh/ruff/actions/workflows/release.yaml) with:
|
||||
- The new version number (without starting `v`)
|
||||
- The commit hash of the merged release pull request on `main`
|
||||
1. The release workflow will do the following:
|
||||
1. Build all the assets. If this fails (even though we tested in step 4), we haven't tagged or
|
||||
uploaded anything, you can restart after pushing a fix.
|
||||
1. Upload to PyPI.
|
||||
1. Create and push the Git tag (as extracted from `pyproject.toml`). We create the Git tag only
|
||||
after building the wheels and uploading to PyPI, since we can't delete or modify the tag ([#4468](https://github.com/charliermarsh/ruff/issues/4468)).
|
||||
after building the wheels and uploading to PyPI, since we can't delete or modify the tag ([#4468](https://github.com/astral-sh/ruff/issues/4468)).
|
||||
1. Attach artifacts to draft GitHub release
|
||||
1. Trigger downstream repositories. This can fail non-catastrophically, as we can run any
|
||||
downstream jobs manually if needed.
|
||||
@@ -344,7 +346,10 @@ We use an experimental in-house tool for managing releases.
|
||||
1. Copy the changelog for the release into the GitHub release
|
||||
- See previous releases for formatting of section headers
|
||||
1. Generate the contributor list with `rooster contributors` and add to the release notes
|
||||
1. If needed, [update the schemastore](https://github.com/charliermarsh/ruff/blob/main/scripts/update_schemastore.py)
|
||||
1. If needed, [update the schemastore](https://github.com/astral-sh/ruff/blob/main/scripts/update_schemastore.py).
|
||||
1. One can determine if an update is needed when
|
||||
`git diff old-version-tag new-version-tag -- ruff.schema.json` returns a non-empty diff.
|
||||
1. Once run successfully, you should follow the link in the output to create a PR.
|
||||
1. If needed, update the `ruff-lsp` and `ruff-vscode` repositories.
|
||||
|
||||
## Ecosystem CI
|
||||
|
||||
88
Cargo.lock
generated
88
Cargo.lock
generated
@@ -123,9 +123,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.75"
|
||||
version = "1.0.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||
checksum = "59d2a3357dde987206219e78ecfbbb6e8dad06cbb65292758d3270e6254f7355"
|
||||
|
||||
[[package]]
|
||||
name = "argfile"
|
||||
@@ -234,9 +234,9 @@ checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
|
||||
|
||||
[[package]]
|
||||
name = "cachedir"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e236bf5873ea57ec2877445297f4da008916bfae51567131acfc54a073d694f3"
|
||||
checksum = "4703f3937077db8fa35bee3c8789343c1aec2585f0146f09d658d4ccc0e8d873"
|
||||
dependencies = [
|
||||
"tempfile",
|
||||
]
|
||||
@@ -434,11 +434,10 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "2.0.4"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6"
|
||||
checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8"
|
||||
dependencies = [
|
||||
"is-terminal",
|
||||
"lazy_static",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
@@ -736,9 +735,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.10.0"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
|
||||
checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece"
|
||||
dependencies = [
|
||||
"humantime",
|
||||
"is-terminal",
|
||||
@@ -809,7 +808,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.1.8"
|
||||
version = "0.1.9"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -818,7 +817,7 @@ dependencies = [
|
||||
"itertools 0.11.0",
|
||||
"log",
|
||||
"once_cell",
|
||||
"pep440_rs",
|
||||
"pep440_rs 0.4.0",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"ruff_linter",
|
||||
@@ -998,17 +997,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ignore"
|
||||
version = "0.4.20"
|
||||
version = "0.4.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492"
|
||||
checksum = "747ad1b4ae841a78e8aba0d63adbfbeaea26b517b63705d47856b73015d27060"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"globset",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"memchr",
|
||||
"regex",
|
||||
"regex-automata 0.4.3",
|
||||
"same-file",
|
||||
"thread_local",
|
||||
"walkdir",
|
||||
"winapi-util",
|
||||
]
|
||||
@@ -1605,6 +1603,18 @@ dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pep440_rs"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0c29f9c43de378b4e4e0cd7dbcce0e5cfb80443de8c05620368b2948bc936a1"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"regex",
|
||||
"serde",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pep508_rs"
|
||||
version = "0.2.1"
|
||||
@@ -1612,7 +1622,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0713d7bb861ca2b7d4c50a38e1f31a4b63a2e2df35ef1e5855cc29e108453e2"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"pep440_rs",
|
||||
"pep440_rs 0.3.12",
|
||||
"regex",
|
||||
"serde",
|
||||
"thiserror",
|
||||
@@ -1794,9 +1804,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.70"
|
||||
version = "1.0.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
|
||||
checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -1808,7 +1818,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46d4a5e69187f23a29f8aa0ea57491d104ba541bc55f76552c2a74962aa20e04"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"pep440_rs",
|
||||
"pep440_rs 0.3.12",
|
||||
"pep508_rs",
|
||||
"serde",
|
||||
"toml",
|
||||
@@ -2063,7 +2073,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_cli"
|
||||
version = "0.1.8"
|
||||
version = "0.1.9"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.2",
|
||||
"anyhow",
|
||||
@@ -2199,7 +2209,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_linter"
|
||||
version = "0.1.8"
|
||||
version = "0.1.9"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"annotate-snippets 0.9.2",
|
||||
@@ -2222,7 +2232,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"path-absolutize",
|
||||
"pathdiff",
|
||||
"pep440_rs",
|
||||
"pep440_rs 0.4.0",
|
||||
"pretty_assertions",
|
||||
"pyproject-toml",
|
||||
"quick-junit",
|
||||
@@ -2452,7 +2462,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_shrinking"
|
||||
version = "0.1.8"
|
||||
version = "0.1.9"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -2528,7 +2538,7 @@ dependencies = [
|
||||
"log",
|
||||
"once_cell",
|
||||
"path-absolutize",
|
||||
"pep440_rs",
|
||||
"pep440_rs 0.4.0",
|
||||
"regex",
|
||||
"ruff_cache",
|
||||
"ruff_formatter",
|
||||
@@ -2731,9 +2741,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.3"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186"
|
||||
checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -2958,9 +2968,9 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76"
|
||||
|
||||
[[package]]
|
||||
name = "test-case"
|
||||
version = "3.2.1"
|
||||
version = "3.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8f1e820b7f1d95a0cdbf97a5df9de10e1be731983ab943e56703ac1b8e9d425"
|
||||
checksum = "eb2550dd13afcd286853192af8601920d959b14c401fcece38071d53bf0768a8"
|
||||
dependencies = [
|
||||
"test-case-macros",
|
||||
]
|
||||
@@ -2993,18 +3003,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.50"
|
||||
version = "1.0.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
|
||||
checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.50"
|
||||
version = "1.0.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
|
||||
checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3093,9 +3103,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.2"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d"
|
||||
checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
@@ -3105,18 +3115,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.3"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
|
||||
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.20.2"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338"
|
||||
checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
|
||||
14
Cargo.toml
14
Cargo.toml
@@ -12,15 +12,15 @@ authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||
license = "MIT"
|
||||
|
||||
[workspace.dependencies]
|
||||
anyhow = { version = "1.0.69" }
|
||||
anyhow = { version = "1.0.76" }
|
||||
bitflags = { version = "2.4.1" }
|
||||
chrono = { version = "0.4.31", default-features = false, features = ["clock"] }
|
||||
clap = { version = "4.4.7", features = ["derive"] }
|
||||
colored = { version = "2.0.0" }
|
||||
colored = { version = "2.1.0" }
|
||||
filetime = { version = "0.2.23" }
|
||||
glob = { version = "0.3.1" }
|
||||
globset = { version = "0.4.14" }
|
||||
ignore = { version = "0.4.20" }
|
||||
ignore = { version = "0.4.21" }
|
||||
insta = { version = "1.34.0", feature = ["filters", "glob"] }
|
||||
is-macro = { version = "0.3.1" }
|
||||
itertools = { version = "0.11.0" }
|
||||
@@ -29,7 +29,7 @@ log = { version = "0.4.17" }
|
||||
memchr = { version = "2.6.4" }
|
||||
once_cell = { version = "1.19.0" }
|
||||
path-absolutize = { version = "3.1.1" }
|
||||
proc-macro2 = { version = "1.0.70" }
|
||||
proc-macro2 = { version = "1.0.71" }
|
||||
quote = { version = "1.0.23" }
|
||||
regex = { version = "1.10.2" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
@@ -43,9 +43,9 @@ static_assertions = "1.1.0"
|
||||
strum = { version = "0.25.0", features = ["strum_macros"] }
|
||||
strum_macros = { version = "0.25.3" }
|
||||
syn = { version = "2.0.40" }
|
||||
test-case = { version = "3.2.1" }
|
||||
thiserror = { version = "1.0.50" }
|
||||
toml = { version = "0.8.2" }
|
||||
test-case = { version = "3.3.1" }
|
||||
thiserror = { version = "1.0.51" }
|
||||
toml = { version = "0.8.8" }
|
||||
tracing = { version = "0.1.40" }
|
||||
tracing-indicatif = { version = "0.3.6" }
|
||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
|
||||
@@ -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.8
|
||||
rev: v0.1.9
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.1.8"
|
||||
version = "0.1.9"
|
||||
description = """
|
||||
Convert Flake8 configuration files to Ruff configuration files.
|
||||
"""
|
||||
@@ -23,7 +23,7 @@ configparser = { version = "3.0.3" }
|
||||
itertools = { workspace = true }
|
||||
log = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
pep440_rs = { version = "0.3.12", features = ["serde"] }
|
||||
pep440_rs = { version = "0.4.0", features = ["serde"] }
|
||||
regex = { workspace = true }
|
||||
rustc-hash = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
|
||||
@@ -2,7 +2,7 @@ use ruff_benchmark::criterion::{
|
||||
criterion_group, criterion_main, BenchmarkGroup, BenchmarkId, Criterion, Throughput,
|
||||
};
|
||||
use ruff_benchmark::{TestCase, TestFile, TestFileDownloadError};
|
||||
use ruff_linter::linter::lint_only;
|
||||
use ruff_linter::linter::{lint_only, ParseSource};
|
||||
use ruff_linter::rule_selector::PreviewOptions;
|
||||
use ruff_linter::settings::rule_table::RuleTable;
|
||||
use ruff_linter::settings::types::PreviewMode;
|
||||
@@ -10,6 +10,7 @@ use ruff_linter::settings::{flags, LinterSettings};
|
||||
use ruff_linter::source_kind::SourceKind;
|
||||
use ruff_linter::{registry::Rule, RuleSelector};
|
||||
use ruff_python_ast::PySourceType;
|
||||
use ruff_python_parser::{lexer, parse_program_tokens, Mode};
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[global_allocator]
|
||||
@@ -53,7 +54,13 @@ fn benchmark_linter(mut group: BenchmarkGroup, settings: &LinterSettings) {
|
||||
BenchmarkId::from_parameter(case.name()),
|
||||
&case,
|
||||
|b, case| {
|
||||
let kind = SourceKind::Python(case.code().to_string());
|
||||
// Tokenize the source.
|
||||
let tokens = lexer::lex(case.code(), Mode::Module).collect::<Vec<_>>();
|
||||
|
||||
// Parse the source.
|
||||
let ast =
|
||||
parse_program_tokens(tokens.clone(), case.code(), case.name(), false).unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
let path = case.path();
|
||||
let result = lint_only(
|
||||
@@ -61,8 +68,12 @@ fn benchmark_linter(mut group: BenchmarkGroup, settings: &LinterSettings) {
|
||||
None,
|
||||
settings,
|
||||
flags::Noqa::Enabled,
|
||||
&kind,
|
||||
&SourceKind::Python(case.code().to_string()),
|
||||
PySourceType::from(path.as_path()),
|
||||
ParseSource::Precomputed {
|
||||
tokens: &tokens,
|
||||
ast: &ast,
|
||||
},
|
||||
);
|
||||
|
||||
// Assert that file contains no parse errors
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_cli"
|
||||
version = "0.1.8"
|
||||
version = "0.1.9"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
@@ -33,7 +33,7 @@ anyhow = { workspace = true }
|
||||
argfile = { version = "0.1.6" }
|
||||
bincode = { version = "1.3.3" }
|
||||
bitflags = { workspace = true }
|
||||
cachedir = { version = "0.3.0" }
|
||||
cachedir = { version = "0.3.1" }
|
||||
chrono = { workspace = true }
|
||||
clap = { workspace = true, features = ["derive", "env"] }
|
||||
clap_complete_command = { version = "0.5.1" }
|
||||
|
||||
@@ -12,7 +12,7 @@ use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::cache::{Cache, FileCacheKey, LintCacheData};
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_linter::linter::{lint_fix, lint_only, FixTable, FixerResult, LinterResult};
|
||||
use ruff_linter::linter::{lint_fix, lint_only, FixTable, FixerResult, LinterResult, ParseSource};
|
||||
use ruff_linter::logging::DisplayParseError;
|
||||
use ruff_linter::message::Message;
|
||||
use ruff_linter::pyproject_toml::lint_pyproject_toml;
|
||||
@@ -303,12 +303,28 @@ pub(crate) fn lint_path(
|
||||
(result, fixed)
|
||||
} else {
|
||||
// If we fail to fix, lint the original source code.
|
||||
let result = lint_only(path, package, settings, noqa, &source_kind, source_type);
|
||||
let result = lint_only(
|
||||
path,
|
||||
package,
|
||||
settings,
|
||||
noqa,
|
||||
&source_kind,
|
||||
source_type,
|
||||
ParseSource::None,
|
||||
);
|
||||
let fixed = FxHashMap::default();
|
||||
(result, fixed)
|
||||
}
|
||||
} else {
|
||||
let result = lint_only(path, package, settings, noqa, &source_kind, source_type);
|
||||
let result = lint_only(
|
||||
path,
|
||||
package,
|
||||
settings,
|
||||
noqa,
|
||||
&source_kind,
|
||||
source_type,
|
||||
ParseSource::None,
|
||||
);
|
||||
let fixed = FxHashMap::default();
|
||||
(result, fixed)
|
||||
};
|
||||
@@ -444,6 +460,7 @@ pub(crate) fn lint_stdin(
|
||||
noqa,
|
||||
&source_kind,
|
||||
source_type,
|
||||
ParseSource::None,
|
||||
);
|
||||
let fixed = FxHashMap::default();
|
||||
|
||||
@@ -462,6 +479,7 @@ pub(crate) fn lint_stdin(
|
||||
noqa,
|
||||
&source_kind,
|
||||
source_type,
|
||||
ParseSource::None,
|
||||
);
|
||||
let fixed = FxHashMap::default();
|
||||
(result, fixed)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_linter"
|
||||
version = "0.1.8"
|
||||
version = "0.1.9"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
@@ -52,7 +52,7 @@ path-absolutize = { workspace = true, features = [
|
||||
"use_unix_paths_on_wasm",
|
||||
] }
|
||||
pathdiff = { version = "0.2.1" }
|
||||
pep440_rs = { version = "0.3.12", features = ["serde"] }
|
||||
pep440_rs = { version = "0.4.0", features = ["serde"] }
|
||||
pyproject-toml = { version = "0.8.1" }
|
||||
quick-junit = { version = "0.3.5" }
|
||||
regex = { workspace = true }
|
||||
|
||||
@@ -212,3 +212,20 @@ def func(x: int):
|
||||
raise ValueError
|
||||
else:
|
||||
return 1
|
||||
|
||||
|
||||
from typing import overload
|
||||
|
||||
|
||||
@overload
|
||||
def overloaded(i: int) -> "int":
|
||||
...
|
||||
|
||||
|
||||
@overload
|
||||
def overloaded(i: "str") -> "str":
|
||||
...
|
||||
|
||||
|
||||
def overloaded(i):
|
||||
return i
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# Errors
|
||||
"yoda" == compare # SIM300
|
||||
"yoda" == compare # SIM300
|
||||
42 == age # SIM300
|
||||
("a", "b") == compare # SIM300
|
||||
"yoda" <= compare # SIM300
|
||||
@@ -13,10 +12,17 @@ YODA > age # SIM300
|
||||
YODA >= age # SIM300
|
||||
JediOrder.YODA == age # SIM300
|
||||
0 < (number - 100) # SIM300
|
||||
SomeClass().settings.SOME_CONSTANT_VALUE > (60 * 60) # SIM300
|
||||
B<A[0][0]or B
|
||||
B or(B)<A[0][0]
|
||||
|
||||
# Errors in preview
|
||||
['upper'] == UPPER_LIST
|
||||
{} == DummyHandler.CONFIG
|
||||
|
||||
# Errors in stable
|
||||
UPPER_LIST == ['upper']
|
||||
DummyHandler.CONFIG == {}
|
||||
|
||||
# OK
|
||||
compare == "yoda"
|
||||
age == 42
|
||||
@@ -31,3 +37,6 @@ age <= YODA
|
||||
YODA == YODA
|
||||
age == JediOrder.YODA
|
||||
(number - 100) > 0
|
||||
SECONDS_IN_DAY == 60 * 60 * 24 # Error in 0.1.8
|
||||
SomeClass().settings.SOME_CONSTANT_VALUE > (60 * 60) # Error in 0.1.8
|
||||
{"non-empty-dict": "is-ok"} == DummyHandler.CONFIG
|
||||
|
||||
9
crates/ruff_linter/resources/test/fixtures/pycodestyle/E402_1.py
vendored
Normal file
9
crates/ruff_linter/resources/test/fixtures/pycodestyle/E402_1.py
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import a
|
||||
|
||||
"""Some other docstring."""
|
||||
|
||||
import b
|
||||
|
||||
"""Some other docstring."""
|
||||
|
||||
import c
|
||||
5
crates/ruff_linter/resources/test/fixtures/pyflakes/F404_1.py
vendored
Normal file
5
crates/ruff_linter/resources/test/fixtures/pyflakes/F404_1.py
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
"""Docstring"""
|
||||
|
||||
"""Non-docstring"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
8
crates/ruff_linter/resources/test/fixtures/pyflakes/F821_24.py
vendored
Normal file
8
crates/ruff_linter/resources/test/fixtures/pyflakes/F821_24.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
"""Test for accessing class members within a generator."""
|
||||
|
||||
|
||||
class Class:
|
||||
items = []
|
||||
|
||||
if len(replacements := {item[1] for item in items}) > 1:
|
||||
pass
|
||||
@@ -1,30 +1,28 @@
|
||||
# These should change
|
||||
x = u"Hello"
|
||||
u"Hello"
|
||||
|
||||
u'world'
|
||||
x = u"Hello" # UP025
|
||||
|
||||
print(u"Hello")
|
||||
u'world' # UP025
|
||||
|
||||
print(u'world')
|
||||
print(u"Hello") # UP025
|
||||
|
||||
print(u'world') # UP025
|
||||
|
||||
import foo
|
||||
|
||||
foo(u"Hello", U"world", a=u"Hello", b=u"world")
|
||||
foo(u"Hello", U"world", a=u"Hello", b=u"world") # UP025
|
||||
|
||||
# These should stay quoted they way they are
|
||||
# Retain quotes when fixing.
|
||||
x = u'hello' # UP025
|
||||
x = u"""hello""" # UP025
|
||||
x = u'''hello''' # UP025
|
||||
x = u'Hello "World"' # UP025
|
||||
|
||||
x = u'hello'
|
||||
x = u"""hello"""
|
||||
x = u'''hello'''
|
||||
x = u'Hello "World"'
|
||||
|
||||
# These should not change
|
||||
u = "Hello"
|
||||
|
||||
u = u
|
||||
u = "Hello" # OK
|
||||
u = u # OK
|
||||
|
||||
def hello():
|
||||
return"Hello"
|
||||
return"Hello" # OK
|
||||
|
||||
f"foo"u"bar"
|
||||
f"foo" u"bar"
|
||||
f"foo"u"bar" # OK
|
||||
f"foo" u"bar" # OK
|
||||
|
||||
@@ -243,3 +243,12 @@ raise ValueError(
|
||||
).format(a, b)
|
||||
|
||||
("{}" "{{{}}}").format(a, b)
|
||||
|
||||
# The dictionary should be parenthesized.
|
||||
"{}".format({0: 1}[0])
|
||||
|
||||
# The dictionary should be parenthesized.
|
||||
"{}".format({0: 1}.bar)
|
||||
|
||||
# The dictionary should be parenthesized.
|
||||
"{}".format({0: 1}())
|
||||
|
||||
@@ -152,3 +152,13 @@ async def f(x: bool):
|
||||
t = asyncio.create_task(asyncio.sleep(1))
|
||||
else:
|
||||
t = None
|
||||
|
||||
|
||||
# OK
|
||||
async def f(x: bool):
|
||||
global T
|
||||
|
||||
if x:
|
||||
T = asyncio.create_task(asyncio.sleep(1))
|
||||
else:
|
||||
T = None
|
||||
|
||||
8
crates/ruff_linter/resources/test/fixtures/ruff/RUF020.py
vendored
Normal file
8
crates/ruff_linter/resources/test/fixtures/ruff/RUF020.py
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
from typing import Never, NoReturn, Union
|
||||
|
||||
Union[Never, int]
|
||||
Union[NoReturn, int]
|
||||
Never | int
|
||||
NoReturn | int
|
||||
Union[Union[Never, int], Union[NoReturn, int]]
|
||||
Union[NoReturn, int, float]
|
||||
@@ -81,6 +81,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
Rule::DuplicateUnionMember,
|
||||
Rule::RedundantLiteralUnion,
|
||||
Rule::UnnecessaryTypeUnion,
|
||||
Rule::NeverUnion,
|
||||
]) {
|
||||
// Avoid duplicate checks if the parent is a union, since these rules already
|
||||
// traverse nested unions.
|
||||
@@ -100,6 +101,10 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
|
||||
if checker.enabled(Rule::NeverUnion) {
|
||||
ruff::rules::never_union(checker, expr);
|
||||
}
|
||||
|
||||
if checker.any_enabled(&[
|
||||
Rule::SysVersionSlice3,
|
||||
Rule::SysVersion2,
|
||||
@@ -1154,6 +1159,10 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
|
||||
if checker.enabled(Rule::NeverUnion) {
|
||||
ruff::rules::never_union(checker, expr);
|
||||
}
|
||||
|
||||
// Avoid duplicate checks if the parent is a union, since these rules already
|
||||
// traverse nested unions.
|
||||
if !checker.semantic.in_nested_union() {
|
||||
|
||||
@@ -287,7 +287,18 @@ where
|
||||
|
||||
// Track whether we've seen docstrings, non-imports, etc.
|
||||
match stmt {
|
||||
Stmt::Expr(ast::StmtExpr { value, .. })
|
||||
if !self
|
||||
.semantic
|
||||
.flags
|
||||
.intersects(SemanticModelFlags::MODULE_DOCSTRING)
|
||||
&& value.is_string_literal_expr() =>
|
||||
{
|
||||
self.semantic.flags |= SemanticModelFlags::MODULE_DOCSTRING;
|
||||
}
|
||||
Stmt::ImportFrom(ast::StmtImportFrom { module, names, .. }) => {
|
||||
self.semantic.flags |= SemanticModelFlags::MODULE_DOCSTRING;
|
||||
|
||||
// Allow __future__ imports until we see a non-__future__ import.
|
||||
if let Some("__future__") = module.as_deref() {
|
||||
if names
|
||||
@@ -301,9 +312,11 @@ where
|
||||
}
|
||||
}
|
||||
Stmt::Import(_) => {
|
||||
self.semantic.flags |= SemanticModelFlags::MODULE_DOCSTRING;
|
||||
self.semantic.flags |= SemanticModelFlags::FUTURES_BOUNDARY;
|
||||
}
|
||||
_ => {
|
||||
self.semantic.flags |= SemanticModelFlags::MODULE_DOCSTRING;
|
||||
self.semantic.flags |= SemanticModelFlags::FUTURES_BOUNDARY;
|
||||
if !(self.semantic.seen_import_boundary()
|
||||
|| helpers::is_assignment_to_a_dunder(stmt)
|
||||
@@ -1435,11 +1448,8 @@ where
|
||||
|
||||
impl<'a> Checker<'a> {
|
||||
/// Visit a [`Module`]. Returns `true` if the module contains a module-level docstring.
|
||||
fn visit_module(&mut self, python_ast: &'a Suite) -> bool {
|
||||
fn visit_module(&mut self, python_ast: &'a Suite) {
|
||||
analyze::module(python_ast, self);
|
||||
|
||||
let docstring = docstrings::extraction::docstring_from(python_ast);
|
||||
docstring.is_some()
|
||||
}
|
||||
|
||||
/// Visit a list of [`Comprehension`] nodes, assumed to be the comprehensions that compose a
|
||||
@@ -1745,10 +1755,13 @@ impl<'a> Checker<'a> {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the expression is the left-hand side of a walrus operator, then it's a named
|
||||
// expression assignment.
|
||||
if self
|
||||
.semantic
|
||||
.current_expressions()
|
||||
.any(Expr::is_named_expr_expr)
|
||||
.filter_map(Expr::as_named_expr_expr)
|
||||
.any(|parent| parent.target.as_ref() == expr)
|
||||
{
|
||||
self.add_binding(id, expr.range(), BindingKind::NamedExprAssignment, flags);
|
||||
return;
|
||||
@@ -2003,14 +2016,8 @@ pub(crate) fn check_ast(
|
||||
);
|
||||
checker.bind_builtins();
|
||||
|
||||
// Check for module docstring.
|
||||
let python_ast = if checker.visit_module(python_ast) {
|
||||
&python_ast[1..]
|
||||
} else {
|
||||
python_ast
|
||||
};
|
||||
|
||||
// Iterate over the AST.
|
||||
checker.visit_module(python_ast);
|
||||
checker.visit_body(python_ast);
|
||||
|
||||
// Visit any deferred syntax nodes. Take care to visit in order, such that we avoid adding
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use ruff_diagnostics::{Diagnostic, DiagnosticKind};
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_python_codegen::Stylist;
|
||||
use ruff_python_parser::lexer::LexResult;
|
||||
use ruff_python_parser::TokenKind;
|
||||
@@ -97,7 +97,7 @@ pub(crate) fn check_logical_lines(
|
||||
indent_size,
|
||||
) {
|
||||
if settings.rules.enabled(kind.rule()) {
|
||||
context.push(kind, range);
|
||||
context.push_diagnostic(Diagnostic::new(kind, range));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,18 +123,6 @@ impl<'a> LogicalLinesContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn push<K: Into<DiagnosticKind>>(&mut self, kind: K, range: TextRange) {
|
||||
let kind = kind.into();
|
||||
if self.settings.rules.enabled(kind.rule()) {
|
||||
self.diagnostics.push(Diagnostic {
|
||||
kind,
|
||||
range,
|
||||
fix: None,
|
||||
parent: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn push_diagnostic(&mut self, diagnostic: Diagnostic) {
|
||||
if self.settings.rules.enabled(diagnostic.kind.rule()) {
|
||||
self.diagnostics.push(diagnostic);
|
||||
|
||||
@@ -901,6 +901,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Ruff, "017") => (RuleGroup::Nursery, rules::ruff::rules::QuadraticListSummation),
|
||||
(Ruff, "018") => (RuleGroup::Preview, rules::ruff::rules::AssignmentInAssert),
|
||||
(Ruff, "019") => (RuleGroup::Preview, rules::ruff::rules::UnnecessaryKeyCheck),
|
||||
(Ruff, "020") => (RuleGroup::Preview, rules::ruff::rules::NeverUnion),
|
||||
(Ruff, "100") => (RuleGroup::Stable, rules::ruff::rules::UnusedNOQA),
|
||||
(Ruff, "200") => (RuleGroup::Stable, rules::ruff::rules::InvalidPyprojectToml),
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ use rustc_hash::FxHashMap;
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_notebook::Notebook;
|
||||
use ruff_python_ast::imports::ImportMap;
|
||||
use ruff_python_ast::PySourceType;
|
||||
use ruff_python_ast::{PySourceType, Suite};
|
||||
use ruff_python_codegen::Stylist;
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_python_parser::lexer::LexResult;
|
||||
@@ -73,7 +73,6 @@ pub struct FixerResult<'a> {
|
||||
pub fn check_path(
|
||||
path: &Path,
|
||||
package: Option<&Path>,
|
||||
tokens: Vec<LexResult>,
|
||||
locator: &Locator,
|
||||
stylist: &Stylist,
|
||||
indexer: &Indexer,
|
||||
@@ -82,6 +81,7 @@ pub fn check_path(
|
||||
noqa: flags::Noqa,
|
||||
source_kind: &SourceKind,
|
||||
source_type: PySourceType,
|
||||
tokens: TokenSource,
|
||||
) -> LinterResult<(Vec<Diagnostic>, Option<ImportMap>)> {
|
||||
// Aggregate all diagnostics.
|
||||
let mut diagnostics = vec![];
|
||||
@@ -144,12 +144,8 @@ pub fn check_path(
|
||||
.iter_enabled()
|
||||
.any(|rule_code| rule_code.lint_source().is_imports());
|
||||
if use_ast || use_imports || use_doc_lines {
|
||||
match ruff_python_parser::parse_program_tokens(
|
||||
tokens,
|
||||
source_kind.source_code(),
|
||||
&path.to_string_lossy(),
|
||||
source_type.is_ipynb(),
|
||||
) {
|
||||
// Parse, if the AST wasn't pre-provided provided.
|
||||
match tokens.into_ast_source(source_kind, source_type, path) {
|
||||
Ok(python_ast) => {
|
||||
let cell_offsets = source_kind.as_ipy_notebook().map(Notebook::cell_offsets);
|
||||
if use_ast {
|
||||
@@ -325,7 +321,6 @@ pub fn add_noqa_to_path(
|
||||
} = check_path(
|
||||
path,
|
||||
package,
|
||||
tokens,
|
||||
&locator,
|
||||
&stylist,
|
||||
&indexer,
|
||||
@@ -334,6 +329,7 @@ pub fn add_noqa_to_path(
|
||||
flags::Noqa::Disabled,
|
||||
source_kind,
|
||||
source_type,
|
||||
TokenSource::Tokens(tokens),
|
||||
);
|
||||
|
||||
// Log any parse errors.
|
||||
@@ -365,10 +361,10 @@ pub fn lint_only(
|
||||
noqa: flags::Noqa,
|
||||
source_kind: &SourceKind,
|
||||
source_type: PySourceType,
|
||||
data: ParseSource,
|
||||
) -> LinterResult<(Vec<Message>, Option<ImportMap>)> {
|
||||
// Tokenize once.
|
||||
let tokens: Vec<LexResult> =
|
||||
ruff_python_parser::tokenize(source_kind.source_code(), source_type.as_mode());
|
||||
let tokens = data.into_token_source(source_kind, source_type);
|
||||
|
||||
// Map row and column locations to byte slices (lazily).
|
||||
let locator = Locator::new(source_kind.source_code());
|
||||
@@ -391,7 +387,6 @@ pub fn lint_only(
|
||||
let result = check_path(
|
||||
path,
|
||||
package,
|
||||
tokens,
|
||||
&locator,
|
||||
&stylist,
|
||||
&indexer,
|
||||
@@ -400,6 +395,7 @@ pub fn lint_only(
|
||||
noqa,
|
||||
source_kind,
|
||||
source_type,
|
||||
tokens,
|
||||
);
|
||||
|
||||
result.map(|(diagnostics, imports)| {
|
||||
@@ -487,7 +483,6 @@ pub fn lint_fix<'a>(
|
||||
let result = check_path(
|
||||
path,
|
||||
package,
|
||||
tokens,
|
||||
&locator,
|
||||
&stylist,
|
||||
&indexer,
|
||||
@@ -496,6 +491,7 @@ pub fn lint_fix<'a>(
|
||||
noqa,
|
||||
&transformed,
|
||||
source_type,
|
||||
TokenSource::Tokens(tokens),
|
||||
);
|
||||
|
||||
if iterations == 0 {
|
||||
@@ -632,6 +628,95 @@ This indicates a bug in Ruff. If you could open an issue at:
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ParseSource<'a> {
|
||||
/// Extract the tokens and AST from the given source code.
|
||||
None,
|
||||
/// Use the precomputed tokens and AST.
|
||||
Precomputed {
|
||||
tokens: &'a [LexResult],
|
||||
ast: &'a Suite,
|
||||
},
|
||||
}
|
||||
|
||||
impl<'a> ParseSource<'a> {
|
||||
/// Convert to a [`TokenSource`], tokenizing if necessary.
|
||||
fn into_token_source(
|
||||
self,
|
||||
source_kind: &SourceKind,
|
||||
source_type: PySourceType,
|
||||
) -> TokenSource<'a> {
|
||||
match self {
|
||||
Self::None => TokenSource::Tokens(ruff_python_parser::tokenize(
|
||||
source_kind.source_code(),
|
||||
source_type.as_mode(),
|
||||
)),
|
||||
Self::Precomputed { tokens, ast } => TokenSource::Precomputed { tokens, ast },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum TokenSource<'a> {
|
||||
/// Use the precomputed tokens to generate the AST.
|
||||
Tokens(Vec<LexResult>),
|
||||
/// Use the precomputed tokens and AST.
|
||||
Precomputed {
|
||||
tokens: &'a [LexResult],
|
||||
ast: &'a Suite,
|
||||
},
|
||||
}
|
||||
|
||||
impl Deref for TokenSource<'_> {
|
||||
type Target = [LexResult];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
Self::Tokens(tokens) => tokens,
|
||||
Self::Precomputed { tokens, .. } => tokens,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TokenSource<'a> {
|
||||
/// Convert to an [`AstSource`], parsing if necessary.
|
||||
fn into_ast_source(
|
||||
self,
|
||||
source_kind: &SourceKind,
|
||||
source_type: PySourceType,
|
||||
path: &Path,
|
||||
) -> Result<AstSource<'a>, ParseError> {
|
||||
match self {
|
||||
Self::Tokens(tokens) => Ok(AstSource::Ast(ruff_python_parser::parse_program_tokens(
|
||||
tokens,
|
||||
source_kind.source_code(),
|
||||
&path.to_string_lossy(),
|
||||
source_type.is_ipynb(),
|
||||
)?)),
|
||||
Self::Precomputed { ast, .. } => Ok(AstSource::Precomputed(ast)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum AstSource<'a> {
|
||||
/// Extract the AST from the given source code.
|
||||
Ast(Suite),
|
||||
/// Use the precomputed AST.
|
||||
Precomputed(&'a Suite),
|
||||
}
|
||||
|
||||
impl Deref for AstSource<'_> {
|
||||
type Target = Suite;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
Self::Ast(ast) => ast,
|
||||
Self::Precomputed(ast) => ast,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::Path;
|
||||
|
||||
@@ -243,6 +243,17 @@ pub struct PreviewOptions {
|
||||
pub require_explicit: bool,
|
||||
}
|
||||
|
||||
impl PreviewOptions {
|
||||
/// Return a copy with the same preview mode setting but require explicit disabled.
|
||||
#[must_use]
|
||||
pub fn without_require_explicit(&self) -> Self {
|
||||
Self {
|
||||
mode: self.mode,
|
||||
require_explicit: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "schemars")]
|
||||
mod schema {
|
||||
use itertools::Itertools;
|
||||
|
||||
@@ -482,7 +482,6 @@ impl Violation for AnyType {
|
||||
format!("Dynamically typed expressions (typing.Any) are disallowed in `{name}`")
|
||||
}
|
||||
}
|
||||
|
||||
fn is_none_returning(body: &[Stmt]) -> bool {
|
||||
let mut visitor = ReturnStatementVisitor::default();
|
||||
visitor.visit_body(body);
|
||||
@@ -537,17 +536,41 @@ fn check_dynamically_typed<F>(
|
||||
}
|
||||
}
|
||||
|
||||
fn is_empty_body(body: &[Stmt]) -> bool {
|
||||
body.iter().all(|stmt| match stmt {
|
||||
Stmt::Pass(_) => true,
|
||||
Stmt::Expr(ast::StmtExpr { value, range: _ }) => {
|
||||
matches!(
|
||||
value.as_ref(),
|
||||
Expr::StringLiteral(_) | Expr::EllipsisLiteral(_)
|
||||
)
|
||||
/// Return `true` if a function appears to be a stub.
|
||||
fn is_stub_function(function_def: &ast::StmtFunctionDef, checker: &Checker) -> bool {
|
||||
/// Returns `true` if a function has an empty body.
|
||||
fn is_empty_body(function_def: &ast::StmtFunctionDef) -> bool {
|
||||
function_def.body.iter().all(|stmt| match stmt {
|
||||
Stmt::Pass(_) => true,
|
||||
Stmt::Expr(ast::StmtExpr { value, range: _ }) => {
|
||||
matches!(
|
||||
value.as_ref(),
|
||||
Expr::StringLiteral(_) | Expr::EllipsisLiteral(_)
|
||||
)
|
||||
}
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
|
||||
// Ignore functions with empty bodies in...
|
||||
if is_empty_body(function_def) {
|
||||
// Stub definitions (.pyi files)...
|
||||
if checker.source_type.is_stub() {
|
||||
return true;
|
||||
}
|
||||
_ => false,
|
||||
})
|
||||
|
||||
// Abstract methods...
|
||||
if visibility::is_abstract(&function_def.decorator_list, checker.semantic()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Overload definitions...
|
||||
if visibility::is_overload(&function_def.decorator_list, checker.semantic()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Generate flake8-annotation checks for a given `Definition`.
|
||||
@@ -738,9 +761,7 @@ pub(crate) fn definition(
|
||||
) {
|
||||
if is_method && visibility::is_classmethod(decorator_list, checker.semantic()) {
|
||||
if checker.enabled(Rule::MissingReturnTypeClassMethod) {
|
||||
let return_type = if visibility::is_abstract(decorator_list, checker.semantic())
|
||||
&& is_empty_body(body)
|
||||
{
|
||||
let return_type = if is_stub_function(function, checker) {
|
||||
None
|
||||
} else {
|
||||
auto_return_type(function)
|
||||
@@ -771,9 +792,7 @@ pub(crate) fn definition(
|
||||
}
|
||||
} else if is_method && visibility::is_staticmethod(decorator_list, checker.semantic()) {
|
||||
if checker.enabled(Rule::MissingReturnTypeStaticMethod) {
|
||||
let return_type = if visibility::is_abstract(decorator_list, checker.semantic())
|
||||
&& is_empty_body(body)
|
||||
{
|
||||
let return_type = if is_stub_function(function, checker) {
|
||||
None
|
||||
} else {
|
||||
auto_return_type(function)
|
||||
@@ -843,25 +862,22 @@ pub(crate) fn definition(
|
||||
match visibility {
|
||||
visibility::Visibility::Public => {
|
||||
if checker.enabled(Rule::MissingReturnTypeUndocumentedPublicFunction) {
|
||||
let return_type =
|
||||
if visibility::is_abstract(decorator_list, checker.semantic())
|
||||
&& is_empty_body(body)
|
||||
{
|
||||
None
|
||||
} else {
|
||||
auto_return_type(function)
|
||||
.and_then(|return_type| {
|
||||
return_type.into_expression(
|
||||
checker.importer(),
|
||||
function.parameters.start(),
|
||||
checker.semantic(),
|
||||
checker.settings.target_version,
|
||||
)
|
||||
})
|
||||
.map(|(return_type, edits)| {
|
||||
(checker.generator().expr(&return_type), edits)
|
||||
})
|
||||
};
|
||||
let return_type = if is_stub_function(function, checker) {
|
||||
None
|
||||
} else {
|
||||
auto_return_type(function)
|
||||
.and_then(|return_type| {
|
||||
return_type.into_expression(
|
||||
checker.importer(),
|
||||
function.parameters.start(),
|
||||
checker.semantic(),
|
||||
checker.settings.target_version,
|
||||
)
|
||||
})
|
||||
.map(|(return_type, edits)| {
|
||||
(checker.generator().expr(&return_type), edits)
|
||||
})
|
||||
};
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
MissingReturnTypeUndocumentedPublicFunction {
|
||||
name: name.to_string(),
|
||||
@@ -885,25 +901,22 @@ pub(crate) fn definition(
|
||||
}
|
||||
visibility::Visibility::Private => {
|
||||
if checker.enabled(Rule::MissingReturnTypePrivateFunction) {
|
||||
let return_type =
|
||||
if visibility::is_abstract(decorator_list, checker.semantic())
|
||||
&& is_empty_body(body)
|
||||
{
|
||||
None
|
||||
} else {
|
||||
auto_return_type(function)
|
||||
.and_then(|return_type| {
|
||||
return_type.into_expression(
|
||||
checker.importer(),
|
||||
function.parameters.start(),
|
||||
checker.semantic(),
|
||||
checker.settings.target_version,
|
||||
)
|
||||
})
|
||||
.map(|(return_type, edits)| {
|
||||
(checker.generator().expr(&return_type), edits)
|
||||
})
|
||||
};
|
||||
let return_type = if is_stub_function(function, checker) {
|
||||
None
|
||||
} else {
|
||||
auto_return_type(function)
|
||||
.and_then(|return_type| {
|
||||
return_type.into_expression(
|
||||
checker.importer(),
|
||||
function.parameters.start(),
|
||||
checker.semantic(),
|
||||
checker.settings.target_version,
|
||||
)
|
||||
})
|
||||
.map(|(return_type, edits)| {
|
||||
(checker.generator().expr(&return_type), edits)
|
||||
})
|
||||
};
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
MissingReturnTypePrivateFunction {
|
||||
name: name.to_string(),
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
use ruff_python_ast::{self as ast, Expr, Operator};
|
||||
use ruff_python_semantic::SemanticModel;
|
||||
|
||||
/// Traverse a "union" type annotation, applying `func` to each union member.
|
||||
/// Supports traversal of `Union` and `|` union expressions.
|
||||
/// The function is called with each expression in the union (excluding declarations of nested unions)
|
||||
/// and the parent expression (if any).
|
||||
pub(super) fn traverse_union<'a, F>(
|
||||
func: &mut F,
|
||||
semantic: &SemanticModel,
|
||||
expr: &'a Expr,
|
||||
parent: Option<&'a Expr>,
|
||||
) where
|
||||
F: FnMut(&'a Expr, Option<&'a Expr>),
|
||||
{
|
||||
// Ex) x | y
|
||||
if let Expr::BinOp(ast::ExprBinOp {
|
||||
op: Operator::BitOr,
|
||||
left,
|
||||
right,
|
||||
range: _,
|
||||
}) = expr
|
||||
{
|
||||
// The union data structure usually looks like this:
|
||||
// a | b | c -> (a | b) | c
|
||||
//
|
||||
// However, parenthesized expressions can coerce it into any structure:
|
||||
// a | (b | c)
|
||||
//
|
||||
// So we have to traverse both branches in order (left, then right), to report members
|
||||
// in the order they appear in the source code.
|
||||
|
||||
// Traverse the left then right arms
|
||||
traverse_union(func, semantic, left, Some(expr));
|
||||
traverse_union(func, semantic, right, Some(expr));
|
||||
return;
|
||||
}
|
||||
|
||||
// Ex) Union[x, y]
|
||||
if let Expr::Subscript(ast::ExprSubscript { value, slice, .. }) = expr {
|
||||
if semantic.match_typing_expr(value, "Union") {
|
||||
if let Expr::Tuple(ast::ExprTuple { elts, .. }) = slice.as_ref() {
|
||||
// Traverse each element of the tuple within the union recursively to handle cases
|
||||
// such as `Union[..., Union[...]]
|
||||
elts.iter()
|
||||
.for_each(|elt| traverse_union(func, semantic, elt, Some(expr)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, call the function on expression
|
||||
func(expr, parent);
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
//! Rules from [flake8-pyi](https://pypi.org/project/flake8-pyi/).
|
||||
mod helpers;
|
||||
pub(crate) mod rules;
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
use ruff_python_ast::{self as ast, Expr};
|
||||
use rustc_hash::FxHashSet;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::rules::flake8_pyi::helpers::traverse_union;
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::comparable::ComparableExpr;
|
||||
use ruff_python_ast::{self as ast, Expr};
|
||||
use ruff_python_semantic::analyze::typing::traverse_union;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for duplicate union members.
|
||||
///
|
||||
@@ -55,7 +56,7 @@ pub(crate) fn duplicate_union_member<'a>(checker: &mut Checker, expr: &'a Expr)
|
||||
let mut diagnostics: Vec<Diagnostic> = Vec::new();
|
||||
|
||||
// Adds a member to `literal_exprs` if it is a `Literal` annotation
|
||||
let mut check_for_duplicate_members = |expr: &'a Expr, parent: Option<&'a Expr>| {
|
||||
let mut check_for_duplicate_members = |expr: &'a Expr, parent: &'a Expr| {
|
||||
// If we've already seen this union member, raise a violation.
|
||||
if !seen_nodes.insert(expr.into()) {
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
@@ -68,7 +69,7 @@ pub(crate) fn duplicate_union_member<'a>(checker: &mut Checker, expr: &'a Expr)
|
||||
// parent without the duplicate.
|
||||
|
||||
// If the parent node is not a `BinOp` we will not perform a fix
|
||||
if let Some(parent @ Expr::BinOp(ast::ExprBinOp { left, right, .. })) = parent {
|
||||
if let Expr::BinOp(ast::ExprBinOp { left, right, .. }) = parent {
|
||||
// Replace the parent with its non-duplicate child.
|
||||
let child = if expr == left.as_ref() { right } else { left };
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
@@ -81,12 +82,7 @@ pub(crate) fn duplicate_union_member<'a>(checker: &mut Checker, expr: &'a Expr)
|
||||
};
|
||||
|
||||
// Traverse the union, collect all diagnostic members
|
||||
traverse_union(
|
||||
&mut check_for_duplicate_members,
|
||||
checker.semantic(),
|
||||
expr,
|
||||
None,
|
||||
);
|
||||
traverse_union(&mut check_for_duplicate_members, checker.semantic(), expr);
|
||||
|
||||
// Add all diagnostics to the checker
|
||||
checker.diagnostics.append(&mut diagnostics);
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
use rustc_hash::FxHashSet;
|
||||
use std::fmt;
|
||||
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::{self as ast, Expr, LiteralExpressionRef};
|
||||
use ruff_python_semantic::analyze::typing::traverse_union;
|
||||
use ruff_python_semantic::SemanticModel;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::snippet::SourceCodeSnippet;
|
||||
use crate::{checkers::ast::Checker, rules::flake8_pyi::helpers::traverse_union};
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for the presence of redundant `Literal` types and builtin super
|
||||
@@ -64,7 +66,7 @@ pub(crate) fn redundant_literal_union<'a>(checker: &mut Checker, union: &'a Expr
|
||||
|
||||
// Adds a member to `literal_exprs` for each value in a `Literal`, and any builtin types
|
||||
// to `builtin_types_in_union`.
|
||||
let mut func = |expr: &'a Expr, _| {
|
||||
let mut func = |expr: &'a Expr, _parent: &'a Expr| {
|
||||
if let Expr::Subscript(ast::ExprSubscript { value, slice, .. }) = expr {
|
||||
if checker.semantic().match_typing_expr(value, "Literal") {
|
||||
if let Expr::Tuple(ast::ExprTuple { elts, .. }) = slice.as_ref() {
|
||||
@@ -82,7 +84,7 @@ pub(crate) fn redundant_literal_union<'a>(checker: &mut Checker, union: &'a Expr
|
||||
builtin_types_in_union.insert(builtin_type);
|
||||
};
|
||||
|
||||
traverse_union(&mut func, checker.semantic(), union, None);
|
||||
traverse_union(&mut func, checker.semantic(), union);
|
||||
|
||||
for typing_literal_expr in typing_literal_exprs {
|
||||
let Some(literal_type) = match_literal_type(typing_literal_expr) else {
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
use ruff_python_ast::{Expr, Parameters};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::{Expr, Parameters};
|
||||
use ruff_python_semantic::analyze::typing::traverse_union;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::rules::flake8_pyi::helpers::traverse_union;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for union annotations that contain redundant numeric types (e.g.,
|
||||
@@ -90,7 +89,7 @@ fn check_annotation(checker: &mut Checker, annotation: &Expr) {
|
||||
let mut has_complex = false;
|
||||
let mut has_int = false;
|
||||
|
||||
let mut func = |expr: &Expr, _parent: Option<&Expr>| {
|
||||
let mut func = |expr: &Expr, _parent: &Expr| {
|
||||
let Some(call_path) = checker.semantic().resolve_call_path(expr) else {
|
||||
return;
|
||||
};
|
||||
@@ -103,7 +102,7 @@ fn check_annotation(checker: &mut Checker, annotation: &Expr) {
|
||||
}
|
||||
};
|
||||
|
||||
traverse_union(&mut func, checker.semantic(), annotation, None);
|
||||
traverse_union(&mut func, checker.semantic(), annotation);
|
||||
|
||||
if has_complex {
|
||||
if has_float {
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
use ast::{ExprSubscript, Operator};
|
||||
use ast::Operator;
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::{self as ast, Expr};
|
||||
use ruff_python_semantic::analyze::typing::traverse_union;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
use crate::rules::flake8_pyi::helpers::traverse_union;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for the presence of multiple literal types in a union.
|
||||
///
|
||||
@@ -62,7 +61,7 @@ fn concatenate_bin_ors(exprs: Vec<&Expr>) -> Expr {
|
||||
})
|
||||
}
|
||||
|
||||
fn make_union(subscript: &ExprSubscript, exprs: Vec<&Expr>) -> Expr {
|
||||
fn make_union(subscript: &ast::ExprSubscript, exprs: Vec<&Expr>) -> Expr {
|
||||
Expr::Subscript(ast::ExprSubscript {
|
||||
value: subscript.value.clone(),
|
||||
slice: Box::new(Expr::Tuple(ast::ExprTuple {
|
||||
@@ -108,7 +107,7 @@ pub(crate) fn unnecessary_literal_union<'a>(checker: &mut Checker, expr: &'a Exp
|
||||
let mut total_literals = 0;
|
||||
|
||||
// Split members into `literal_exprs` if they are a `Literal` annotation and `other_exprs` otherwise
|
||||
let mut collect_literal_expr = |expr: &'a Expr, _| {
|
||||
let mut collect_literal_expr = |expr: &'a Expr, _parent: &'a Expr| {
|
||||
if let Expr::Subscript(ast::ExprSubscript { value, slice, .. }) = expr {
|
||||
if checker.semantic().match_typing_expr(value, "Literal") {
|
||||
total_literals += 1;
|
||||
@@ -137,7 +136,7 @@ pub(crate) fn unnecessary_literal_union<'a>(checker: &mut Checker, expr: &'a Exp
|
||||
};
|
||||
|
||||
// Traverse the union, collect all members, split out the literals from the rest.
|
||||
traverse_union(&mut collect_literal_expr, checker.semantic(), expr, None);
|
||||
traverse_union(&mut collect_literal_expr, checker.semantic(), expr);
|
||||
|
||||
let union_subscript = expr.as_subscript_expr();
|
||||
if union_subscript.is_some_and(|subscript| {
|
||||
|
||||
@@ -2,9 +2,10 @@ use ast::{ExprContext, Operator};
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::{self as ast, Expr};
|
||||
use ruff_python_semantic::analyze::typing::traverse_union;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::{checkers::ast::Checker, rules::flake8_pyi::helpers::traverse_union};
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for the presence of multiple `type`s in a union.
|
||||
@@ -82,7 +83,7 @@ pub(crate) fn unnecessary_type_union<'a>(checker: &mut Checker, union: &'a Expr)
|
||||
let mut type_exprs = Vec::new();
|
||||
let mut other_exprs = Vec::new();
|
||||
|
||||
let mut collect_type_exprs = |expr: &'a Expr, _| {
|
||||
let mut collect_type_exprs = |expr: &'a Expr, _parent: &'a Expr| {
|
||||
let subscript = expr.as_subscript_expr();
|
||||
|
||||
if subscript.is_none() {
|
||||
@@ -101,7 +102,7 @@ pub(crate) fn unnecessary_type_union<'a>(checker: &mut Checker, union: &'a Expr)
|
||||
}
|
||||
};
|
||||
|
||||
traverse_union(&mut collect_type_exprs, checker.semantic(), union, None);
|
||||
traverse_union(&mut collect_type_exprs, checker.semantic(), union);
|
||||
|
||||
if type_exprs.len() > 1 {
|
||||
let type_members: Vec<String> = type_exprs
|
||||
|
||||
@@ -56,6 +56,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test_case(Rule::InDictKeys, Path::new("SIM118.py"))]
|
||||
#[test_case(Rule::YodaConditions, Path::new("SIM300.py"))]
|
||||
#[test_case(Rule::IfElseBlockInsteadOfDictGet, Path::new("SIM401.py"))]
|
||||
#[test_case(Rule::DictGetWithNoneDefault, Path::new("SIM910.py"))]
|
||||
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::cmp;
|
||||
|
||||
use anyhow::Result;
|
||||
use libcst_native::CompOp;
|
||||
|
||||
@@ -14,6 +16,7 @@ use crate::cst::helpers::or_space;
|
||||
use crate::cst::matchers::{match_comparison, transform_expression};
|
||||
use crate::fix::edits::pad;
|
||||
use crate::fix::snippet::SourceCodeSnippet;
|
||||
use crate::settings::types::PreviewMode;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for conditions that position a constant on the left-hand side of the
|
||||
@@ -78,18 +81,65 @@ impl Violation for YodaConditions {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return `true` if an [`Expr`] is a constant or a constant-like name.
|
||||
fn is_constant_like(expr: &Expr) -> bool {
|
||||
match expr {
|
||||
Expr::Attribute(ast::ExprAttribute { attr, .. }) => str::is_cased_uppercase(attr),
|
||||
Expr::Tuple(ast::ExprTuple { elts, .. }) => elts.iter().all(is_constant_like),
|
||||
Expr::Name(ast::ExprName { id, .. }) => str::is_cased_uppercase(id),
|
||||
Expr::UnaryOp(ast::ExprUnaryOp {
|
||||
op: UnaryOp::UAdd | UnaryOp::USub | UnaryOp::Invert,
|
||||
operand,
|
||||
range: _,
|
||||
}) => operand.is_literal_expr(),
|
||||
_ => expr.is_literal_expr(),
|
||||
/// Comparisons left-hand side must not be more [`ConstantLikelihood`] than the right-hand side.
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
enum ConstantLikelihood {
|
||||
/// The expression is unlikely to be a constant (e.g., `foo` or `foo(bar)`).
|
||||
Unlikely = 0,
|
||||
|
||||
/// The expression is likely to be a constant (e.g., `FOO`).
|
||||
Probably = 1,
|
||||
|
||||
/// The expression is definitely a constant (e.g., `42` or `"foo"`).
|
||||
Definitely = 2,
|
||||
}
|
||||
|
||||
impl ConstantLikelihood {
|
||||
/// Determine the [`ConstantLikelihood`] of an expression.
|
||||
fn from_expression(expr: &Expr, preview: PreviewMode) -> Self {
|
||||
match expr {
|
||||
_ if expr.is_literal_expr() => ConstantLikelihood::Definitely,
|
||||
Expr::Attribute(ast::ExprAttribute { attr, .. }) => {
|
||||
ConstantLikelihood::from_identifier(attr)
|
||||
}
|
||||
Expr::Name(ast::ExprName { id, .. }) => ConstantLikelihood::from_identifier(id),
|
||||
Expr::Tuple(ast::ExprTuple { elts, .. }) => elts
|
||||
.iter()
|
||||
.map(|expr| ConstantLikelihood::from_expression(expr, preview))
|
||||
.min()
|
||||
.unwrap_or(ConstantLikelihood::Definitely),
|
||||
Expr::List(ast::ExprList { elts, .. }) if preview.is_enabled() => elts
|
||||
.iter()
|
||||
.map(|expr| ConstantLikelihood::from_expression(expr, preview))
|
||||
.min()
|
||||
.unwrap_or(ConstantLikelihood::Definitely),
|
||||
Expr::Dict(ast::ExprDict { values: vs, .. }) if preview.is_enabled() => {
|
||||
if vs.is_empty() {
|
||||
ConstantLikelihood::Definitely
|
||||
} else {
|
||||
ConstantLikelihood::Probably
|
||||
}
|
||||
}
|
||||
Expr::BinOp(ast::ExprBinOp { left, right, .. }) => cmp::min(
|
||||
ConstantLikelihood::from_expression(left, preview),
|
||||
ConstantLikelihood::from_expression(right, preview),
|
||||
),
|
||||
Expr::UnaryOp(ast::ExprUnaryOp {
|
||||
op: UnaryOp::UAdd | UnaryOp::USub | UnaryOp::Invert,
|
||||
operand,
|
||||
range: _,
|
||||
}) => ConstantLikelihood::from_expression(operand, preview),
|
||||
_ => ConstantLikelihood::Unlikely,
|
||||
}
|
||||
}
|
||||
|
||||
/// Determine the [`ConstantLikelihood`] of an identifier.
|
||||
fn from_identifier(identifier: &str) -> Self {
|
||||
if str::is_cased_uppercase(identifier) {
|
||||
ConstantLikelihood::Probably
|
||||
} else {
|
||||
ConstantLikelihood::Unlikely
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +230,9 @@ pub(crate) fn yoda_conditions(
|
||||
return;
|
||||
}
|
||||
|
||||
if !is_constant_like(left) || is_constant_like(right) {
|
||||
if ConstantLikelihood::from_expression(left, checker.settings.preview)
|
||||
<= ConstantLikelihood::from_expression(right, checker.settings.preview)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ SIM300.py:2:1: SIM300 [*] Yoda conditions are discouraged, use `compare == "yoda
|
||||
1 | # Errors
|
||||
2 | "yoda" == compare # SIM300
|
||||
| ^^^^^^^^^^^^^^^^^ SIM300
|
||||
3 | "yoda" == compare # SIM300
|
||||
4 | 42 == age # SIM300
|
||||
3 | 42 == age # SIM300
|
||||
4 | ("a", "b") == compare # SIM300
|
||||
|
|
||||
= help: Replace Yoda condition with `compare == "yoda"`
|
||||
|
||||
@@ -15,342 +15,340 @@ SIM300.py:2:1: SIM300 [*] Yoda conditions are discouraged, use `compare == "yoda
|
||||
1 1 | # Errors
|
||||
2 |-"yoda" == compare # SIM300
|
||||
2 |+compare == "yoda" # SIM300
|
||||
3 3 | "yoda" == compare # SIM300
|
||||
4 4 | 42 == age # SIM300
|
||||
5 5 | ("a", "b") == compare # SIM300
|
||||
3 3 | 42 == age # SIM300
|
||||
4 4 | ("a", "b") == compare # SIM300
|
||||
5 5 | "yoda" <= compare # SIM300
|
||||
|
||||
SIM300.py:3:1: SIM300 [*] Yoda conditions are discouraged, use `compare == "yoda"` instead
|
||||
SIM300.py:3:1: SIM300 [*] Yoda conditions are discouraged, use `age == 42` instead
|
||||
|
|
||||
1 | # Errors
|
||||
2 | "yoda" == compare # SIM300
|
||||
3 | "yoda" == compare # SIM300
|
||||
| ^^^^^^^^^^^^^^^^^ SIM300
|
||||
4 | 42 == age # SIM300
|
||||
5 | ("a", "b") == compare # SIM300
|
||||
|
|
||||
= help: Replace Yoda condition with `compare == "yoda"`
|
||||
|
||||
ℹ Safe fix
|
||||
1 1 | # Errors
|
||||
2 2 | "yoda" == compare # SIM300
|
||||
3 |-"yoda" == compare # SIM300
|
||||
3 |+compare == "yoda" # SIM300
|
||||
4 4 | 42 == age # SIM300
|
||||
5 5 | ("a", "b") == compare # SIM300
|
||||
6 6 | "yoda" <= compare # SIM300
|
||||
|
||||
SIM300.py:4:1: SIM300 [*] Yoda conditions are discouraged, use `age == 42` instead
|
||||
|
|
||||
2 | "yoda" == compare # SIM300
|
||||
3 | "yoda" == compare # SIM300
|
||||
4 | 42 == age # SIM300
|
||||
3 | 42 == age # SIM300
|
||||
| ^^^^^^^^^ SIM300
|
||||
5 | ("a", "b") == compare # SIM300
|
||||
6 | "yoda" <= compare # SIM300
|
||||
4 | ("a", "b") == compare # SIM300
|
||||
5 | "yoda" <= compare # SIM300
|
||||
|
|
||||
= help: Replace Yoda condition with `age == 42`
|
||||
|
||||
ℹ Safe fix
|
||||
1 1 | # Errors
|
||||
2 2 | "yoda" == compare # SIM300
|
||||
3 3 | "yoda" == compare # SIM300
|
||||
4 |-42 == age # SIM300
|
||||
4 |+age == 42 # SIM300
|
||||
5 5 | ("a", "b") == compare # SIM300
|
||||
6 6 | "yoda" <= compare # SIM300
|
||||
7 7 | "yoda" < compare # SIM300
|
||||
3 |-42 == age # SIM300
|
||||
3 |+age == 42 # SIM300
|
||||
4 4 | ("a", "b") == compare # SIM300
|
||||
5 5 | "yoda" <= compare # SIM300
|
||||
6 6 | "yoda" < compare # SIM300
|
||||
|
||||
SIM300.py:5:1: SIM300 [*] Yoda conditions are discouraged, use `compare == ("a", "b")` instead
|
||||
SIM300.py:4:1: SIM300 [*] Yoda conditions are discouraged, use `compare == ("a", "b")` instead
|
||||
|
|
||||
3 | "yoda" == compare # SIM300
|
||||
4 | 42 == age # SIM300
|
||||
5 | ("a", "b") == compare # SIM300
|
||||
2 | "yoda" == compare # SIM300
|
||||
3 | 42 == age # SIM300
|
||||
4 | ("a", "b") == compare # SIM300
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ SIM300
|
||||
6 | "yoda" <= compare # SIM300
|
||||
7 | "yoda" < compare # SIM300
|
||||
5 | "yoda" <= compare # SIM300
|
||||
6 | "yoda" < compare # SIM300
|
||||
|
|
||||
= help: Replace Yoda condition with `compare == ("a", "b")`
|
||||
|
||||
ℹ Safe fix
|
||||
1 1 | # Errors
|
||||
2 2 | "yoda" == compare # SIM300
|
||||
3 3 | "yoda" == compare # SIM300
|
||||
4 4 | 42 == age # SIM300
|
||||
5 |-("a", "b") == compare # SIM300
|
||||
5 |+compare == ("a", "b") # SIM300
|
||||
6 6 | "yoda" <= compare # SIM300
|
||||
7 7 | "yoda" < compare # SIM300
|
||||
8 8 | 42 > age # SIM300
|
||||
3 3 | 42 == age # SIM300
|
||||
4 |-("a", "b") == compare # SIM300
|
||||
4 |+compare == ("a", "b") # SIM300
|
||||
5 5 | "yoda" <= compare # SIM300
|
||||
6 6 | "yoda" < compare # SIM300
|
||||
7 7 | 42 > age # SIM300
|
||||
|
||||
SIM300.py:6:1: SIM300 [*] Yoda conditions are discouraged, use `compare >= "yoda"` instead
|
||||
SIM300.py:5:1: SIM300 [*] Yoda conditions are discouraged, use `compare >= "yoda"` instead
|
||||
|
|
||||
4 | 42 == age # SIM300
|
||||
5 | ("a", "b") == compare # SIM300
|
||||
6 | "yoda" <= compare # SIM300
|
||||
3 | 42 == age # SIM300
|
||||
4 | ("a", "b") == compare # SIM300
|
||||
5 | "yoda" <= compare # SIM300
|
||||
| ^^^^^^^^^^^^^^^^^ SIM300
|
||||
7 | "yoda" < compare # SIM300
|
||||
8 | 42 > age # SIM300
|
||||
6 | "yoda" < compare # SIM300
|
||||
7 | 42 > age # SIM300
|
||||
|
|
||||
= help: Replace Yoda condition with `compare >= "yoda"`
|
||||
|
||||
ℹ Safe fix
|
||||
3 3 | "yoda" == compare # SIM300
|
||||
4 4 | 42 == age # SIM300
|
||||
5 5 | ("a", "b") == compare # SIM300
|
||||
6 |-"yoda" <= compare # SIM300
|
||||
6 |+compare >= "yoda" # SIM300
|
||||
7 7 | "yoda" < compare # SIM300
|
||||
8 8 | 42 > age # SIM300
|
||||
9 9 | -42 > age # SIM300
|
||||
2 2 | "yoda" == compare # SIM300
|
||||
3 3 | 42 == age # SIM300
|
||||
4 4 | ("a", "b") == compare # SIM300
|
||||
5 |-"yoda" <= compare # SIM300
|
||||
5 |+compare >= "yoda" # SIM300
|
||||
6 6 | "yoda" < compare # SIM300
|
||||
7 7 | 42 > age # SIM300
|
||||
8 8 | -42 > age # SIM300
|
||||
|
||||
SIM300.py:7:1: SIM300 [*] Yoda conditions are discouraged, use `compare > "yoda"` instead
|
||||
SIM300.py:6:1: SIM300 [*] Yoda conditions are discouraged, use `compare > "yoda"` instead
|
||||
|
|
||||
5 | ("a", "b") == compare # SIM300
|
||||
6 | "yoda" <= compare # SIM300
|
||||
7 | "yoda" < compare # SIM300
|
||||
4 | ("a", "b") == compare # SIM300
|
||||
5 | "yoda" <= compare # SIM300
|
||||
6 | "yoda" < compare # SIM300
|
||||
| ^^^^^^^^^^^^^^^^ SIM300
|
||||
8 | 42 > age # SIM300
|
||||
9 | -42 > age # SIM300
|
||||
7 | 42 > age # SIM300
|
||||
8 | -42 > age # SIM300
|
||||
|
|
||||
= help: Replace Yoda condition with `compare > "yoda"`
|
||||
|
||||
ℹ Safe fix
|
||||
4 4 | 42 == age # SIM300
|
||||
5 5 | ("a", "b") == compare # SIM300
|
||||
6 6 | "yoda" <= compare # SIM300
|
||||
7 |-"yoda" < compare # SIM300
|
||||
7 |+compare > "yoda" # SIM300
|
||||
8 8 | 42 > age # SIM300
|
||||
9 9 | -42 > age # SIM300
|
||||
10 10 | +42 > age # SIM300
|
||||
3 3 | 42 == age # SIM300
|
||||
4 4 | ("a", "b") == compare # SIM300
|
||||
5 5 | "yoda" <= compare # SIM300
|
||||
6 |-"yoda" < compare # SIM300
|
||||
6 |+compare > "yoda" # SIM300
|
||||
7 7 | 42 > age # SIM300
|
||||
8 8 | -42 > age # SIM300
|
||||
9 9 | +42 > age # SIM300
|
||||
|
||||
SIM300.py:8:1: SIM300 [*] Yoda conditions are discouraged, use `age < 42` instead
|
||||
|
|
||||
6 | "yoda" <= compare # SIM300
|
||||
7 | "yoda" < compare # SIM300
|
||||
8 | 42 > age # SIM300
|
||||
| ^^^^^^^^ SIM300
|
||||
9 | -42 > age # SIM300
|
||||
10 | +42 > age # SIM300
|
||||
|
|
||||
= help: Replace Yoda condition with `age < 42`
|
||||
SIM300.py:7:1: SIM300 [*] Yoda conditions are discouraged, use `age < 42` instead
|
||||
|
|
||||
5 | "yoda" <= compare # SIM300
|
||||
6 | "yoda" < compare # SIM300
|
||||
7 | 42 > age # SIM300
|
||||
| ^^^^^^^^ SIM300
|
||||
8 | -42 > age # SIM300
|
||||
9 | +42 > age # SIM300
|
||||
|
|
||||
= help: Replace Yoda condition with `age < 42`
|
||||
|
||||
ℹ Safe fix
|
||||
5 5 | ("a", "b") == compare # SIM300
|
||||
6 6 | "yoda" <= compare # SIM300
|
||||
7 7 | "yoda" < compare # SIM300
|
||||
8 |-42 > age # SIM300
|
||||
8 |+age < 42 # SIM300
|
||||
9 9 | -42 > age # SIM300
|
||||
10 10 | +42 > age # SIM300
|
||||
11 11 | YODA == age # SIM300
|
||||
4 4 | ("a", "b") == compare # SIM300
|
||||
5 5 | "yoda" <= compare # SIM300
|
||||
6 6 | "yoda" < compare # SIM300
|
||||
7 |-42 > age # SIM300
|
||||
7 |+age < 42 # SIM300
|
||||
8 8 | -42 > age # SIM300
|
||||
9 9 | +42 > age # SIM300
|
||||
10 10 | YODA == age # SIM300
|
||||
|
||||
SIM300.py:9:1: SIM300 [*] Yoda conditions are discouraged, use `age < -42` instead
|
||||
SIM300.py:8:1: SIM300 [*] Yoda conditions are discouraged, use `age < -42` instead
|
||||
|
|
||||
7 | "yoda" < compare # SIM300
|
||||
8 | 42 > age # SIM300
|
||||
9 | -42 > age # SIM300
|
||||
6 | "yoda" < compare # SIM300
|
||||
7 | 42 > age # SIM300
|
||||
8 | -42 > age # SIM300
|
||||
| ^^^^^^^^^ SIM300
|
||||
10 | +42 > age # SIM300
|
||||
11 | YODA == age # SIM300
|
||||
9 | +42 > age # SIM300
|
||||
10 | YODA == age # SIM300
|
||||
|
|
||||
= help: Replace Yoda condition with `age < -42`
|
||||
|
||||
ℹ Safe fix
|
||||
6 6 | "yoda" <= compare # SIM300
|
||||
7 7 | "yoda" < compare # SIM300
|
||||
8 8 | 42 > age # SIM300
|
||||
9 |--42 > age # SIM300
|
||||
9 |+age < -42 # SIM300
|
||||
10 10 | +42 > age # SIM300
|
||||
11 11 | YODA == age # SIM300
|
||||
12 12 | YODA > age # SIM300
|
||||
5 5 | "yoda" <= compare # SIM300
|
||||
6 6 | "yoda" < compare # SIM300
|
||||
7 7 | 42 > age # SIM300
|
||||
8 |--42 > age # SIM300
|
||||
8 |+age < -42 # SIM300
|
||||
9 9 | +42 > age # SIM300
|
||||
10 10 | YODA == age # SIM300
|
||||
11 11 | YODA > age # SIM300
|
||||
|
||||
SIM300.py:10:1: SIM300 [*] Yoda conditions are discouraged, use `age < +42` instead
|
||||
SIM300.py:9:1: SIM300 [*] Yoda conditions are discouraged, use `age < +42` instead
|
||||
|
|
||||
8 | 42 > age # SIM300
|
||||
9 | -42 > age # SIM300
|
||||
10 | +42 > age # SIM300
|
||||
7 | 42 > age # SIM300
|
||||
8 | -42 > age # SIM300
|
||||
9 | +42 > age # SIM300
|
||||
| ^^^^^^^^^ SIM300
|
||||
11 | YODA == age # SIM300
|
||||
12 | YODA > age # SIM300
|
||||
10 | YODA == age # SIM300
|
||||
11 | YODA > age # SIM300
|
||||
|
|
||||
= help: Replace Yoda condition with `age < +42`
|
||||
|
||||
ℹ Safe fix
|
||||
7 7 | "yoda" < compare # SIM300
|
||||
8 8 | 42 > age # SIM300
|
||||
9 9 | -42 > age # SIM300
|
||||
10 |-+42 > age # SIM300
|
||||
10 |+age < +42 # SIM300
|
||||
11 11 | YODA == age # SIM300
|
||||
12 12 | YODA > age # SIM300
|
||||
13 13 | YODA >= age # SIM300
|
||||
6 6 | "yoda" < compare # SIM300
|
||||
7 7 | 42 > age # SIM300
|
||||
8 8 | -42 > age # SIM300
|
||||
9 |-+42 > age # SIM300
|
||||
9 |+age < +42 # SIM300
|
||||
10 10 | YODA == age # SIM300
|
||||
11 11 | YODA > age # SIM300
|
||||
12 12 | YODA >= age # SIM300
|
||||
|
||||
SIM300.py:11:1: SIM300 [*] Yoda conditions are discouraged, use `age == YODA` instead
|
||||
SIM300.py:10:1: SIM300 [*] Yoda conditions are discouraged, use `age == YODA` instead
|
||||
|
|
||||
9 | -42 > age # SIM300
|
||||
10 | +42 > age # SIM300
|
||||
11 | YODA == age # SIM300
|
||||
8 | -42 > age # SIM300
|
||||
9 | +42 > age # SIM300
|
||||
10 | YODA == age # SIM300
|
||||
| ^^^^^^^^^^^ SIM300
|
||||
12 | YODA > age # SIM300
|
||||
13 | YODA >= age # SIM300
|
||||
11 | YODA > age # SIM300
|
||||
12 | YODA >= age # SIM300
|
||||
|
|
||||
= help: Replace Yoda condition with `age == YODA`
|
||||
|
||||
ℹ Safe fix
|
||||
8 8 | 42 > age # SIM300
|
||||
9 9 | -42 > age # SIM300
|
||||
10 10 | +42 > age # SIM300
|
||||
11 |-YODA == age # SIM300
|
||||
11 |+age == YODA # SIM300
|
||||
12 12 | YODA > age # SIM300
|
||||
13 13 | YODA >= age # SIM300
|
||||
14 14 | JediOrder.YODA == age # SIM300
|
||||
7 7 | 42 > age # SIM300
|
||||
8 8 | -42 > age # SIM300
|
||||
9 9 | +42 > age # SIM300
|
||||
10 |-YODA == age # SIM300
|
||||
10 |+age == YODA # SIM300
|
||||
11 11 | YODA > age # SIM300
|
||||
12 12 | YODA >= age # SIM300
|
||||
13 13 | JediOrder.YODA == age # SIM300
|
||||
|
||||
SIM300.py:12:1: SIM300 [*] Yoda conditions are discouraged, use `age < YODA` instead
|
||||
SIM300.py:11:1: SIM300 [*] Yoda conditions are discouraged, use `age < YODA` instead
|
||||
|
|
||||
10 | +42 > age # SIM300
|
||||
11 | YODA == age # SIM300
|
||||
12 | YODA > age # SIM300
|
||||
9 | +42 > age # SIM300
|
||||
10 | YODA == age # SIM300
|
||||
11 | YODA > age # SIM300
|
||||
| ^^^^^^^^^^ SIM300
|
||||
13 | YODA >= age # SIM300
|
||||
14 | JediOrder.YODA == age # SIM300
|
||||
12 | YODA >= age # SIM300
|
||||
13 | JediOrder.YODA == age # SIM300
|
||||
|
|
||||
= help: Replace Yoda condition with `age < YODA`
|
||||
|
||||
ℹ Safe fix
|
||||
9 9 | -42 > age # SIM300
|
||||
10 10 | +42 > age # SIM300
|
||||
11 11 | YODA == age # SIM300
|
||||
12 |-YODA > age # SIM300
|
||||
12 |+age < YODA # SIM300
|
||||
13 13 | YODA >= age # SIM300
|
||||
14 14 | JediOrder.YODA == age # SIM300
|
||||
15 15 | 0 < (number - 100) # SIM300
|
||||
8 8 | -42 > age # SIM300
|
||||
9 9 | +42 > age # SIM300
|
||||
10 10 | YODA == age # SIM300
|
||||
11 |-YODA > age # SIM300
|
||||
11 |+age < YODA # SIM300
|
||||
12 12 | YODA >= age # SIM300
|
||||
13 13 | JediOrder.YODA == age # SIM300
|
||||
14 14 | 0 < (number - 100) # SIM300
|
||||
|
||||
SIM300.py:13:1: SIM300 [*] Yoda conditions are discouraged, use `age <= YODA` instead
|
||||
SIM300.py:12:1: SIM300 [*] Yoda conditions are discouraged, use `age <= YODA` instead
|
||||
|
|
||||
11 | YODA == age # SIM300
|
||||
12 | YODA > age # SIM300
|
||||
13 | YODA >= age # SIM300
|
||||
10 | YODA == age # SIM300
|
||||
11 | YODA > age # SIM300
|
||||
12 | YODA >= age # SIM300
|
||||
| ^^^^^^^^^^^ SIM300
|
||||
14 | JediOrder.YODA == age # SIM300
|
||||
15 | 0 < (number - 100) # SIM300
|
||||
13 | JediOrder.YODA == age # SIM300
|
||||
14 | 0 < (number - 100) # SIM300
|
||||
|
|
||||
= help: Replace Yoda condition with `age <= YODA`
|
||||
|
||||
ℹ Safe fix
|
||||
10 10 | +42 > age # SIM300
|
||||
11 11 | YODA == age # SIM300
|
||||
12 12 | YODA > age # SIM300
|
||||
13 |-YODA >= age # SIM300
|
||||
13 |+age <= YODA # SIM300
|
||||
14 14 | JediOrder.YODA == age # SIM300
|
||||
15 15 | 0 < (number - 100) # SIM300
|
||||
16 16 | SomeClass().settings.SOME_CONSTANT_VALUE > (60 * 60) # SIM300
|
||||
9 9 | +42 > age # SIM300
|
||||
10 10 | YODA == age # SIM300
|
||||
11 11 | YODA > age # SIM300
|
||||
12 |-YODA >= age # SIM300
|
||||
12 |+age <= YODA # SIM300
|
||||
13 13 | JediOrder.YODA == age # SIM300
|
||||
14 14 | 0 < (number - 100) # SIM300
|
||||
15 15 | B<A[0][0]or B
|
||||
|
||||
SIM300.py:14:1: SIM300 [*] Yoda conditions are discouraged, use `age == JediOrder.YODA` instead
|
||||
SIM300.py:13:1: SIM300 [*] Yoda conditions are discouraged, use `age == JediOrder.YODA` instead
|
||||
|
|
||||
12 | YODA > age # SIM300
|
||||
13 | YODA >= age # SIM300
|
||||
14 | JediOrder.YODA == age # SIM300
|
||||
11 | YODA > age # SIM300
|
||||
12 | YODA >= age # SIM300
|
||||
13 | JediOrder.YODA == age # SIM300
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ SIM300
|
||||
15 | 0 < (number - 100) # SIM300
|
||||
16 | SomeClass().settings.SOME_CONSTANT_VALUE > (60 * 60) # SIM300
|
||||
14 | 0 < (number - 100) # SIM300
|
||||
15 | B<A[0][0]or B
|
||||
|
|
||||
= help: Replace Yoda condition with `age == JediOrder.YODA`
|
||||
|
||||
ℹ Safe fix
|
||||
11 11 | YODA == age # SIM300
|
||||
12 12 | YODA > age # SIM300
|
||||
13 13 | YODA >= age # SIM300
|
||||
14 |-JediOrder.YODA == age # SIM300
|
||||
14 |+age == JediOrder.YODA # SIM300
|
||||
15 15 | 0 < (number - 100) # SIM300
|
||||
16 16 | SomeClass().settings.SOME_CONSTANT_VALUE > (60 * 60) # SIM300
|
||||
17 17 | B<A[0][0]or B
|
||||
10 10 | YODA == age # SIM300
|
||||
11 11 | YODA > age # SIM300
|
||||
12 12 | YODA >= age # SIM300
|
||||
13 |-JediOrder.YODA == age # SIM300
|
||||
13 |+age == JediOrder.YODA # SIM300
|
||||
14 14 | 0 < (number - 100) # SIM300
|
||||
15 15 | B<A[0][0]or B
|
||||
16 16 | B or(B)<A[0][0]
|
||||
|
||||
SIM300.py:15:1: SIM300 [*] Yoda conditions are discouraged, use `(number - 100) > 0` instead
|
||||
SIM300.py:14:1: SIM300 [*] Yoda conditions are discouraged, use `(number - 100) > 0` instead
|
||||
|
|
||||
13 | YODA >= age # SIM300
|
||||
14 | JediOrder.YODA == age # SIM300
|
||||
15 | 0 < (number - 100) # SIM300
|
||||
12 | YODA >= age # SIM300
|
||||
13 | JediOrder.YODA == age # SIM300
|
||||
14 | 0 < (number - 100) # SIM300
|
||||
| ^^^^^^^^^^^^^^^^^^ SIM300
|
||||
16 | SomeClass().settings.SOME_CONSTANT_VALUE > (60 * 60) # SIM300
|
||||
17 | B<A[0][0]or B
|
||||
15 | B<A[0][0]or B
|
||||
16 | B or(B)<A[0][0]
|
||||
|
|
||||
= help: Replace Yoda condition with `(number - 100) > 0`
|
||||
|
||||
ℹ Safe fix
|
||||
12 12 | YODA > age # SIM300
|
||||
13 13 | YODA >= age # SIM300
|
||||
14 14 | JediOrder.YODA == age # SIM300
|
||||
15 |-0 < (number - 100) # SIM300
|
||||
15 |+(number - 100) > 0 # SIM300
|
||||
16 16 | SomeClass().settings.SOME_CONSTANT_VALUE > (60 * 60) # SIM300
|
||||
17 17 | B<A[0][0]or B
|
||||
18 18 | B or(B)<A[0][0]
|
||||
11 11 | YODA > age # SIM300
|
||||
12 12 | YODA >= age # SIM300
|
||||
13 13 | JediOrder.YODA == age # SIM300
|
||||
14 |-0 < (number - 100) # SIM300
|
||||
14 |+(number - 100) > 0 # SIM300
|
||||
15 15 | B<A[0][0]or B
|
||||
16 16 | B or(B)<A[0][0]
|
||||
17 17 |
|
||||
|
||||
SIM300.py:16:1: SIM300 [*] Yoda conditions are discouraged
|
||||
SIM300.py:15:1: SIM300 [*] Yoda conditions are discouraged, use `A[0][0] > B` instead
|
||||
|
|
||||
14 | JediOrder.YODA == age # SIM300
|
||||
15 | 0 < (number - 100) # SIM300
|
||||
16 | SomeClass().settings.SOME_CONSTANT_VALUE > (60 * 60) # SIM300
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SIM300
|
||||
17 | B<A[0][0]or B
|
||||
18 | B or(B)<A[0][0]
|
||||
|
|
||||
= help: Replace Yoda condition
|
||||
|
||||
ℹ Safe fix
|
||||
13 13 | YODA >= age # SIM300
|
||||
14 14 | JediOrder.YODA == age # SIM300
|
||||
15 15 | 0 < (number - 100) # SIM300
|
||||
16 |-SomeClass().settings.SOME_CONSTANT_VALUE > (60 * 60) # SIM300
|
||||
16 |+(60 * 60) < SomeClass().settings.SOME_CONSTANT_VALUE # SIM300
|
||||
17 17 | B<A[0][0]or B
|
||||
18 18 | B or(B)<A[0][0]
|
||||
19 19 |
|
||||
|
||||
SIM300.py:17:1: SIM300 [*] Yoda conditions are discouraged, use `A[0][0] > B` instead
|
||||
|
|
||||
15 | 0 < (number - 100) # SIM300
|
||||
16 | SomeClass().settings.SOME_CONSTANT_VALUE > (60 * 60) # SIM300
|
||||
17 | B<A[0][0]or B
|
||||
13 | JediOrder.YODA == age # SIM300
|
||||
14 | 0 < (number - 100) # SIM300
|
||||
15 | B<A[0][0]or B
|
||||
| ^^^^^^^^^ SIM300
|
||||
18 | B or(B)<A[0][0]
|
||||
16 | B or(B)<A[0][0]
|
||||
|
|
||||
= help: Replace Yoda condition with `A[0][0] > B`
|
||||
|
||||
ℹ Safe fix
|
||||
14 14 | JediOrder.YODA == age # SIM300
|
||||
15 15 | 0 < (number - 100) # SIM300
|
||||
16 16 | SomeClass().settings.SOME_CONSTANT_VALUE > (60 * 60) # SIM300
|
||||
17 |-B<A[0][0]or B
|
||||
17 |+A[0][0] > B or B
|
||||
18 18 | B or(B)<A[0][0]
|
||||
19 19 |
|
||||
20 20 | # OK
|
||||
12 12 | YODA >= age # SIM300
|
||||
13 13 | JediOrder.YODA == age # SIM300
|
||||
14 14 | 0 < (number - 100) # SIM300
|
||||
15 |-B<A[0][0]or B
|
||||
15 |+A[0][0] > B or B
|
||||
16 16 | B or(B)<A[0][0]
|
||||
17 17 |
|
||||
18 18 | # Errors in preview
|
||||
|
||||
SIM300.py:18:5: SIM300 [*] Yoda conditions are discouraged, use `A[0][0] > (B)` instead
|
||||
SIM300.py:16:5: SIM300 [*] Yoda conditions are discouraged, use `A[0][0] > (B)` instead
|
||||
|
|
||||
16 | SomeClass().settings.SOME_CONSTANT_VALUE > (60 * 60) # SIM300
|
||||
17 | B<A[0][0]or B
|
||||
18 | B or(B)<A[0][0]
|
||||
14 | 0 < (number - 100) # SIM300
|
||||
15 | B<A[0][0]or B
|
||||
16 | B or(B)<A[0][0]
|
||||
| ^^^^^^^^^^^ SIM300
|
||||
19 |
|
||||
20 | # OK
|
||||
17 |
|
||||
18 | # Errors in preview
|
||||
|
|
||||
= help: Replace Yoda condition with `A[0][0] > (B)`
|
||||
|
||||
ℹ Safe fix
|
||||
15 15 | 0 < (number - 100) # SIM300
|
||||
16 16 | SomeClass().settings.SOME_CONSTANT_VALUE > (60 * 60) # SIM300
|
||||
17 17 | B<A[0][0]or B
|
||||
18 |-B or(B)<A[0][0]
|
||||
18 |+B or A[0][0] > (B)
|
||||
19 19 |
|
||||
20 20 | # OK
|
||||
21 21 | compare == "yoda"
|
||||
13 13 | JediOrder.YODA == age # SIM300
|
||||
14 14 | 0 < (number - 100) # SIM300
|
||||
15 15 | B<A[0][0]or B
|
||||
16 |-B or(B)<A[0][0]
|
||||
16 |+B or A[0][0] > (B)
|
||||
17 17 |
|
||||
18 18 | # Errors in preview
|
||||
19 19 | ['upper'] == UPPER_LIST
|
||||
|
||||
SIM300.py:23:1: SIM300 [*] Yoda conditions are discouraged, use `['upper'] == UPPER_LIST` instead
|
||||
|
|
||||
22 | # Errors in stable
|
||||
23 | UPPER_LIST == ['upper']
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ SIM300
|
||||
24 | DummyHandler.CONFIG == {}
|
||||
|
|
||||
= help: Replace Yoda condition with `['upper'] == UPPER_LIST`
|
||||
|
||||
ℹ Safe fix
|
||||
20 20 | {} == DummyHandler.CONFIG
|
||||
21 21 |
|
||||
22 22 | # Errors in stable
|
||||
23 |-UPPER_LIST == ['upper']
|
||||
23 |+['upper'] == UPPER_LIST
|
||||
24 24 | DummyHandler.CONFIG == {}
|
||||
25 25 |
|
||||
26 26 | # OK
|
||||
|
||||
SIM300.py:24:1: SIM300 [*] Yoda conditions are discouraged, use `{} == DummyHandler.CONFIG` instead
|
||||
|
|
||||
22 | # Errors in stable
|
||||
23 | UPPER_LIST == ['upper']
|
||||
24 | DummyHandler.CONFIG == {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ SIM300
|
||||
25 |
|
||||
26 | # OK
|
||||
|
|
||||
= help: Replace Yoda condition with `{} == DummyHandler.CONFIG`
|
||||
|
||||
ℹ Safe fix
|
||||
21 21 |
|
||||
22 22 | # Errors in stable
|
||||
23 23 | UPPER_LIST == ['upper']
|
||||
24 |-DummyHandler.CONFIG == {}
|
||||
24 |+{} == DummyHandler.CONFIG
|
||||
25 25 |
|
||||
26 26 | # OK
|
||||
27 27 | compare == "yoda"
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,354 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_simplify/mod.rs
|
||||
---
|
||||
SIM300.py:2:1: SIM300 [*] Yoda conditions are discouraged, use `compare == "yoda"` instead
|
||||
|
|
||||
1 | # Errors
|
||||
2 | "yoda" == compare # SIM300
|
||||
| ^^^^^^^^^^^^^^^^^ SIM300
|
||||
3 | 42 == age # SIM300
|
||||
4 | ("a", "b") == compare # SIM300
|
||||
|
|
||||
= help: Replace Yoda condition with `compare == "yoda"`
|
||||
|
||||
ℹ Safe fix
|
||||
1 1 | # Errors
|
||||
2 |-"yoda" == compare # SIM300
|
||||
2 |+compare == "yoda" # SIM300
|
||||
3 3 | 42 == age # SIM300
|
||||
4 4 | ("a", "b") == compare # SIM300
|
||||
5 5 | "yoda" <= compare # SIM300
|
||||
|
||||
SIM300.py:3:1: SIM300 [*] Yoda conditions are discouraged, use `age == 42` instead
|
||||
|
|
||||
1 | # Errors
|
||||
2 | "yoda" == compare # SIM300
|
||||
3 | 42 == age # SIM300
|
||||
| ^^^^^^^^^ SIM300
|
||||
4 | ("a", "b") == compare # SIM300
|
||||
5 | "yoda" <= compare # SIM300
|
||||
|
|
||||
= help: Replace Yoda condition with `age == 42`
|
||||
|
||||
ℹ Safe fix
|
||||
1 1 | # Errors
|
||||
2 2 | "yoda" == compare # SIM300
|
||||
3 |-42 == age # SIM300
|
||||
3 |+age == 42 # SIM300
|
||||
4 4 | ("a", "b") == compare # SIM300
|
||||
5 5 | "yoda" <= compare # SIM300
|
||||
6 6 | "yoda" < compare # SIM300
|
||||
|
||||
SIM300.py:4:1: SIM300 [*] Yoda conditions are discouraged, use `compare == ("a", "b")` instead
|
||||
|
|
||||
2 | "yoda" == compare # SIM300
|
||||
3 | 42 == age # SIM300
|
||||
4 | ("a", "b") == compare # SIM300
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ SIM300
|
||||
5 | "yoda" <= compare # SIM300
|
||||
6 | "yoda" < compare # SIM300
|
||||
|
|
||||
= help: Replace Yoda condition with `compare == ("a", "b")`
|
||||
|
||||
ℹ Safe fix
|
||||
1 1 | # Errors
|
||||
2 2 | "yoda" == compare # SIM300
|
||||
3 3 | 42 == age # SIM300
|
||||
4 |-("a", "b") == compare # SIM300
|
||||
4 |+compare == ("a", "b") # SIM300
|
||||
5 5 | "yoda" <= compare # SIM300
|
||||
6 6 | "yoda" < compare # SIM300
|
||||
7 7 | 42 > age # SIM300
|
||||
|
||||
SIM300.py:5:1: SIM300 [*] Yoda conditions are discouraged, use `compare >= "yoda"` instead
|
||||
|
|
||||
3 | 42 == age # SIM300
|
||||
4 | ("a", "b") == compare # SIM300
|
||||
5 | "yoda" <= compare # SIM300
|
||||
| ^^^^^^^^^^^^^^^^^ SIM300
|
||||
6 | "yoda" < compare # SIM300
|
||||
7 | 42 > age # SIM300
|
||||
|
|
||||
= help: Replace Yoda condition with `compare >= "yoda"`
|
||||
|
||||
ℹ Safe fix
|
||||
2 2 | "yoda" == compare # SIM300
|
||||
3 3 | 42 == age # SIM300
|
||||
4 4 | ("a", "b") == compare # SIM300
|
||||
5 |-"yoda" <= compare # SIM300
|
||||
5 |+compare >= "yoda" # SIM300
|
||||
6 6 | "yoda" < compare # SIM300
|
||||
7 7 | 42 > age # SIM300
|
||||
8 8 | -42 > age # SIM300
|
||||
|
||||
SIM300.py:6:1: SIM300 [*] Yoda conditions are discouraged, use `compare > "yoda"` instead
|
||||
|
|
||||
4 | ("a", "b") == compare # SIM300
|
||||
5 | "yoda" <= compare # SIM300
|
||||
6 | "yoda" < compare # SIM300
|
||||
| ^^^^^^^^^^^^^^^^ SIM300
|
||||
7 | 42 > age # SIM300
|
||||
8 | -42 > age # SIM300
|
||||
|
|
||||
= help: Replace Yoda condition with `compare > "yoda"`
|
||||
|
||||
ℹ Safe fix
|
||||
3 3 | 42 == age # SIM300
|
||||
4 4 | ("a", "b") == compare # SIM300
|
||||
5 5 | "yoda" <= compare # SIM300
|
||||
6 |-"yoda" < compare # SIM300
|
||||
6 |+compare > "yoda" # SIM300
|
||||
7 7 | 42 > age # SIM300
|
||||
8 8 | -42 > age # SIM300
|
||||
9 9 | +42 > age # SIM300
|
||||
|
||||
SIM300.py:7:1: SIM300 [*] Yoda conditions are discouraged, use `age < 42` instead
|
||||
|
|
||||
5 | "yoda" <= compare # SIM300
|
||||
6 | "yoda" < compare # SIM300
|
||||
7 | 42 > age # SIM300
|
||||
| ^^^^^^^^ SIM300
|
||||
8 | -42 > age # SIM300
|
||||
9 | +42 > age # SIM300
|
||||
|
|
||||
= help: Replace Yoda condition with `age < 42`
|
||||
|
||||
ℹ Safe fix
|
||||
4 4 | ("a", "b") == compare # SIM300
|
||||
5 5 | "yoda" <= compare # SIM300
|
||||
6 6 | "yoda" < compare # SIM300
|
||||
7 |-42 > age # SIM300
|
||||
7 |+age < 42 # SIM300
|
||||
8 8 | -42 > age # SIM300
|
||||
9 9 | +42 > age # SIM300
|
||||
10 10 | YODA == age # SIM300
|
||||
|
||||
SIM300.py:8:1: SIM300 [*] Yoda conditions are discouraged, use `age < -42` instead
|
||||
|
|
||||
6 | "yoda" < compare # SIM300
|
||||
7 | 42 > age # SIM300
|
||||
8 | -42 > age # SIM300
|
||||
| ^^^^^^^^^ SIM300
|
||||
9 | +42 > age # SIM300
|
||||
10 | YODA == age # SIM300
|
||||
|
|
||||
= help: Replace Yoda condition with `age < -42`
|
||||
|
||||
ℹ Safe fix
|
||||
5 5 | "yoda" <= compare # SIM300
|
||||
6 6 | "yoda" < compare # SIM300
|
||||
7 7 | 42 > age # SIM300
|
||||
8 |--42 > age # SIM300
|
||||
8 |+age < -42 # SIM300
|
||||
9 9 | +42 > age # SIM300
|
||||
10 10 | YODA == age # SIM300
|
||||
11 11 | YODA > age # SIM300
|
||||
|
||||
SIM300.py:9:1: SIM300 [*] Yoda conditions are discouraged, use `age < +42` instead
|
||||
|
|
||||
7 | 42 > age # SIM300
|
||||
8 | -42 > age # SIM300
|
||||
9 | +42 > age # SIM300
|
||||
| ^^^^^^^^^ SIM300
|
||||
10 | YODA == age # SIM300
|
||||
11 | YODA > age # SIM300
|
||||
|
|
||||
= help: Replace Yoda condition with `age < +42`
|
||||
|
||||
ℹ Safe fix
|
||||
6 6 | "yoda" < compare # SIM300
|
||||
7 7 | 42 > age # SIM300
|
||||
8 8 | -42 > age # SIM300
|
||||
9 |-+42 > age # SIM300
|
||||
9 |+age < +42 # SIM300
|
||||
10 10 | YODA == age # SIM300
|
||||
11 11 | YODA > age # SIM300
|
||||
12 12 | YODA >= age # SIM300
|
||||
|
||||
SIM300.py:10:1: SIM300 [*] Yoda conditions are discouraged, use `age == YODA` instead
|
||||
|
|
||||
8 | -42 > age # SIM300
|
||||
9 | +42 > age # SIM300
|
||||
10 | YODA == age # SIM300
|
||||
| ^^^^^^^^^^^ SIM300
|
||||
11 | YODA > age # SIM300
|
||||
12 | YODA >= age # SIM300
|
||||
|
|
||||
= help: Replace Yoda condition with `age == YODA`
|
||||
|
||||
ℹ Safe fix
|
||||
7 7 | 42 > age # SIM300
|
||||
8 8 | -42 > age # SIM300
|
||||
9 9 | +42 > age # SIM300
|
||||
10 |-YODA == age # SIM300
|
||||
10 |+age == YODA # SIM300
|
||||
11 11 | YODA > age # SIM300
|
||||
12 12 | YODA >= age # SIM300
|
||||
13 13 | JediOrder.YODA == age # SIM300
|
||||
|
||||
SIM300.py:11:1: SIM300 [*] Yoda conditions are discouraged, use `age < YODA` instead
|
||||
|
|
||||
9 | +42 > age # SIM300
|
||||
10 | YODA == age # SIM300
|
||||
11 | YODA > age # SIM300
|
||||
| ^^^^^^^^^^ SIM300
|
||||
12 | YODA >= age # SIM300
|
||||
13 | JediOrder.YODA == age # SIM300
|
||||
|
|
||||
= help: Replace Yoda condition with `age < YODA`
|
||||
|
||||
ℹ Safe fix
|
||||
8 8 | -42 > age # SIM300
|
||||
9 9 | +42 > age # SIM300
|
||||
10 10 | YODA == age # SIM300
|
||||
11 |-YODA > age # SIM300
|
||||
11 |+age < YODA # SIM300
|
||||
12 12 | YODA >= age # SIM300
|
||||
13 13 | JediOrder.YODA == age # SIM300
|
||||
14 14 | 0 < (number - 100) # SIM300
|
||||
|
||||
SIM300.py:12:1: SIM300 [*] Yoda conditions are discouraged, use `age <= YODA` instead
|
||||
|
|
||||
10 | YODA == age # SIM300
|
||||
11 | YODA > age # SIM300
|
||||
12 | YODA >= age # SIM300
|
||||
| ^^^^^^^^^^^ SIM300
|
||||
13 | JediOrder.YODA == age # SIM300
|
||||
14 | 0 < (number - 100) # SIM300
|
||||
|
|
||||
= help: Replace Yoda condition with `age <= YODA`
|
||||
|
||||
ℹ Safe fix
|
||||
9 9 | +42 > age # SIM300
|
||||
10 10 | YODA == age # SIM300
|
||||
11 11 | YODA > age # SIM300
|
||||
12 |-YODA >= age # SIM300
|
||||
12 |+age <= YODA # SIM300
|
||||
13 13 | JediOrder.YODA == age # SIM300
|
||||
14 14 | 0 < (number - 100) # SIM300
|
||||
15 15 | B<A[0][0]or B
|
||||
|
||||
SIM300.py:13:1: SIM300 [*] Yoda conditions are discouraged, use `age == JediOrder.YODA` instead
|
||||
|
|
||||
11 | YODA > age # SIM300
|
||||
12 | YODA >= age # SIM300
|
||||
13 | JediOrder.YODA == age # SIM300
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ SIM300
|
||||
14 | 0 < (number - 100) # SIM300
|
||||
15 | B<A[0][0]or B
|
||||
|
|
||||
= help: Replace Yoda condition with `age == JediOrder.YODA`
|
||||
|
||||
ℹ Safe fix
|
||||
10 10 | YODA == age # SIM300
|
||||
11 11 | YODA > age # SIM300
|
||||
12 12 | YODA >= age # SIM300
|
||||
13 |-JediOrder.YODA == age # SIM300
|
||||
13 |+age == JediOrder.YODA # SIM300
|
||||
14 14 | 0 < (number - 100) # SIM300
|
||||
15 15 | B<A[0][0]or B
|
||||
16 16 | B or(B)<A[0][0]
|
||||
|
||||
SIM300.py:14:1: SIM300 [*] Yoda conditions are discouraged, use `(number - 100) > 0` instead
|
||||
|
|
||||
12 | YODA >= age # SIM300
|
||||
13 | JediOrder.YODA == age # SIM300
|
||||
14 | 0 < (number - 100) # SIM300
|
||||
| ^^^^^^^^^^^^^^^^^^ SIM300
|
||||
15 | B<A[0][0]or B
|
||||
16 | B or(B)<A[0][0]
|
||||
|
|
||||
= help: Replace Yoda condition with `(number - 100) > 0`
|
||||
|
||||
ℹ Safe fix
|
||||
11 11 | YODA > age # SIM300
|
||||
12 12 | YODA >= age # SIM300
|
||||
13 13 | JediOrder.YODA == age # SIM300
|
||||
14 |-0 < (number - 100) # SIM300
|
||||
14 |+(number - 100) > 0 # SIM300
|
||||
15 15 | B<A[0][0]or B
|
||||
16 16 | B or(B)<A[0][0]
|
||||
17 17 |
|
||||
|
||||
SIM300.py:15:1: SIM300 [*] Yoda conditions are discouraged, use `A[0][0] > B` instead
|
||||
|
|
||||
13 | JediOrder.YODA == age # SIM300
|
||||
14 | 0 < (number - 100) # SIM300
|
||||
15 | B<A[0][0]or B
|
||||
| ^^^^^^^^^ SIM300
|
||||
16 | B or(B)<A[0][0]
|
||||
|
|
||||
= help: Replace Yoda condition with `A[0][0] > B`
|
||||
|
||||
ℹ Safe fix
|
||||
12 12 | YODA >= age # SIM300
|
||||
13 13 | JediOrder.YODA == age # SIM300
|
||||
14 14 | 0 < (number - 100) # SIM300
|
||||
15 |-B<A[0][0]or B
|
||||
15 |+A[0][0] > B or B
|
||||
16 16 | B or(B)<A[0][0]
|
||||
17 17 |
|
||||
18 18 | # Errors in preview
|
||||
|
||||
SIM300.py:16:5: SIM300 [*] Yoda conditions are discouraged, use `A[0][0] > (B)` instead
|
||||
|
|
||||
14 | 0 < (number - 100) # SIM300
|
||||
15 | B<A[0][0]or B
|
||||
16 | B or(B)<A[0][0]
|
||||
| ^^^^^^^^^^^ SIM300
|
||||
17 |
|
||||
18 | # Errors in preview
|
||||
|
|
||||
= help: Replace Yoda condition with `A[0][0] > (B)`
|
||||
|
||||
ℹ Safe fix
|
||||
13 13 | JediOrder.YODA == age # SIM300
|
||||
14 14 | 0 < (number - 100) # SIM300
|
||||
15 15 | B<A[0][0]or B
|
||||
16 |-B or(B)<A[0][0]
|
||||
16 |+B or A[0][0] > (B)
|
||||
17 17 |
|
||||
18 18 | # Errors in preview
|
||||
19 19 | ['upper'] == UPPER_LIST
|
||||
|
||||
SIM300.py:19:1: SIM300 [*] Yoda conditions are discouraged, use `UPPER_LIST == ['upper']` instead
|
||||
|
|
||||
18 | # Errors in preview
|
||||
19 | ['upper'] == UPPER_LIST
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ SIM300
|
||||
20 | {} == DummyHandler.CONFIG
|
||||
|
|
||||
= help: Replace Yoda condition with `UPPER_LIST == ['upper']`
|
||||
|
||||
ℹ Safe fix
|
||||
16 16 | B or(B)<A[0][0]
|
||||
17 17 |
|
||||
18 18 | # Errors in preview
|
||||
19 |-['upper'] == UPPER_LIST
|
||||
19 |+UPPER_LIST == ['upper']
|
||||
20 20 | {} == DummyHandler.CONFIG
|
||||
21 21 |
|
||||
22 22 | # Errors in stable
|
||||
|
||||
SIM300.py:20:1: SIM300 [*] Yoda conditions are discouraged, use `DummyHandler.CONFIG == {}` instead
|
||||
|
|
||||
18 | # Errors in preview
|
||||
19 | ['upper'] == UPPER_LIST
|
||||
20 | {} == DummyHandler.CONFIG
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ SIM300
|
||||
21 |
|
||||
22 | # Errors in stable
|
||||
|
|
||||
= help: Replace Yoda condition with `DummyHandler.CONFIG == {}`
|
||||
|
||||
ℹ Safe fix
|
||||
17 17 |
|
||||
18 18 | # Errors in preview
|
||||
19 19 | ['upper'] == UPPER_LIST
|
||||
20 |-{} == DummyHandler.CONFIG
|
||||
20 |+DummyHandler.CONFIG == {}
|
||||
21 21 |
|
||||
22 22 | # Errors in stable
|
||||
23 23 | UPPER_LIST == ['upper']
|
||||
|
||||
|
||||
@@ -35,7 +35,8 @@ mod tests {
|
||||
#[test_case(Rule::LineTooLong, Path::new("E501_3.py"))]
|
||||
#[test_case(Rule::MixedSpacesAndTabs, Path::new("E101.py"))]
|
||||
#[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E40.py"))]
|
||||
#[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E402.py"))]
|
||||
#[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E402_0.py"))]
|
||||
#[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E402_1.py"))]
|
||||
#[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E402.ipynb"))]
|
||||
#[test_case(Rule::MultipleImportsOnOneLine, Path::new("E40.py"))]
|
||||
#[test_case(Rule::MultipleStatementsOnOneLineColon, Path::new("E70.py"))]
|
||||
@@ -65,7 +66,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test_case(Rule::IsLiteral, Path::new("constant_literals.py"))]
|
||||
#[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E402.py"))]
|
||||
#[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E402_0.py"))]
|
||||
#[test_case(Rule::TypeComparison, Path::new("E721.py"))]
|
||||
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
|
||||
---
|
||||
E402.py:25:1: E402 Module level import not at top of file
|
||||
E402_0.py:25:1: E402 Module level import not at top of file
|
||||
|
|
||||
23 | sys.path.insert(0, "some/path")
|
||||
24 |
|
||||
@@ -11,7 +11,7 @@ E402.py:25:1: E402 Module level import not at top of file
|
||||
27 | import matplotlib
|
||||
|
|
||||
|
||||
E402.py:27:1: E402 Module level import not at top of file
|
||||
E402_0.py:27:1: E402 Module level import not at top of file
|
||||
|
|
||||
25 | import f
|
||||
26 |
|
||||
@@ -21,7 +21,7 @@ E402.py:27:1: E402 Module level import not at top of file
|
||||
29 | matplotlib.use("Agg")
|
||||
|
|
||||
|
||||
E402.py:31:1: E402 Module level import not at top of file
|
||||
E402_0.py:31:1: E402 Module level import not at top of file
|
||||
|
|
||||
29 | matplotlib.use("Agg")
|
||||
30 |
|
||||
@@ -31,7 +31,7 @@ E402.py:31:1: E402 Module level import not at top of file
|
||||
33 | __some__magic = 1
|
||||
|
|
||||
|
||||
E402.py:35:1: E402 Module level import not at top of file
|
||||
E402_0.py:35:1: E402 Module level import not at top of file
|
||||
|
|
||||
33 | __some__magic = 1
|
||||
34 |
|
||||
@@ -39,7 +39,7 @@ E402.py:35:1: E402 Module level import not at top of file
|
||||
| ^^^^^^^^ E402
|
||||
|
|
||||
|
||||
E402.py:45:1: E402 Module level import not at top of file
|
||||
E402_0.py:45:1: E402 Module level import not at top of file
|
||||
|
|
||||
43 | import j
|
||||
44 |
|
||||
@@ -47,7 +47,7 @@ E402.py:45:1: E402 Module level import not at top of file
|
||||
| ^^^^^^^^ E402
|
||||
|
|
||||
|
||||
E402.py:45:11: E402 Module level import not at top of file
|
||||
E402_0.py:45:11: E402 Module level import not at top of file
|
||||
|
|
||||
43 | import j
|
||||
44 |
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
|
||||
---
|
||||
E402_1.py:5:1: E402 Module level import not at top of file
|
||||
|
|
||||
3 | """Some other docstring."""
|
||||
4 |
|
||||
5 | import b
|
||||
| ^^^^^^^^ E402
|
||||
6 |
|
||||
7 | """Some other docstring."""
|
||||
|
|
||||
|
||||
E402_1.py:9:1: E402 Module level import not at top of file
|
||||
|
|
||||
7 | """Some other docstring."""
|
||||
8 |
|
||||
9 | import c
|
||||
| ^^^^^^^^ E402
|
||||
|
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
|
||||
---
|
||||
E402.py:35:1: E402 Module level import not at top of file
|
||||
E402_0.py:35:1: E402 Module level import not at top of file
|
||||
|
|
||||
33 | __some__magic = 1
|
||||
34 |
|
||||
@@ -9,7 +9,7 @@ E402.py:35:1: E402 Module level import not at top of file
|
||||
| ^^^^^^^^ E402
|
||||
|
|
||||
|
||||
E402.py:45:1: E402 Module level import not at top of file
|
||||
E402_0.py:45:1: E402 Module level import not at top of file
|
||||
|
|
||||
43 | import j
|
||||
44 |
|
||||
@@ -17,7 +17,7 @@ E402.py:45:1: E402 Module level import not at top of file
|
||||
| ^^^^^^^^ E402
|
||||
|
|
||||
|
||||
E402.py:45:11: E402 Module level import not at top of file
|
||||
E402_0.py:45:11: E402 Module level import not at top of file
|
||||
|
|
||||
43 | import j
|
||||
44 |
|
||||
@@ -23,7 +23,7 @@ mod tests {
|
||||
use ruff_source_file::Locator;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::linter::{check_path, LinterResult};
|
||||
use crate::linter::{check_path, LinterResult, TokenSource};
|
||||
use crate::registry::{AsRule, Linter, Rule};
|
||||
use crate::rules::pyflakes;
|
||||
use crate::settings::types::PreviewMode;
|
||||
@@ -55,7 +55,8 @@ mod tests {
|
||||
#[test_case(Rule::UnusedImport, Path::new("F401_20.py"))]
|
||||
#[test_case(Rule::ImportShadowedByLoopVar, Path::new("F402.py"))]
|
||||
#[test_case(Rule::UndefinedLocalWithImportStar, Path::new("F403.py"))]
|
||||
#[test_case(Rule::LateFutureImport, Path::new("F404.py"))]
|
||||
#[test_case(Rule::LateFutureImport, Path::new("F404_0.py"))]
|
||||
#[test_case(Rule::LateFutureImport, Path::new("F404_1.py"))]
|
||||
#[test_case(Rule::UndefinedLocalWithImportStarUsage, Path::new("F405.py"))]
|
||||
#[test_case(Rule::UndefinedLocalWithNestedImportStarUsage, Path::new("F406.py"))]
|
||||
#[test_case(Rule::FutureFeatureNotDefined, Path::new("F407.py"))]
|
||||
@@ -142,6 +143,7 @@ mod tests {
|
||||
#[test_case(Rule::UndefinedName, Path::new("F821_21.py"))]
|
||||
#[test_case(Rule::UndefinedName, Path::new("F821_22.ipynb"))]
|
||||
#[test_case(Rule::UndefinedName, Path::new("F821_23.py"))]
|
||||
#[test_case(Rule::UndefinedName, Path::new("F821_24.py"))]
|
||||
#[test_case(Rule::UndefinedExport, Path::new("F822_0.py"))]
|
||||
#[test_case(Rule::UndefinedExport, Path::new("F822_1.py"))]
|
||||
#[test_case(Rule::UndefinedExport, Path::new("F822_2.py"))]
|
||||
@@ -558,7 +560,6 @@ mod tests {
|
||||
} = check_path(
|
||||
Path::new("<filename>"),
|
||||
None,
|
||||
tokens,
|
||||
&locator,
|
||||
&stylist,
|
||||
&indexer,
|
||||
@@ -567,6 +568,7 @@ mod tests {
|
||||
flags::Noqa::Enabled,
|
||||
&source_kind,
|
||||
source_type,
|
||||
TokenSource::Tokens(tokens),
|
||||
);
|
||||
diagnostics.sort_by_key(Ranged::start);
|
||||
let actual = diagnostics
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pyflakes/mod.rs
|
||||
---
|
||||
F404.py:6:1: F404 `from __future__` imports must occur at the beginning of the file
|
||||
F404_0.py:6:1: F404 `from __future__` imports must occur at the beginning of the file
|
||||
|
|
||||
4 | from collections import namedtuple
|
||||
5 |
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pyflakes/mod.rs
|
||||
---
|
||||
F404_1.py:5:1: F404 `from __future__` imports must occur at the beginning of the file
|
||||
|
|
||||
3 | """Non-docstring"""
|
||||
4 |
|
||||
5 | from __future__ import absolute_import
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ F404
|
||||
|
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pyflakes/mod.rs
|
||||
---
|
||||
|
||||
@@ -7,7 +7,6 @@ use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::snippet::SourceCodeSnippet;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of the known pre-Python 2.5 ternary syntax.
|
||||
@@ -123,11 +122,9 @@ pub(crate) fn and_or_ternary(checker: &mut Checker, bool_op: &ExprBoolOp) {
|
||||
},
|
||||
bool_op.range,
|
||||
);
|
||||
if checker.enabled(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
ternary,
|
||||
bool_op.range,
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
ternary,
|
||||
bool_op.range,
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -137,10 +137,9 @@ enum FormatContext {
|
||||
Accessed,
|
||||
}
|
||||
|
||||
/// Given an [`Expr`], format it for use in a formatted expression within an f-string.
|
||||
fn formatted_expr<'a>(expr: &Expr, context: FormatContext, locator: &Locator<'a>) -> Cow<'a, str> {
|
||||
let text = locator.slice(expr);
|
||||
let parenthesize = match (context, expr) {
|
||||
/// Returns `true` if the expression should be parenthesized when used in an f-string.
|
||||
fn parenthesize(expr: &Expr, text: &str, context: FormatContext) -> bool {
|
||||
match (context, expr) {
|
||||
// E.g., `x + y` should be parenthesized in `f"{(x + y)[0]}"`.
|
||||
(
|
||||
FormatContext::Accessed,
|
||||
@@ -173,9 +172,44 @@ fn formatted_expr<'a>(expr: &Expr, context: FormatContext, locator: &Locator<'a>
|
||||
| Expr::SetComp(_)
|
||||
| Expr::DictComp(_),
|
||||
) => true,
|
||||
(_, Expr::Subscript(ast::ExprSubscript { value, .. })) => {
|
||||
matches!(
|
||||
value.as_ref(),
|
||||
Expr::GeneratorExp(_)
|
||||
| Expr::Dict(_)
|
||||
| Expr::Set(_)
|
||||
| Expr::SetComp(_)
|
||||
| Expr::DictComp(_)
|
||||
)
|
||||
}
|
||||
(_, Expr::Attribute(ast::ExprAttribute { value, .. })) => {
|
||||
matches!(
|
||||
value.as_ref(),
|
||||
Expr::GeneratorExp(_)
|
||||
| Expr::Dict(_)
|
||||
| Expr::Set(_)
|
||||
| Expr::SetComp(_)
|
||||
| Expr::DictComp(_)
|
||||
)
|
||||
}
|
||||
(_, Expr::Call(ast::ExprCall { func, .. })) => {
|
||||
matches!(
|
||||
func.as_ref(),
|
||||
Expr::GeneratorExp(_)
|
||||
| Expr::Dict(_)
|
||||
| Expr::Set(_)
|
||||
| Expr::SetComp(_)
|
||||
| Expr::DictComp(_)
|
||||
)
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
if parenthesize && !text.starts_with('(') && !text.ends_with(')') {
|
||||
}
|
||||
}
|
||||
|
||||
/// Given an [`Expr`], format it for use in a formatted expression within an f-string.
|
||||
fn formatted_expr<'a>(expr: &Expr, context: FormatContext, locator: &Locator<'a>) -> Cow<'a, str> {
|
||||
let text = locator.slice(expr);
|
||||
if parenthesize(expr, text, context) && !(text.starts_with('(') && text.ends_with(')')) {
|
||||
Cow::Owned(format!("({text})"))
|
||||
} else {
|
||||
Cow::Borrowed(text)
|
||||
|
||||
@@ -1,284 +1,302 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pyupgrade/mod.rs
|
||||
---
|
||||
UP025.py:2:5: UP025 [*] Remove unicode literals from strings
|
||||
UP025.py:1:1: UP025 [*] Remove unicode literals from strings
|
||||
|
|
||||
1 | # These should change
|
||||
2 | x = u"Hello"
|
||||
| ^^^^^^^^ UP025
|
||||
3 |
|
||||
4 | u'world'
|
||||
|
|
||||
= help: Remove unicode prefix
|
||||
|
||||
ℹ Safe fix
|
||||
1 1 | # These should change
|
||||
2 |-x = u"Hello"
|
||||
2 |+x = "Hello"
|
||||
3 3 |
|
||||
4 4 | u'world'
|
||||
5 5 |
|
||||
|
||||
UP025.py:4:1: UP025 [*] Remove unicode literals from strings
|
||||
|
|
||||
2 | x = u"Hello"
|
||||
3 |
|
||||
4 | u'world'
|
||||
1 | u"Hello"
|
||||
| ^^^^^^^^ UP025
|
||||
5 |
|
||||
6 | print(u"Hello")
|
||||
2 |
|
||||
3 | x = u"Hello" # UP025
|
||||
|
|
||||
= help: Remove unicode prefix
|
||||
|
||||
ℹ Safe fix
|
||||
1 1 | # These should change
|
||||
2 2 | x = u"Hello"
|
||||
3 3 |
|
||||
4 |-u'world'
|
||||
4 |+'world'
|
||||
5 5 |
|
||||
6 6 | print(u"Hello")
|
||||
7 7 |
|
||||
1 |-u"Hello"
|
||||
1 |+"Hello"
|
||||
2 2 |
|
||||
3 3 | x = u"Hello" # UP025
|
||||
4 4 |
|
||||
|
||||
UP025.py:6:7: UP025 [*] Remove unicode literals from strings
|
||||
UP025.py:3:5: UP025 [*] Remove unicode literals from strings
|
||||
|
|
||||
4 | u'world'
|
||||
5 |
|
||||
6 | print(u"Hello")
|
||||
1 | u"Hello"
|
||||
2 |
|
||||
3 | x = u"Hello" # UP025
|
||||
| ^^^^^^^^ UP025
|
||||
4 |
|
||||
5 | u'world' # UP025
|
||||
|
|
||||
= help: Remove unicode prefix
|
||||
|
||||
ℹ Safe fix
|
||||
1 1 | u"Hello"
|
||||
2 2 |
|
||||
3 |-x = u"Hello" # UP025
|
||||
3 |+x = "Hello" # UP025
|
||||
4 4 |
|
||||
5 5 | u'world' # UP025
|
||||
6 6 |
|
||||
|
||||
UP025.py:5:1: UP025 [*] Remove unicode literals from strings
|
||||
|
|
||||
3 | x = u"Hello" # UP025
|
||||
4 |
|
||||
5 | u'world' # UP025
|
||||
| ^^^^^^^^ UP025
|
||||
6 |
|
||||
7 | print(u"Hello") # UP025
|
||||
|
|
||||
= help: Remove unicode prefix
|
||||
|
||||
ℹ Safe fix
|
||||
2 2 |
|
||||
3 3 | x = u"Hello" # UP025
|
||||
4 4 |
|
||||
5 |-u'world' # UP025
|
||||
5 |+'world' # UP025
|
||||
6 6 |
|
||||
7 7 | print(u"Hello") # UP025
|
||||
8 8 |
|
||||
|
||||
UP025.py:7:7: UP025 [*] Remove unicode literals from strings
|
||||
|
|
||||
5 | u'world' # UP025
|
||||
6 |
|
||||
7 | print(u"Hello") # UP025
|
||||
| ^^^^^^^^ UP025
|
||||
7 |
|
||||
8 | print(u'world')
|
||||
8 |
|
||||
9 | print(u'world') # UP025
|
||||
|
|
||||
= help: Remove unicode prefix
|
||||
|
||||
ℹ Safe fix
|
||||
3 3 |
|
||||
4 4 | u'world'
|
||||
5 5 |
|
||||
6 |-print(u"Hello")
|
||||
6 |+print("Hello")
|
||||
7 7 |
|
||||
8 8 | print(u'world')
|
||||
9 9 |
|
||||
4 4 |
|
||||
5 5 | u'world' # UP025
|
||||
6 6 |
|
||||
7 |-print(u"Hello") # UP025
|
||||
7 |+print("Hello") # UP025
|
||||
8 8 |
|
||||
9 9 | print(u'world') # UP025
|
||||
10 10 |
|
||||
|
||||
UP025.py:8:7: UP025 [*] Remove unicode literals from strings
|
||||
UP025.py:9:7: UP025 [*] Remove unicode literals from strings
|
||||
|
|
||||
6 | print(u"Hello")
|
||||
7 |
|
||||
8 | print(u'world')
|
||||
7 | print(u"Hello") # UP025
|
||||
8 |
|
||||
9 | print(u'world') # UP025
|
||||
| ^^^^^^^^ UP025
|
||||
9 |
|
||||
10 | import foo
|
||||
10 |
|
||||
11 | import foo
|
||||
|
|
||||
= help: Remove unicode prefix
|
||||
|
||||
ℹ Safe fix
|
||||
5 5 |
|
||||
6 6 | print(u"Hello")
|
||||
7 7 |
|
||||
8 |-print(u'world')
|
||||
8 |+print('world')
|
||||
9 9 |
|
||||
10 10 | import foo
|
||||
11 11 |
|
||||
6 6 |
|
||||
7 7 | print(u"Hello") # UP025
|
||||
8 8 |
|
||||
9 |-print(u'world') # UP025
|
||||
9 |+print('world') # UP025
|
||||
10 10 |
|
||||
11 11 | import foo
|
||||
12 12 |
|
||||
|
||||
UP025.py:12:5: UP025 [*] Remove unicode literals from strings
|
||||
UP025.py:13:5: UP025 [*] Remove unicode literals from strings
|
||||
|
|
||||
10 | import foo
|
||||
11 |
|
||||
12 | foo(u"Hello", U"world", a=u"Hello", b=u"world")
|
||||
11 | import foo
|
||||
12 |
|
||||
13 | foo(u"Hello", U"world", a=u"Hello", b=u"world") # UP025
|
||||
| ^^^^^^^^ UP025
|
||||
13 |
|
||||
14 | # These should stay quoted they way they are
|
||||
14 |
|
||||
15 | # Retain quotes when fixing.
|
||||
|
|
||||
= help: Remove unicode prefix
|
||||
|
||||
ℹ Safe fix
|
||||
9 9 |
|
||||
10 10 | import foo
|
||||
11 11 |
|
||||
12 |-foo(u"Hello", U"world", a=u"Hello", b=u"world")
|
||||
12 |+foo("Hello", U"world", a=u"Hello", b=u"world")
|
||||
13 13 |
|
||||
14 14 | # These should stay quoted they way they are
|
||||
15 15 |
|
||||
10 10 |
|
||||
11 11 | import foo
|
||||
12 12 |
|
||||
13 |-foo(u"Hello", U"world", a=u"Hello", b=u"world") # UP025
|
||||
13 |+foo("Hello", U"world", a=u"Hello", b=u"world") # UP025
|
||||
14 14 |
|
||||
15 15 | # Retain quotes when fixing.
|
||||
16 16 | x = u'hello' # UP025
|
||||
|
||||
UP025.py:12:15: UP025 [*] Remove unicode literals from strings
|
||||
UP025.py:13:15: UP025 [*] Remove unicode literals from strings
|
||||
|
|
||||
10 | import foo
|
||||
11 |
|
||||
12 | foo(u"Hello", U"world", a=u"Hello", b=u"world")
|
||||
11 | import foo
|
||||
12 |
|
||||
13 | foo(u"Hello", U"world", a=u"Hello", b=u"world") # UP025
|
||||
| ^^^^^^^^ UP025
|
||||
13 |
|
||||
14 | # These should stay quoted they way they are
|
||||
14 |
|
||||
15 | # Retain quotes when fixing.
|
||||
|
|
||||
= help: Remove unicode prefix
|
||||
|
||||
ℹ Safe fix
|
||||
9 9 |
|
||||
10 10 | import foo
|
||||
11 11 |
|
||||
12 |-foo(u"Hello", U"world", a=u"Hello", b=u"world")
|
||||
12 |+foo(u"Hello", "world", a=u"Hello", b=u"world")
|
||||
13 13 |
|
||||
14 14 | # These should stay quoted they way they are
|
||||
15 15 |
|
||||
10 10 |
|
||||
11 11 | import foo
|
||||
12 12 |
|
||||
13 |-foo(u"Hello", U"world", a=u"Hello", b=u"world") # UP025
|
||||
13 |+foo(u"Hello", "world", a=u"Hello", b=u"world") # UP025
|
||||
14 14 |
|
||||
15 15 | # Retain quotes when fixing.
|
||||
16 16 | x = u'hello' # UP025
|
||||
|
||||
UP025.py:12:27: UP025 [*] Remove unicode literals from strings
|
||||
UP025.py:13:27: UP025 [*] Remove unicode literals from strings
|
||||
|
|
||||
10 | import foo
|
||||
11 |
|
||||
12 | foo(u"Hello", U"world", a=u"Hello", b=u"world")
|
||||
11 | import foo
|
||||
12 |
|
||||
13 | foo(u"Hello", U"world", a=u"Hello", b=u"world") # UP025
|
||||
| ^^^^^^^^ UP025
|
||||
13 |
|
||||
14 | # These should stay quoted they way they are
|
||||
14 |
|
||||
15 | # Retain quotes when fixing.
|
||||
|
|
||||
= help: Remove unicode prefix
|
||||
|
||||
ℹ Safe fix
|
||||
9 9 |
|
||||
10 10 | import foo
|
||||
11 11 |
|
||||
12 |-foo(u"Hello", U"world", a=u"Hello", b=u"world")
|
||||
12 |+foo(u"Hello", U"world", a="Hello", b=u"world")
|
||||
13 13 |
|
||||
14 14 | # These should stay quoted they way they are
|
||||
15 15 |
|
||||
10 10 |
|
||||
11 11 | import foo
|
||||
12 12 |
|
||||
13 |-foo(u"Hello", U"world", a=u"Hello", b=u"world") # UP025
|
||||
13 |+foo(u"Hello", U"world", a="Hello", b=u"world") # UP025
|
||||
14 14 |
|
||||
15 15 | # Retain quotes when fixing.
|
||||
16 16 | x = u'hello' # UP025
|
||||
|
||||
UP025.py:12:39: UP025 [*] Remove unicode literals from strings
|
||||
UP025.py:13:39: UP025 [*] Remove unicode literals from strings
|
||||
|
|
||||
10 | import foo
|
||||
11 |
|
||||
12 | foo(u"Hello", U"world", a=u"Hello", b=u"world")
|
||||
11 | import foo
|
||||
12 |
|
||||
13 | foo(u"Hello", U"world", a=u"Hello", b=u"world") # UP025
|
||||
| ^^^^^^^^ UP025
|
||||
13 |
|
||||
14 | # These should stay quoted they way they are
|
||||
14 |
|
||||
15 | # Retain quotes when fixing.
|
||||
|
|
||||
= help: Remove unicode prefix
|
||||
|
||||
ℹ Safe fix
|
||||
9 9 |
|
||||
10 10 | import foo
|
||||
11 11 |
|
||||
12 |-foo(u"Hello", U"world", a=u"Hello", b=u"world")
|
||||
12 |+foo(u"Hello", U"world", a=u"Hello", b="world")
|
||||
13 13 |
|
||||
14 14 | # These should stay quoted they way they are
|
||||
15 15 |
|
||||
10 10 |
|
||||
11 11 | import foo
|
||||
12 12 |
|
||||
13 |-foo(u"Hello", U"world", a=u"Hello", b=u"world") # UP025
|
||||
13 |+foo(u"Hello", U"world", a=u"Hello", b="world") # UP025
|
||||
14 14 |
|
||||
15 15 | # Retain quotes when fixing.
|
||||
16 16 | x = u'hello' # UP025
|
||||
|
||||
UP025.py:16:5: UP025 [*] Remove unicode literals from strings
|
||||
|
|
||||
14 | # These should stay quoted they way they are
|
||||
15 |
|
||||
16 | x = u'hello'
|
||||
15 | # Retain quotes when fixing.
|
||||
16 | x = u'hello' # UP025
|
||||
| ^^^^^^^^ UP025
|
||||
17 | x = u"""hello"""
|
||||
18 | x = u'''hello'''
|
||||
17 | x = u"""hello""" # UP025
|
||||
18 | x = u'''hello''' # UP025
|
||||
|
|
||||
= help: Remove unicode prefix
|
||||
|
||||
ℹ Safe fix
|
||||
13 13 |
|
||||
14 14 | # These should stay quoted they way they are
|
||||
15 15 |
|
||||
16 |-x = u'hello'
|
||||
16 |+x = 'hello'
|
||||
17 17 | x = u"""hello"""
|
||||
18 18 | x = u'''hello'''
|
||||
19 19 | x = u'Hello "World"'
|
||||
13 13 | foo(u"Hello", U"world", a=u"Hello", b=u"world") # UP025
|
||||
14 14 |
|
||||
15 15 | # Retain quotes when fixing.
|
||||
16 |-x = u'hello' # UP025
|
||||
16 |+x = 'hello' # UP025
|
||||
17 17 | x = u"""hello""" # UP025
|
||||
18 18 | x = u'''hello''' # UP025
|
||||
19 19 | x = u'Hello "World"' # UP025
|
||||
|
||||
UP025.py:17:5: UP025 [*] Remove unicode literals from strings
|
||||
|
|
||||
16 | x = u'hello'
|
||||
17 | x = u"""hello"""
|
||||
15 | # Retain quotes when fixing.
|
||||
16 | x = u'hello' # UP025
|
||||
17 | x = u"""hello""" # UP025
|
||||
| ^^^^^^^^^^^^ UP025
|
||||
18 | x = u'''hello'''
|
||||
19 | x = u'Hello "World"'
|
||||
18 | x = u'''hello''' # UP025
|
||||
19 | x = u'Hello "World"' # UP025
|
||||
|
|
||||
= help: Remove unicode prefix
|
||||
|
||||
ℹ Safe fix
|
||||
14 14 | # These should stay quoted they way they are
|
||||
15 15 |
|
||||
16 16 | x = u'hello'
|
||||
17 |-x = u"""hello"""
|
||||
17 |+x = """hello"""
|
||||
18 18 | x = u'''hello'''
|
||||
19 19 | x = u'Hello "World"'
|
||||
14 14 |
|
||||
15 15 | # Retain quotes when fixing.
|
||||
16 16 | x = u'hello' # UP025
|
||||
17 |-x = u"""hello""" # UP025
|
||||
17 |+x = """hello""" # UP025
|
||||
18 18 | x = u'''hello''' # UP025
|
||||
19 19 | x = u'Hello "World"' # UP025
|
||||
20 20 |
|
||||
|
||||
UP025.py:18:5: UP025 [*] Remove unicode literals from strings
|
||||
|
|
||||
16 | x = u'hello'
|
||||
17 | x = u"""hello"""
|
||||
18 | x = u'''hello'''
|
||||
16 | x = u'hello' # UP025
|
||||
17 | x = u"""hello""" # UP025
|
||||
18 | x = u'''hello''' # UP025
|
||||
| ^^^^^^^^^^^^ UP025
|
||||
19 | x = u'Hello "World"'
|
||||
19 | x = u'Hello "World"' # UP025
|
||||
|
|
||||
= help: Remove unicode prefix
|
||||
|
||||
ℹ Safe fix
|
||||
15 15 |
|
||||
16 16 | x = u'hello'
|
||||
17 17 | x = u"""hello"""
|
||||
18 |-x = u'''hello'''
|
||||
18 |+x = '''hello'''
|
||||
19 19 | x = u'Hello "World"'
|
||||
15 15 | # Retain quotes when fixing.
|
||||
16 16 | x = u'hello' # UP025
|
||||
17 17 | x = u"""hello""" # UP025
|
||||
18 |-x = u'''hello''' # UP025
|
||||
18 |+x = '''hello''' # UP025
|
||||
19 19 | x = u'Hello "World"' # UP025
|
||||
20 20 |
|
||||
21 21 | # These should not change
|
||||
21 21 | u = "Hello" # OK
|
||||
|
||||
UP025.py:19:5: UP025 [*] Remove unicode literals from strings
|
||||
|
|
||||
17 | x = u"""hello"""
|
||||
18 | x = u'''hello'''
|
||||
19 | x = u'Hello "World"'
|
||||
17 | x = u"""hello""" # UP025
|
||||
18 | x = u'''hello''' # UP025
|
||||
19 | x = u'Hello "World"' # UP025
|
||||
| ^^^^^^^^^^^^^^^^ UP025
|
||||
20 |
|
||||
21 | # These should not change
|
||||
21 | u = "Hello" # OK
|
||||
|
|
||||
= help: Remove unicode prefix
|
||||
|
||||
ℹ Safe fix
|
||||
16 16 | x = u'hello'
|
||||
17 17 | x = u"""hello"""
|
||||
18 18 | x = u'''hello'''
|
||||
19 |-x = u'Hello "World"'
|
||||
19 |+x = 'Hello "World"'
|
||||
16 16 | x = u'hello' # UP025
|
||||
17 17 | x = u"""hello""" # UP025
|
||||
18 18 | x = u'''hello''' # UP025
|
||||
19 |-x = u'Hello "World"' # UP025
|
||||
19 |+x = 'Hello "World"' # UP025
|
||||
20 20 |
|
||||
21 21 | # These should not change
|
||||
22 22 | u = "Hello"
|
||||
21 21 | u = "Hello" # OK
|
||||
22 22 | u = u # OK
|
||||
|
||||
UP025.py:29:7: UP025 [*] Remove unicode literals from strings
|
||||
UP025.py:27:7: UP025 [*] Remove unicode literals from strings
|
||||
|
|
||||
27 | return"Hello"
|
||||
28 |
|
||||
29 | f"foo"u"bar"
|
||||
25 | return"Hello" # OK
|
||||
26 |
|
||||
27 | f"foo"u"bar" # OK
|
||||
| ^^^^^^ UP025
|
||||
30 | f"foo" u"bar"
|
||||
28 | f"foo" u"bar" # OK
|
||||
|
|
||||
= help: Remove unicode prefix
|
||||
|
||||
ℹ Safe fix
|
||||
26 26 | def hello():
|
||||
27 27 | return"Hello"
|
||||
28 28 |
|
||||
29 |-f"foo"u"bar"
|
||||
29 |+f"foo""bar"
|
||||
30 30 | f"foo" u"bar"
|
||||
24 24 | def hello():
|
||||
25 25 | return"Hello" # OK
|
||||
26 26 |
|
||||
27 |-f"foo"u"bar" # OK
|
||||
27 |+f"foo""bar" # OK
|
||||
28 28 | f"foo" u"bar" # OK
|
||||
|
||||
UP025.py:30:8: UP025 [*] Remove unicode literals from strings
|
||||
UP025.py:28:8: UP025 [*] Remove unicode literals from strings
|
||||
|
|
||||
29 | f"foo"u"bar"
|
||||
30 | f"foo" u"bar"
|
||||
27 | f"foo"u"bar" # OK
|
||||
28 | f"foo" u"bar" # OK
|
||||
| ^^^^^^ UP025
|
||||
|
|
||||
= help: Remove unicode prefix
|
||||
|
||||
ℹ Safe fix
|
||||
27 27 | return"Hello"
|
||||
28 28 |
|
||||
29 29 | f"foo"u"bar"
|
||||
30 |-f"foo" u"bar"
|
||||
30 |+f"foo" "bar"
|
||||
25 25 | return"Hello" # OK
|
||||
26 26 |
|
||||
27 27 | f"foo"u"bar" # OK
|
||||
28 |-f"foo" u"bar" # OK
|
||||
28 |+f"foo" "bar" # OK
|
||||
|
||||
|
||||
|
||||
@@ -1141,6 +1141,7 @@ UP032_0.py:240:1: UP032 [*] Use f-string instead of `format` call
|
||||
243 |+)
|
||||
244 244 |
|
||||
245 245 | ("{}" "{{{}}}").format(a, b)
|
||||
246 246 |
|
||||
|
||||
UP032_0.py:245:1: UP032 [*] Use f-string instead of `format` call
|
||||
|
|
||||
@@ -1148,6 +1149,8 @@ UP032_0.py:245:1: UP032 [*] Use f-string instead of `format` call
|
||||
244 |
|
||||
245 | ("{}" "{{{}}}").format(a, b)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UP032
|
||||
246 |
|
||||
247 | # The dictionary should be parenthesized.
|
||||
|
|
||||
= help: Convert to f-string
|
||||
|
||||
@@ -1157,5 +1160,63 @@ UP032_0.py:245:1: UP032 [*] Use f-string instead of `format` call
|
||||
244 244 |
|
||||
245 |-("{}" "{{{}}}").format(a, b)
|
||||
245 |+(f"{a}" f"{{{b}}}")
|
||||
246 246 |
|
||||
247 247 | # The dictionary should be parenthesized.
|
||||
248 248 | "{}".format({0: 1}[0])
|
||||
|
||||
UP032_0.py:248:1: UP032 [*] Use f-string instead of `format` call
|
||||
|
|
||||
247 | # The dictionary should be parenthesized.
|
||||
248 | "{}".format({0: 1}[0])
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ UP032
|
||||
249 |
|
||||
250 | # The dictionary should be parenthesized.
|
||||
|
|
||||
= help: Convert to f-string
|
||||
|
||||
ℹ Safe fix
|
||||
245 245 | ("{}" "{{{}}}").format(a, b)
|
||||
246 246 |
|
||||
247 247 | # The dictionary should be parenthesized.
|
||||
248 |-"{}".format({0: 1}[0])
|
||||
248 |+f"{({0: 1}[0])}"
|
||||
249 249 |
|
||||
250 250 | # The dictionary should be parenthesized.
|
||||
251 251 | "{}".format({0: 1}.bar)
|
||||
|
||||
UP032_0.py:251:1: UP032 [*] Use f-string instead of `format` call
|
||||
|
|
||||
250 | # The dictionary should be parenthesized.
|
||||
251 | "{}".format({0: 1}.bar)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ UP032
|
||||
252 |
|
||||
253 | # The dictionary should be parenthesized.
|
||||
|
|
||||
= help: Convert to f-string
|
||||
|
||||
ℹ Safe fix
|
||||
248 248 | "{}".format({0: 1}[0])
|
||||
249 249 |
|
||||
250 250 | # The dictionary should be parenthesized.
|
||||
251 |-"{}".format({0: 1}.bar)
|
||||
251 |+f"{({0: 1}.bar)}"
|
||||
252 252 |
|
||||
253 253 | # The dictionary should be parenthesized.
|
||||
254 254 | "{}".format({0: 1}())
|
||||
|
||||
UP032_0.py:254:1: UP032 [*] Use f-string instead of `format` call
|
||||
|
|
||||
253 | # The dictionary should be parenthesized.
|
||||
254 | "{}".format({0: 1}())
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ UP032
|
||||
|
|
||||
= help: Convert to f-string
|
||||
|
||||
ℹ Safe fix
|
||||
251 251 | "{}".format({0: 1}.bar)
|
||||
252 252 |
|
||||
253 253 | # The dictionary should be parenthesized.
|
||||
254 |-"{}".format({0: 1}())
|
||||
254 |+f"{({0: 1}())}"
|
||||
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ mod tests {
|
||||
#[test_case(Rule::QuadraticListSummation, Path::new("RUF017_0.py"))]
|
||||
#[test_case(Rule::AssignmentInAssert, Path::new("RUF018.py"))]
|
||||
#[test_case(Rule::UnnecessaryKeyCheck, Path::new("RUF019.py"))]
|
||||
#[test_case(Rule::NeverUnion, Path::new("RUF020.py"))]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
|
||||
@@ -112,11 +112,15 @@ pub(crate) fn asyncio_dangling_binding(
|
||||
for binding_id in scope.binding_ids() {
|
||||
// If the binding itself is used, or it's not an assignment, skip it.
|
||||
let binding = semantic.binding(binding_id);
|
||||
if binding.is_used() || !binding.kind.is_assignment() {
|
||||
if binding.is_used()
|
||||
|| binding.is_global()
|
||||
|| binding.is_nonlocal()
|
||||
|| !binding.kind.is_assignment()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, any dangling tasks, including those that are shadowed, as in:
|
||||
// Otherwise, flag any dangling tasks, including those that are shadowed, as in:
|
||||
// ```python
|
||||
// if x > 0:
|
||||
// task = asyncio.create_task(make_request())
|
||||
@@ -127,7 +131,11 @@ pub(crate) fn asyncio_dangling_binding(
|
||||
std::iter::successors(Some(binding_id), |id| semantic.shadowed_binding(*id))
|
||||
{
|
||||
let binding = semantic.binding(binding_id);
|
||||
if binding.is_used() || !binding.kind.is_assignment() {
|
||||
if binding.is_used()
|
||||
|| binding.is_global()
|
||||
|| binding.is_nonlocal()
|
||||
|| !binding.kind.is_assignment()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,9 @@ pub(crate) use invalid_index_type::*;
|
||||
pub(crate) use invalid_pyproject_toml::*;
|
||||
pub(crate) use mutable_class_default::*;
|
||||
pub(crate) use mutable_dataclass_default::*;
|
||||
pub(crate) use never_union::*;
|
||||
pub(crate) use pairwise_over_zipped::*;
|
||||
pub(crate) use quadratic_list_summation::*;
|
||||
pub(crate) use static_key_dict_comprehension::*;
|
||||
pub(crate) use unnecessary_iterable_allocation_for_first_element::*;
|
||||
pub(crate) use unnecessary_key_check::*;
|
||||
@@ -30,6 +32,7 @@ mod invalid_index_type;
|
||||
mod invalid_pyproject_toml;
|
||||
mod mutable_class_default;
|
||||
mod mutable_dataclass_default;
|
||||
mod never_union;
|
||||
mod pairwise_over_zipped;
|
||||
mod static_key_dict_comprehension;
|
||||
mod unnecessary_iterable_allocation_for_first_element;
|
||||
@@ -44,6 +47,5 @@ pub(crate) enum Context {
|
||||
Docstring,
|
||||
Comment,
|
||||
}
|
||||
pub(crate) use quadratic_list_summation::*;
|
||||
|
||||
mod quadratic_list_summation;
|
||||
|
||||
212
crates/ruff_linter/src/rules/ruff/rules/never_union.rs
Normal file
212
crates/ruff_linter/src/rules/ruff/rules/never_union.rs
Normal file
@@ -0,0 +1,212 @@
|
||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::{self as ast, Expr, Operator};
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `typing.NoReturn` and `typing.Never` in union types.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// `typing.NoReturn` and `typing.Never` are special types, used to indicate
|
||||
/// that a function never returns, or that a type has no values.
|
||||
///
|
||||
/// Including `typing.NoReturn` or `typing.Never` in a union type is redundant,
|
||||
/// as, e.g., `typing.Never | T` is equivalent to `T`.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// from typing import Never
|
||||
///
|
||||
///
|
||||
/// def func() -> Never | int:
|
||||
/// ...
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// def func() -> int:
|
||||
/// ...
|
||||
/// ```
|
||||
///
|
||||
/// ## Options
|
||||
/// - [Python documentation: `typing.Never`](https://docs.python.org/3/library/typing.html#typing.Never)
|
||||
/// - [Python documentation: `typing.NoReturn`](https://docs.python.org/3/library/typing.html#typing.NoReturn)
|
||||
#[violation]
|
||||
pub struct NeverUnion {
|
||||
never_like: NeverLike,
|
||||
union_like: UnionLike,
|
||||
}
|
||||
|
||||
impl AlwaysFixableViolation for NeverUnion {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let Self {
|
||||
never_like,
|
||||
union_like,
|
||||
} = self;
|
||||
match union_like {
|
||||
UnionLike::BinOp => {
|
||||
format!("`{never_like} | T` is equivalent to `T`")
|
||||
}
|
||||
UnionLike::TypingUnion => {
|
||||
format!("`Union[{never_like}, T]` is equivalent to `T`")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> String {
|
||||
let Self { never_like, .. } = self;
|
||||
format!("Remove `{never_like}`")
|
||||
}
|
||||
}
|
||||
|
||||
/// RUF020
|
||||
pub(crate) fn never_union(checker: &mut Checker, expr: &Expr) {
|
||||
match expr {
|
||||
// Ex) `typing.NoReturn | int`
|
||||
Expr::BinOp(ast::ExprBinOp {
|
||||
op: Operator::BitOr,
|
||||
left,
|
||||
right,
|
||||
range: _,
|
||||
}) => {
|
||||
// Analyze the left-hand side of the `|` operator.
|
||||
if let Some(never_like) = NeverLike::from_expr(left, checker.semantic()) {
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
NeverUnion {
|
||||
never_like,
|
||||
union_like: UnionLike::BinOp,
|
||||
},
|
||||
left.range(),
|
||||
);
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
checker.locator().slice(right.as_ref()).to_string(),
|
||||
expr.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
// Analyze the right-hand side of the `|` operator.
|
||||
if let Some(never_like) = NeverLike::from_expr(right, checker.semantic()) {
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
NeverUnion {
|
||||
never_like,
|
||||
union_like: UnionLike::BinOp,
|
||||
},
|
||||
right.range(),
|
||||
);
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
checker.locator().slice(left.as_ref()).to_string(),
|
||||
expr.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
// Ex) `typing.Union[typing.NoReturn, int]`
|
||||
Expr::Subscript(ast::ExprSubscript {
|
||||
value,
|
||||
slice,
|
||||
ctx: _,
|
||||
range: _,
|
||||
}) if checker.semantic().match_typing_expr(value, "Union") => {
|
||||
let Expr::Tuple(ast::ExprTuple {
|
||||
elts,
|
||||
ctx: _,
|
||||
range: _,
|
||||
}) = slice.as_ref()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Analyze each element of the `Union`.
|
||||
for elt in elts {
|
||||
if let Some(never_like) = NeverLike::from_expr(elt, checker.semantic()) {
|
||||
// Collect the other elements of the `Union`.
|
||||
let rest = elts
|
||||
.iter()
|
||||
.filter(|other| *other != elt)
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Ignore, e.g., `typing.Union[typing.NoReturn]`.
|
||||
if rest.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
NeverUnion {
|
||||
never_like,
|
||||
union_like: UnionLike::TypingUnion,
|
||||
},
|
||||
elt.range(),
|
||||
);
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
if let [only] = rest.as_slice() {
|
||||
// Ex) `typing.Union[typing.NoReturn, int]` -> `int`
|
||||
checker.locator().slice(only).to_string()
|
||||
} else {
|
||||
// Ex) `typing.Union[typing.NoReturn, int, str]` -> `typing.Union[int, str]`
|
||||
checker
|
||||
.generator()
|
||||
.expr(&Expr::Subscript(ast::ExprSubscript {
|
||||
value: value.clone(),
|
||||
slice: Box::new(Expr::Tuple(ast::ExprTuple {
|
||||
elts: rest,
|
||||
ctx: ast::ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
})),
|
||||
ctx: ast::ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
}))
|
||||
},
|
||||
expr.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum UnionLike {
|
||||
/// E.g., `typing.Union[int, str]`
|
||||
TypingUnion,
|
||||
/// E.g., `int | str`
|
||||
BinOp,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum NeverLike {
|
||||
/// E.g., `typing.NoReturn`
|
||||
NoReturn,
|
||||
/// E.g., `typing.Never`
|
||||
Never,
|
||||
}
|
||||
|
||||
impl NeverLike {
|
||||
fn from_expr(expr: &Expr, semantic: &ruff_python_semantic::SemanticModel) -> Option<Self> {
|
||||
let call_path = semantic.resolve_call_path(expr)?;
|
||||
if semantic.match_typing_call_path(&call_path, "NoReturn") {
|
||||
Some(NeverLike::NoReturn)
|
||||
} else if semantic.match_typing_call_path(&call_path, "Never") {
|
||||
Some(NeverLike::Never)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for NeverLike {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
NeverLike::NoReturn => f.write_str("NoReturn"),
|
||||
NeverLike::Never => f.write_str("Never"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/ruff/mod.rs
|
||||
---
|
||||
RUF020.py:3:7: RUF020 [*] `Union[Never, T]` is equivalent to `T`
|
||||
|
|
||||
1 | from typing import Never, NoReturn, Union
|
||||
2 |
|
||||
3 | Union[Never, int]
|
||||
| ^^^^^ RUF020
|
||||
4 | Union[NoReturn, int]
|
||||
5 | Never | int
|
||||
|
|
||||
= help: Remove `Never`
|
||||
|
||||
ℹ Safe fix
|
||||
1 1 | from typing import Never, NoReturn, Union
|
||||
2 2 |
|
||||
3 |-Union[Never, int]
|
||||
3 |+int
|
||||
4 4 | Union[NoReturn, int]
|
||||
5 5 | Never | int
|
||||
6 6 | NoReturn | int
|
||||
|
||||
RUF020.py:4:7: RUF020 [*] `Union[NoReturn, T]` is equivalent to `T`
|
||||
|
|
||||
3 | Union[Never, int]
|
||||
4 | Union[NoReturn, int]
|
||||
| ^^^^^^^^ RUF020
|
||||
5 | Never | int
|
||||
6 | NoReturn | int
|
||||
|
|
||||
= help: Remove `NoReturn`
|
||||
|
||||
ℹ Safe fix
|
||||
1 1 | from typing import Never, NoReturn, Union
|
||||
2 2 |
|
||||
3 3 | Union[Never, int]
|
||||
4 |-Union[NoReturn, int]
|
||||
4 |+int
|
||||
5 5 | Never | int
|
||||
6 6 | NoReturn | int
|
||||
7 7 | Union[Union[Never, int], Union[NoReturn, int]]
|
||||
|
||||
RUF020.py:5:1: RUF020 [*] `Never | T` is equivalent to `T`
|
||||
|
|
||||
3 | Union[Never, int]
|
||||
4 | Union[NoReturn, int]
|
||||
5 | Never | int
|
||||
| ^^^^^ RUF020
|
||||
6 | NoReturn | int
|
||||
7 | Union[Union[Never, int], Union[NoReturn, int]]
|
||||
|
|
||||
= help: Remove `Never`
|
||||
|
||||
ℹ Safe fix
|
||||
2 2 |
|
||||
3 3 | Union[Never, int]
|
||||
4 4 | Union[NoReturn, int]
|
||||
5 |-Never | int
|
||||
5 |+int
|
||||
6 6 | NoReturn | int
|
||||
7 7 | Union[Union[Never, int], Union[NoReturn, int]]
|
||||
8 8 | Union[NoReturn, int, float]
|
||||
|
||||
RUF020.py:6:1: RUF020 [*] `NoReturn | T` is equivalent to `T`
|
||||
|
|
||||
4 | Union[NoReturn, int]
|
||||
5 | Never | int
|
||||
6 | NoReturn | int
|
||||
| ^^^^^^^^ RUF020
|
||||
7 | Union[Union[Never, int], Union[NoReturn, int]]
|
||||
8 | Union[NoReturn, int, float]
|
||||
|
|
||||
= help: Remove `NoReturn`
|
||||
|
||||
ℹ Safe fix
|
||||
3 3 | Union[Never, int]
|
||||
4 4 | Union[NoReturn, int]
|
||||
5 5 | Never | int
|
||||
6 |-NoReturn | int
|
||||
6 |+int
|
||||
7 7 | Union[Union[Never, int], Union[NoReturn, int]]
|
||||
8 8 | Union[NoReturn, int, float]
|
||||
|
||||
RUF020.py:7:13: RUF020 [*] `Union[Never, T]` is equivalent to `T`
|
||||
|
|
||||
5 | Never | int
|
||||
6 | NoReturn | int
|
||||
7 | Union[Union[Never, int], Union[NoReturn, int]]
|
||||
| ^^^^^ RUF020
|
||||
8 | Union[NoReturn, int, float]
|
||||
|
|
||||
= help: Remove `Never`
|
||||
|
||||
ℹ Safe fix
|
||||
4 4 | Union[NoReturn, int]
|
||||
5 5 | Never | int
|
||||
6 6 | NoReturn | int
|
||||
7 |-Union[Union[Never, int], Union[NoReturn, int]]
|
||||
7 |+Union[int, Union[NoReturn, int]]
|
||||
8 8 | Union[NoReturn, int, float]
|
||||
|
||||
RUF020.py:7:32: RUF020 [*] `Union[NoReturn, T]` is equivalent to `T`
|
||||
|
|
||||
5 | Never | int
|
||||
6 | NoReturn | int
|
||||
7 | Union[Union[Never, int], Union[NoReturn, int]]
|
||||
| ^^^^^^^^ RUF020
|
||||
8 | Union[NoReturn, int, float]
|
||||
|
|
||||
= help: Remove `NoReturn`
|
||||
|
||||
ℹ Safe fix
|
||||
4 4 | Union[NoReturn, int]
|
||||
5 5 | Never | int
|
||||
6 6 | NoReturn | int
|
||||
7 |-Union[Union[Never, int], Union[NoReturn, int]]
|
||||
7 |+Union[Union[Never, int], int]
|
||||
8 8 | Union[NoReturn, int, float]
|
||||
|
||||
RUF020.py:8:7: RUF020 [*] `Union[NoReturn, T]` is equivalent to `T`
|
||||
|
|
||||
6 | NoReturn | int
|
||||
7 | Union[Union[Never, int], Union[NoReturn, int]]
|
||||
8 | Union[NoReturn, int, float]
|
||||
| ^^^^^^^^ RUF020
|
||||
|
|
||||
= help: Remove `NoReturn`
|
||||
|
||||
ℹ Safe fix
|
||||
5 5 | Never | int
|
||||
6 6 | NoReturn | int
|
||||
7 7 | Union[Union[Never, int], Union[NoReturn, int]]
|
||||
8 |-Union[NoReturn, int, float]
|
||||
8 |+Union[int, float]
|
||||
|
||||
|
||||
@@ -40,6 +40,8 @@ use crate::rule_selector::RuleSelector;
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub enum PythonVersion {
|
||||
Py37,
|
||||
// Make sure to also change the default for `ruff_python_formatter::PythonVersion`
|
||||
// when changing the default here.
|
||||
#[default]
|
||||
Py38,
|
||||
Py39,
|
||||
|
||||
@@ -21,7 +21,7 @@ use ruff_text_size::Ranged;
|
||||
|
||||
use crate::directives;
|
||||
use crate::fix::{fix_file, FixResult};
|
||||
use crate::linter::{check_path, LinterResult};
|
||||
use crate::linter::{check_path, LinterResult, TokenSource};
|
||||
use crate::message::{Emitter, EmitterContext, Message, TextEmitter};
|
||||
use crate::packaging::detect_package_root;
|
||||
use crate::registry::AsRule;
|
||||
@@ -129,7 +129,6 @@ pub(crate) fn test_contents<'a>(
|
||||
path,
|
||||
path.parent()
|
||||
.and_then(|parent| detect_package_root(parent, &settings.namespace_packages)),
|
||||
tokens,
|
||||
&locator,
|
||||
&stylist,
|
||||
&indexer,
|
||||
@@ -138,6 +137,7 @@ pub(crate) fn test_contents<'a>(
|
||||
flags::Noqa::Enabled,
|
||||
source_kind,
|
||||
source_type,
|
||||
TokenSource::Tokens(tokens),
|
||||
);
|
||||
|
||||
let source_has_errors = error.is_some();
|
||||
@@ -195,7 +195,6 @@ pub(crate) fn test_contents<'a>(
|
||||
} = check_path(
|
||||
path,
|
||||
None,
|
||||
tokens,
|
||||
&locator,
|
||||
&stylist,
|
||||
&indexer,
|
||||
@@ -204,6 +203,7 @@ pub(crate) fn test_contents<'a>(
|
||||
flags::Noqa::Enabled,
|
||||
&transformed,
|
||||
source_type,
|
||||
TokenSource::Tokens(tokens),
|
||||
);
|
||||
|
||||
if let Some(fixed_error) = fixed_error {
|
||||
|
||||
@@ -1505,6 +1505,7 @@ pub fn pep_604_union(elts: &[Expr]) -> Expr {
|
||||
}
|
||||
}
|
||||
|
||||
/// Format the expression as a `typing.Optional`-style optional.
|
||||
pub fn typing_optional(elt: Expr, binding: String) -> Expr {
|
||||
Expr::Subscript(ast::ExprSubscript {
|
||||
value: Box::new(Expr::Name(ast::ExprName {
|
||||
@@ -1518,18 +1519,19 @@ pub fn typing_optional(elt: Expr, binding: String) -> Expr {
|
||||
})
|
||||
}
|
||||
|
||||
/// Format the expressions as a `typing.Union`-style union.
|
||||
pub fn typing_union(elts: &[Expr], binding: String) -> Expr {
|
||||
fn tuple(elts: &[Expr]) -> Expr {
|
||||
fn tuple(elts: &[Expr], binding: String) -> Expr {
|
||||
match elts {
|
||||
[] => Expr::Tuple(ast::ExprTuple {
|
||||
elts: vec![],
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
}),
|
||||
[Expr::Tuple(ast::ExprTuple { elts, .. })] => pep_604_union(elts),
|
||||
[Expr::Tuple(ast::ExprTuple { elts, .. })] => typing_union(elts, binding),
|
||||
[elt] => elt.clone(),
|
||||
[rest @ .., elt] => Expr::BinOp(ast::ExprBinOp {
|
||||
left: Box::new(tuple(rest)),
|
||||
left: Box::new(tuple(rest, binding)),
|
||||
op: Operator::BitOr,
|
||||
right: Box::new(elt.clone()),
|
||||
range: TextRange::default(),
|
||||
@@ -1539,11 +1541,11 @@ pub fn typing_union(elts: &[Expr], binding: String) -> Expr {
|
||||
|
||||
Expr::Subscript(ast::ExprSubscript {
|
||||
value: Box::new(Expr::Name(ast::ExprName {
|
||||
id: binding,
|
||||
id: binding.clone(),
|
||||
range: TextRange::default(),
|
||||
ctx: ExprContext::Load,
|
||||
})),
|
||||
slice: Box::new(tuple(elts)),
|
||||
slice: Box::new(tuple(elts, binding)),
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
})
|
||||
|
||||
2
crates/ruff_python_formatter/resources/test/fixtures/black/cases/comment_type_hint.py
vendored
Normal file
2
crates/ruff_python_formatter/resources/test/fixtures/black/cases/comment_type_hint.py
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# split out from comments2 as it does not work with line-length=1, losing the comment
|
||||
a = "type comment with trailing space" # type: str
|
||||
2
crates/ruff_python_formatter/resources/test/fixtures/black/cases/comment_type_hint.py.expect
vendored
Normal file
2
crates/ruff_python_formatter/resources/test/fixtures/black/cases/comment_type_hint.py.expect
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# split out from comments2 as it does not work with line-length=1, losing the comment
|
||||
a = "type comment with trailing space" # type: str
|
||||
@@ -155,8 +155,6 @@ class Test:
|
||||
pass
|
||||
|
||||
|
||||
a = "type comment with trailing space" # type: str
|
||||
|
||||
#######################
|
||||
### SECTION COMMENT ###
|
||||
#######################
|
||||
|
||||
@@ -162,8 +162,6 @@ class Test:
|
||||
pass
|
||||
|
||||
|
||||
a = "type comment with trailing space" # type: str
|
||||
|
||||
#######################
|
||||
### SECTION COMMENT ###
|
||||
#######################
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# l2 loses the comment with line-length=1 in preview mode
|
||||
l1 = ["This list should be broken up", "into multiple lines", "because it is way too long"]
|
||||
l2 = ["But this list shouldn't", "even though it also has", "way too many characters in it"] # fmt: skip
|
||||
l3 = ["I have", "trailing comma", "so I should be braked",]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# l2 loses the comment with line-length=1 in preview mode
|
||||
l1 = [
|
||||
"This list should be broken up",
|
||||
"into multiple lines",
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"preview": "enabled"}
|
||||
{"preview": "enabled", "target_version": "py310"}
|
||||
@@ -0,0 +1 @@
|
||||
{"target_version": "py310"}
|
||||
@@ -0,0 +1 @@
|
||||
{"target_version": "py310"}
|
||||
@@ -0,0 +1 @@
|
||||
{"target_version": "py310"}
|
||||
@@ -0,0 +1 @@
|
||||
{"target_version": "py310"}
|
||||
@@ -0,0 +1 @@
|
||||
{"target_version": "py310"}
|
||||
@@ -0,0 +1 @@
|
||||
{"target_version": "py310"}
|
||||
@@ -1 +1 @@
|
||||
{"preview": "enabled"}
|
||||
{"preview": "enabled", "target_version": "py310"}
|
||||
1
crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_570.options.json
vendored
Normal file
1
crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_570.options.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"target_version": "py38"}
|
||||
1
crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572.options.json
vendored
Normal file
1
crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572.options.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"target_version": "py38"}
|
||||
1
crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_py310.options.json
vendored
Normal file
1
crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_py310.options.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"target_version": "py310"}
|
||||
1
crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_py39.options.json
vendored
Normal file
1
crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_572_py39.options.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"target_version": "py39"}
|
||||
@@ -0,0 +1 @@
|
||||
{"target_version": "py38"}
|
||||
1
crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_646.options.json
vendored
Normal file
1
crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_646.options.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"target_version": "py311"}
|
||||
1
crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_654.options.json
vendored
Normal file
1
crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_654.options.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"target_version": "py311"}
|
||||
1
crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_654_style.options.json
vendored
Normal file
1
crates/ruff_python_formatter/resources/test/fixtures/black/cases/pep_654_style.options.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"target_version": "py311"}
|
||||
@@ -0,0 +1 @@
|
||||
{"preview": "enabled"}
|
||||
@@ -0,0 +1,62 @@
|
||||
def foo():
|
||||
"""
|
||||
Docstring
|
||||
"""
|
||||
|
||||
# Here we go
|
||||
if x:
|
||||
|
||||
# This is also now fine
|
||||
a = 123
|
||||
|
||||
else:
|
||||
# But not necessary
|
||||
a = 123
|
||||
|
||||
if y:
|
||||
|
||||
while True:
|
||||
|
||||
"""
|
||||
Long comment here
|
||||
"""
|
||||
a = 123
|
||||
|
||||
if z:
|
||||
|
||||
for _ in range(100):
|
||||
a = 123
|
||||
else:
|
||||
|
||||
try:
|
||||
|
||||
# this should be ok
|
||||
a = 123
|
||||
except:
|
||||
|
||||
"""also this"""
|
||||
a = 123
|
||||
|
||||
|
||||
def bar():
|
||||
|
||||
if x:
|
||||
a = 123
|
||||
|
||||
|
||||
def baz():
|
||||
|
||||
# OK
|
||||
if x:
|
||||
a = 123
|
||||
|
||||
def quux():
|
||||
|
||||
new_line = here
|
||||
|
||||
|
||||
class Cls:
|
||||
|
||||
def method(self):
|
||||
|
||||
pass
|
||||
@@ -0,0 +1,62 @@
|
||||
def foo():
|
||||
"""
|
||||
Docstring
|
||||
"""
|
||||
|
||||
# Here we go
|
||||
if x:
|
||||
|
||||
# This is also now fine
|
||||
a = 123
|
||||
|
||||
else:
|
||||
# But not necessary
|
||||
a = 123
|
||||
|
||||
if y:
|
||||
|
||||
while True:
|
||||
|
||||
"""
|
||||
Long comment here
|
||||
"""
|
||||
a = 123
|
||||
|
||||
if z:
|
||||
|
||||
for _ in range(100):
|
||||
a = 123
|
||||
else:
|
||||
|
||||
try:
|
||||
|
||||
# this should be ok
|
||||
a = 123
|
||||
except:
|
||||
|
||||
"""also this"""
|
||||
a = 123
|
||||
|
||||
|
||||
def bar():
|
||||
|
||||
if x:
|
||||
a = 123
|
||||
|
||||
|
||||
def baz():
|
||||
|
||||
# OK
|
||||
if x:
|
||||
a = 123
|
||||
|
||||
|
||||
def quux():
|
||||
|
||||
new_line = here
|
||||
|
||||
|
||||
class Cls:
|
||||
def method(self):
|
||||
|
||||
pass
|
||||
@@ -1 +1 @@
|
||||
{"preview": "enabled"}
|
||||
{"preview": "enabled", "target_version": "py38"}
|
||||
@@ -1 +1 @@
|
||||
{"preview": "enabled"}
|
||||
{"preview": "enabled", "target_version": "py39"}
|
||||
@@ -1 +1 @@
|
||||
{"preview": "enabled"}
|
||||
{"preview": "enabled", "target_version": "py310"}
|
||||
@@ -1 +1 @@
|
||||
{"preview": "enabled"}
|
||||
{"preview": "enabled", "target_version": "py311"}
|
||||
@@ -1 +1 @@
|
||||
{"preview": "enabled"}
|
||||
{"preview": "enabled", "target_version": "py39"}
|
||||
@@ -1,8 +1,10 @@
|
||||
from typing import NoReturn, Protocol, Union, overload
|
||||
|
||||
class Empty:
|
||||
...
|
||||
|
||||
def dummy(a): ...
|
||||
def other(b): ...
|
||||
async def other(b): ...
|
||||
|
||||
|
||||
@overload
|
||||
@@ -46,3 +48,11 @@ def b(arg: Union[int, str, object]) -> Union[int, str]:
|
||||
if not isinstance(arg, (int, str)):
|
||||
raise TypeError
|
||||
return arg
|
||||
|
||||
def has_comment():
|
||||
... # still a dummy
|
||||
|
||||
if some_condition:
|
||||
...
|
||||
|
||||
if already_dummy: ...
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
from typing import NoReturn, Protocol, Union, overload
|
||||
|
||||
|
||||
class Empty: ...
|
||||
|
||||
|
||||
def dummy(a): ...
|
||||
def other(b): ...
|
||||
async def other(b): ...
|
||||
|
||||
|
||||
@overload
|
||||
@@ -46,3 +49,13 @@ def b(arg: Union[int, str, object]) -> Union[int, str]:
|
||||
if not isinstance(arg, (int, str)):
|
||||
raise TypeError
|
||||
return arg
|
||||
|
||||
|
||||
def has_comment(): ... # still a dummy
|
||||
|
||||
|
||||
if some_condition:
|
||||
...
|
||||
|
||||
if already_dummy:
|
||||
...
|
||||
|
||||
@@ -74,6 +74,7 @@ pass
|
||||
|
||||
# form feeds are prohibited inside blocks, or on a line with nonwhitespace
|
||||
def bar(a=1, b: bool = False):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
@@ -124,23 +124,6 @@ func([x for x in "short line"])
|
||||
func([x for x in "long line long line long line long line long line long line long line"])
|
||||
func([x for x in [x for x in "long line long line long line long line long line long line long line"]])
|
||||
|
||||
func({"short line"})
|
||||
func({"long line", "long long line", "long long long line", "long long long long line", "long long long long long line"})
|
||||
func({{"long line", "long long line", "long long long line", "long long long long line", "long long long long long line"}})
|
||||
func(("long line", "long long line", "long long long line", "long long long long line", "long long long long long line"))
|
||||
func((("long line", "long long line", "long long long line", "long long long long line", "long long long long long line")))
|
||||
func([["long line", "long long line", "long long long line", "long long long long line", "long long long long long line"]])
|
||||
|
||||
# Do not hug if the argument fits on a single line.
|
||||
func({"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"})
|
||||
func(("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"))
|
||||
func(["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"])
|
||||
func(**{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit---"})
|
||||
func(*("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit----"))
|
||||
array = [{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"}]
|
||||
array = [("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line")]
|
||||
array = [["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"]]
|
||||
|
||||
foooooooooooooooooooo(
|
||||
[{c: n + 1 for c in range(256)} for n in range(100)] + [{}], {size}
|
||||
)
|
||||
@@ -150,14 +133,11 @@ baaaaaaaaaaaaar(
|
||||
)
|
||||
|
||||
nested_mapping = {"key": [{"a very long key 1": "with a very long value", "a very long key 2": "with a very long value"}]}
|
||||
nested_array = [[["long line", "long long line", "long long long line", "long long long long line", "long long long long long line"]]]
|
||||
explicit_exploding = [[["short", "line",],],]
|
||||
single_item_do_not_explode = Context({
|
||||
"version": get_docs_version(),
|
||||
})
|
||||
|
||||
foo(*["long long long long long line", "long long long long long line", "long long long long long line"])
|
||||
|
||||
foo(*[str(i) for i in range(100000000000000000000000000000000000000000000000000000000000)])
|
||||
|
||||
foo(
|
||||
|
||||
@@ -122,69 +122,6 @@ func([
|
||||
]
|
||||
])
|
||||
|
||||
func({"short line"})
|
||||
func({
|
||||
"long line",
|
||||
"long long line",
|
||||
"long long long line",
|
||||
"long long long long line",
|
||||
"long long long long long line",
|
||||
})
|
||||
func({{
|
||||
"long line",
|
||||
"long long line",
|
||||
"long long long line",
|
||||
"long long long long line",
|
||||
"long long long long long line",
|
||||
}})
|
||||
func((
|
||||
"long line",
|
||||
"long long line",
|
||||
"long long long line",
|
||||
"long long long long line",
|
||||
"long long long long long line",
|
||||
))
|
||||
func(((
|
||||
"long line",
|
||||
"long long line",
|
||||
"long long long line",
|
||||
"long long long long line",
|
||||
"long long long long long line",
|
||||
)))
|
||||
func([[
|
||||
"long line",
|
||||
"long long line",
|
||||
"long long long line",
|
||||
"long long long long line",
|
||||
"long long long long long line",
|
||||
]])
|
||||
|
||||
# Do not hug if the argument fits on a single line.
|
||||
func(
|
||||
{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"}
|
||||
)
|
||||
func(
|
||||
("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line")
|
||||
)
|
||||
func(
|
||||
["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"]
|
||||
)
|
||||
func(
|
||||
**{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit---"}
|
||||
)
|
||||
func(
|
||||
*("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit----")
|
||||
)
|
||||
array = [
|
||||
{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"}
|
||||
]
|
||||
array = [
|
||||
("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line")
|
||||
]
|
||||
array = [
|
||||
["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"]
|
||||
]
|
||||
|
||||
foooooooooooooooooooo(
|
||||
[{c: n + 1 for c in range(256)} for n in range(100)] + [{}], {size}
|
||||
)
|
||||
@@ -199,13 +136,6 @@ nested_mapping = {
|
||||
"a very long key 2": "with a very long value",
|
||||
}]
|
||||
}
|
||||
nested_array = [[[
|
||||
"long line",
|
||||
"long long line",
|
||||
"long long long line",
|
||||
"long long long long line",
|
||||
"long long long long long line",
|
||||
]]]
|
||||
explicit_exploding = [
|
||||
[
|
||||
[
|
||||
@@ -218,12 +148,6 @@ single_item_do_not_explode = Context({
|
||||
"version": get_docs_version(),
|
||||
})
|
||||
|
||||
foo(*[
|
||||
"long long long long long line",
|
||||
"long long long long long line",
|
||||
"long long long long long line",
|
||||
])
|
||||
|
||||
foo(*[
|
||||
str(i) for i in range(100000000000000000000000000000000000000000000000000000000000)
|
||||
])
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"preview": "enabled"}
|
||||
@@ -0,0 +1,23 @@
|
||||
# split out from preview_hug_parens_with_brackes_and_square_brackets, as it produces
|
||||
# different code on the second pass with line-length 1 in many cases.
|
||||
# Seems to be about whether the last string in a sequence gets wrapped in parens or not.
|
||||
foo(*["long long long long long line", "long long long long long line", "long long long long long line"])
|
||||
func({"short line"})
|
||||
func({"long line", "long long line", "long long long line", "long long long long line", "long long long long long line"})
|
||||
func({{"long line", "long long line", "long long long line", "long long long long line", "long long long long long line"}})
|
||||
func(("long line", "long long line", "long long long line", "long long long long line", "long long long long long line"))
|
||||
func((("long line", "long long line", "long long long line", "long long long long line", "long long long long long line")))
|
||||
func([["long line", "long long line", "long long long line", "long long long long line", "long long long long long line"]])
|
||||
|
||||
|
||||
# Do not hug if the argument fits on a single line.
|
||||
func({"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"})
|
||||
func(("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"))
|
||||
func(["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"])
|
||||
func(**{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit---"})
|
||||
func(*("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit----"))
|
||||
array = [{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"}]
|
||||
array = [("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line")]
|
||||
array = [["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"]]
|
||||
|
||||
nested_array = [[["long line", "long long line", "long long long line", "long long long long line", "long long long long long line"]]]
|
||||
@@ -0,0 +1,79 @@
|
||||
# split out from preview_hug_parens_with_brackes_and_square_brackets, as it produces
|
||||
# different code on the second pass with line-length 1 in many cases.
|
||||
# Seems to be about whether the last string in a sequence gets wrapped in parens or not.
|
||||
foo(*[
|
||||
"long long long long long line",
|
||||
"long long long long long line",
|
||||
"long long long long long line",
|
||||
])
|
||||
func({"short line"})
|
||||
func({
|
||||
"long line",
|
||||
"long long line",
|
||||
"long long long line",
|
||||
"long long long long line",
|
||||
"long long long long long line",
|
||||
})
|
||||
func({{
|
||||
"long line",
|
||||
"long long line",
|
||||
"long long long line",
|
||||
"long long long long line",
|
||||
"long long long long long line",
|
||||
}})
|
||||
func((
|
||||
"long line",
|
||||
"long long line",
|
||||
"long long long line",
|
||||
"long long long long line",
|
||||
"long long long long long line",
|
||||
))
|
||||
func(((
|
||||
"long line",
|
||||
"long long line",
|
||||
"long long long line",
|
||||
"long long long long line",
|
||||
"long long long long long line",
|
||||
)))
|
||||
func([[
|
||||
"long line",
|
||||
"long long line",
|
||||
"long long long line",
|
||||
"long long long long line",
|
||||
"long long long long long line",
|
||||
]])
|
||||
|
||||
|
||||
# Do not hug if the argument fits on a single line.
|
||||
func(
|
||||
{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"}
|
||||
)
|
||||
func(
|
||||
("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line")
|
||||
)
|
||||
func(
|
||||
["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"]
|
||||
)
|
||||
func(
|
||||
**{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit---"}
|
||||
)
|
||||
func(
|
||||
*("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit----")
|
||||
)
|
||||
array = [
|
||||
{"fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"}
|
||||
]
|
||||
array = [
|
||||
("fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line")
|
||||
]
|
||||
array = [
|
||||
["fit line", "fit line", "fit line", "fit line", "fit line", "fit line", "fit line"]
|
||||
]
|
||||
|
||||
nested_array = [[[
|
||||
"long line",
|
||||
"long long line",
|
||||
"long long long line",
|
||||
"long long long long line",
|
||||
"long long long long long line",
|
||||
]]]
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user