Compare commits
55 Commits
charlie/sp
...
zb/debug-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17e77fe515 | ||
|
|
946b308197 | ||
|
|
d22ce5372d | ||
|
|
acab5f3cf2 | ||
|
|
06c9f625b6 | ||
|
|
bbb0a0c360 | ||
|
|
9361e22fe9 | ||
|
|
f484df5470 | ||
|
|
af88ffc57e | ||
|
|
b918647927 | ||
|
|
ef7778d794 | ||
|
|
bd443ebe91 | ||
|
|
ee6548d7dd | ||
|
|
b4a050c21d | ||
|
|
958702ded0 | ||
|
|
268d95e911 | ||
|
|
3def18fc21 | ||
|
|
c48ba690eb | ||
|
|
fd49fb935f | ||
|
|
fe54ef08aa | ||
|
|
b7ffd73edd | ||
|
|
8d9912a83a | ||
|
|
93258e8d5b | ||
|
|
b90027d037 | ||
|
|
060a25df09 | ||
|
|
f5d4676c13 | ||
|
|
df69dc9f8d | ||
|
|
cb6c37abd9 | ||
|
|
54de990621 | ||
|
|
b91b09b961 | ||
|
|
0bda1913d1 | ||
|
|
7e390d3772 | ||
|
|
0bf0aa28ac | ||
|
|
8088c5367a | ||
|
|
6fe8f8a272 | ||
|
|
bfae1f1412 | ||
|
|
b358cbf398 | ||
|
|
17c8817695 | ||
|
|
1dda669f9a | ||
|
|
3fbabfe126 | ||
|
|
20ab14e354 | ||
|
|
22d8a989d4 | ||
|
|
35082b28cd | ||
|
|
5aaf99b856 | ||
|
|
58bf6f5762 | ||
|
|
277cd80175 | ||
|
|
20a40771a5 | ||
|
|
4af3f43e5e | ||
|
|
0b1a36f8c8 | ||
|
|
64c2535e28 | ||
|
|
5510a6131e | ||
|
|
e5db72459e | ||
|
|
d66063bb33 | ||
|
|
506be68782 | ||
|
|
cb1d3df085 |
24
.github/workflows/ci.yaml
vendored
24
.github/workflows/ci.yaml
vendored
@@ -48,8 +48,8 @@ jobs:
|
||||
- "!crates/ruff_dev/**"
|
||||
- "!crates/ruff_shrinking/**"
|
||||
- scripts/*
|
||||
- .github/workflows/ci.yaml
|
||||
- python/**
|
||||
- .github/workflows/ci.yaml
|
||||
|
||||
formatter:
|
||||
- Cargo.toml
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
- .github/workflows/ci.yaml
|
||||
|
||||
code:
|
||||
- "*/**"
|
||||
- "**/*"
|
||||
- "!**/*.md"
|
||||
- "!docs/**"
|
||||
- "!assets/**"
|
||||
@@ -86,7 +86,7 @@ jobs:
|
||||
name: "cargo clippy"
|
||||
runs-on: ubuntu-latest
|
||||
needs: determine_changes
|
||||
if: needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main'
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: "Install Rust toolchain"
|
||||
@@ -102,7 +102,7 @@ jobs:
|
||||
cargo-test-linux:
|
||||
runs-on: ubuntu-latest
|
||||
needs: determine_changes
|
||||
if: needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main'
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
name: "cargo test (linux)"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -128,7 +128,7 @@ jobs:
|
||||
cargo-test-windows:
|
||||
runs-on: windows-latest
|
||||
needs: determine_changes
|
||||
if: needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main'
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
name: "cargo test (windows)"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -147,7 +147,7 @@ jobs:
|
||||
cargo-test-wasm:
|
||||
runs-on: ubuntu-latest
|
||||
needs: determine_changes
|
||||
if: needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main'
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
name: "cargo test (wasm)"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -168,7 +168,7 @@ jobs:
|
||||
cargo-fuzz:
|
||||
runs-on: ubuntu-latest
|
||||
needs: determine_changes
|
||||
if: needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main'
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
name: "cargo fuzz"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -187,7 +187,7 @@ jobs:
|
||||
name: "test scripts"
|
||||
runs-on: ubuntu-latest
|
||||
needs: determine_changes
|
||||
if: needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main'
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: "Install Rust toolchain"
|
||||
@@ -321,7 +321,7 @@ jobs:
|
||||
name: "cargo udeps"
|
||||
runs-on: ubuntu-latest
|
||||
needs: determine_changes
|
||||
if: needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main'
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: "Install nightly Rust toolchain"
|
||||
@@ -444,7 +444,7 @@ jobs:
|
||||
needs:
|
||||
- cargo-test-linux
|
||||
- determine_changes
|
||||
if: needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main'
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
steps:
|
||||
- uses: extractions/setup-just@v1
|
||||
env:
|
||||
@@ -483,7 +483,7 @@ jobs:
|
||||
benchmarks:
|
||||
runs-on: ubuntu-latest
|
||||
needs: determine_changes
|
||||
if: needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main'
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
steps:
|
||||
- name: "Checkout Branch"
|
||||
uses: actions/checkout@v4
|
||||
@@ -502,7 +502,7 @@ jobs:
|
||||
run: cargo codspeed build --features codspeed -p ruff_benchmark
|
||||
|
||||
- name: "Run benchmarks"
|
||||
uses: CodSpeedHQ/action@v1
|
||||
uses: CodSpeedHQ/action@v2
|
||||
with:
|
||||
run: cargo codspeed run
|
||||
token: ${{ secrets.CODSPEED_TOKEN }}
|
||||
|
||||
14
.github/workflows/release.yaml
vendored
14
.github/workflows/release.yaml
vendored
@@ -86,7 +86,7 @@ jobs:
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-x86_64-apple-darwin.tar.gz
|
||||
ARCHIVE_FILE=ruff-${{ inputs.tag }}-x86_64-apple-darwin.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/x86_64-apple-darwin/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
@@ -125,7 +125,7 @@ jobs:
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-aarch64-apple-darwin.tar.gz
|
||||
ARCHIVE_FILE=ruff-${{ inputs.tag }}-aarch64-apple-darwin.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/aarch64-apple-darwin/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
@@ -177,7 +177,7 @@ jobs:
|
||||
- name: "Archive binary"
|
||||
shell: bash
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-${{ matrix.platform.target }}.zip
|
||||
ARCHIVE_FILE=ruff-${{ inputs.tag }}-${{ matrix.platform.target }}.zip
|
||||
7z a $ARCHIVE_FILE ./target/${{ matrix.platform.target }}/release/ruff.exe
|
||||
sha256sum $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
@@ -224,7 +224,7 @@ jobs:
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-${{ matrix.target }}.tar.gz
|
||||
ARCHIVE_FILE=ruff-${{ inputs.tag }}-${{ matrix.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
@@ -291,7 +291,7 @@ jobs:
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-${{ matrix.platform.target }}.tar.gz
|
||||
ARCHIVE_FILE=ruff-${{ inputs.tag }}-${{ matrix.platform.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.platform.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
@@ -343,7 +343,7 @@ jobs:
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-${{ matrix.target }}.tar.gz
|
||||
ARCHIVE_FILE=ruff-${{ inputs.tag }}-${{ matrix.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
@@ -399,7 +399,7 @@ jobs:
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-${{ matrix.platform.target }}.tar.gz
|
||||
ARCHIVE_FILE=ruff-${{ inputs.tag }}-${{ matrix.platform.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.platform.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
|
||||
94
CHANGELOG.md
94
CHANGELOG.md
@@ -1,5 +1,99 @@
|
||||
# Changelog
|
||||
|
||||
## 0.1.7
|
||||
|
||||
### Preview features
|
||||
|
||||
- Implement multiline dictionary and list hugging for preview style ([#8293](https://github.com/astral-sh/ruff/pull/8293))
|
||||
- Implement the `fix_power_op_line_length` preview style ([#8947](https://github.com/astral-sh/ruff/pull/8947))
|
||||
- Use Python version to determine typing rewrite safety ([#8919](https://github.com/astral-sh/ruff/pull/8919))
|
||||
- \[`flake8-annotations`\] Enable auto-return-type involving `Optional` and `Union` annotations ([#8885](https://github.com/astral-sh/ruff/pull/8885))
|
||||
- \[`flake8-bandit`\] Implement `django-raw-sql` (`S611`) ([#8651](https://github.com/astral-sh/ruff/pull/8651))
|
||||
- \[`flake8-bandit`\] Implement `tarfile-unsafe-members` (`S202`) ([#8829](https://github.com/astral-sh/ruff/pull/8829))
|
||||
- \[`flake8-pyi`\] Implement fix for `unnecessary-literal-union` (`PYI030`) ([#7934](https://github.com/astral-sh/ruff/pull/7934))
|
||||
- \[`flake8-simplify`\] Extend `dict-get-with-none-default` (`SIM910`) to non-literals ([#8762](https://github.com/astral-sh/ruff/pull/8762))
|
||||
- \[`pylint`\] - add `unnecessary-list-index-lookup` (`PLR1736`) + autofix ([#7999](https://github.com/astral-sh/ruff/pull/7999))
|
||||
- \[`pylint`\] - implement R0202 and R0203 with autofixes ([#8335](https://github.com/astral-sh/ruff/pull/8335))
|
||||
- \[`pylint`\] Implement `repeated-keyword` (`PLe1132`) ([#8706](https://github.com/astral-sh/ruff/pull/8706))
|
||||
- \[`pylint`\] Implement `too-many-positional` (`PLR0917`) ([#8995](https://github.com/astral-sh/ruff/pull/8995))
|
||||
- \[`pylint`\] Implement `unnecessary-dict-index-lookup` (`PLR1733`) ([#8036](https://github.com/astral-sh/ruff/pull/8036))
|
||||
- \[`refurb`\] Implement `redundant-log-base` (`FURB163`) ([#8842](https://github.com/astral-sh/ruff/pull/8842))
|
||||
|
||||
### Rule changes
|
||||
|
||||
- \[`flake8-boolean-trap`\] Allow booleans in `@override` methods ([#8882](https://github.com/astral-sh/ruff/pull/8882))
|
||||
- \[`flake8-bugbear`\] Avoid `B015`,`B018` for last expression in a cell ([#8815](https://github.com/astral-sh/ruff/pull/8815))
|
||||
- \[`flake8-pie`\] Allow ellipses for enum values in stub files ([#8825](https://github.com/astral-sh/ruff/pull/8825))
|
||||
- \[`flake8-pyi`\] Check PEP 695 type aliases for `snake-case-type-alias` and `t-suffixed-type-alias` ([#8966](https://github.com/astral-sh/ruff/pull/8966))
|
||||
- \[`flake8-pyi`\] Check for kwarg and vararg `NoReturn` type annotations ([#8948](https://github.com/astral-sh/ruff/pull/8948))
|
||||
- \[`flake8-simplify`\] Omit select context managers from `SIM117` ([#8801](https://github.com/astral-sh/ruff/pull/8801))
|
||||
- \[`pep8-naming`\] Allow Django model loads in `non-lowercase-variable-in-function` (`N806`) ([#8917](https://github.com/astral-sh/ruff/pull/8917))
|
||||
- \[`pycodestyle`\] Avoid `E703` for last expression in a cell ([#8821](https://github.com/astral-sh/ruff/pull/8821))
|
||||
- \[`pycodestyle`\] Update `E402` to work at cell level for notebooks ([#8872](https://github.com/astral-sh/ruff/pull/8872))
|
||||
- \[`pydocstyle`\] Avoid `D100` for Jupyter Notebooks ([#8816](https://github.com/astral-sh/ruff/pull/8816))
|
||||
- \[`pylint`\] Implement fix for `unspecified-encoding` (`PLW1514`) ([#8928](https://github.com/astral-sh/ruff/pull/8928))
|
||||
|
||||
### Formatter
|
||||
|
||||
- Avoid unstable formatting in ellipsis-only body with trailing comment ([#8984](https://github.com/astral-sh/ruff/pull/8984))
|
||||
- Inline trailing comments for type alias similar to assignments ([#8941](https://github.com/astral-sh/ruff/pull/8941))
|
||||
- Insert trailing comma when function breaks with single argument ([#8921](https://github.com/astral-sh/ruff/pull/8921))
|
||||
|
||||
### CLI
|
||||
|
||||
- Update `ruff check` and `ruff format` to default to the current directory ([#8791](https://github.com/astral-sh/ruff/pull/8791))
|
||||
- Stop at the first resolved parent configuration ([#8864](https://github.com/astral-sh/ruff/pull/8864))
|
||||
|
||||
### Configuration
|
||||
|
||||
- \[`pylint`\] Default `max-positional-args` to `max-args` ([#8998](https://github.com/astral-sh/ruff/pull/8998))
|
||||
- \[`pylint`\] Add `allow-dunder-method-names` setting for `bad-dunder-method-name` (`PLW3201`) ([#8812](https://github.com/astral-sh/ruff/pull/8812))
|
||||
- \[`isort`\] Add support for `from-first` setting ([#8663](https://github.com/astral-sh/ruff/pull/8663))
|
||||
- \[`isort`\] Add support for `length-sort` settings ([#8841](https://github.com/astral-sh/ruff/pull/8841))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Add support for `@functools.singledispatch` ([#8934](https://github.com/astral-sh/ruff/pull/8934))
|
||||
- Avoid off-by-one error in stripping noqa following multi-byte char ([#8979](https://github.com/astral-sh/ruff/pull/8979))
|
||||
- Avoid off-by-one error in with-item named expressions ([#8915](https://github.com/astral-sh/ruff/pull/8915))
|
||||
- Avoid syntax error via invalid ur string prefix ([#8971](https://github.com/astral-sh/ruff/pull/8971))
|
||||
- Avoid underflow in `get_model` matching ([#8965](https://github.com/astral-sh/ruff/pull/8965))
|
||||
- Avoid unnecessary index diagnostics when value is modified ([#8970](https://github.com/astral-sh/ruff/pull/8970))
|
||||
- Convert over-indentation rule to use number of characters ([#8983](https://github.com/astral-sh/ruff/pull/8983))
|
||||
- Detect implicit returns in auto-return-types ([#8952](https://github.com/astral-sh/ruff/pull/8952))
|
||||
- Fix start >= end error in over-indentation ([#8982](https://github.com/astral-sh/ruff/pull/8982))
|
||||
- Ignore `@overload` and `@override` methods for too-many-arguments checks ([#8954](https://github.com/astral-sh/ruff/pull/8954))
|
||||
- Lexer start of line is false only for `Mode::Expression` ([#8880](https://github.com/astral-sh/ruff/pull/8880))
|
||||
- Mark `pydantic_settings.BaseSettings` as having default copy semantics ([#8793](https://github.com/astral-sh/ruff/pull/8793))
|
||||
- Respect dictionary unpacking in `NamedTuple` assignments ([#8810](https://github.com/astral-sh/ruff/pull/8810))
|
||||
- Respect local subclasses in `flake8-type-checking` ([#8768](https://github.com/astral-sh/ruff/pull/8768))
|
||||
- Support type alias statements in simple statement positions ([#8916](https://github.com/astral-sh/ruff/pull/8916))
|
||||
- \[`flake8-annotations`\] Avoid filtering out un-representable types in return annotation ([#8881](https://github.com/astral-sh/ruff/pull/8881))
|
||||
- \[`flake8-pie`\] Retain extra ellipses in protocols and abstract methods ([#8769](https://github.com/astral-sh/ruff/pull/8769))
|
||||
- \[`flake8-pyi`\] Respect local enum subclasses in `simple-defaults` (`PYI052`) ([#8767](https://github.com/astral-sh/ruff/pull/8767))
|
||||
- \[`flake8-trio`\] Use correct range for `TRIO115` fix ([#8933](https://github.com/astral-sh/ruff/pull/8933))
|
||||
- \[`flake8-trio`\] Use full arguments range for zero-sleep-call ([#8936](https://github.com/astral-sh/ruff/pull/8936))
|
||||
- \[`isort`\] fix: mark `__main__` as first-party import ([#8805](https://github.com/astral-sh/ruff/pull/8805))
|
||||
- \[`pep8-naming`\] Avoid `N806` errors for type alias statements ([#8785](https://github.com/astral-sh/ruff/pull/8785))
|
||||
- \[`perflint`\] Avoid `PERF101` if there's an append in loop body ([#8809](https://github.com/astral-sh/ruff/pull/8809))
|
||||
- \[`pycodestyle`\] Allow space-before-colon after end-of-slice ([#8838](https://github.com/astral-sh/ruff/pull/8838))
|
||||
- \[`pydocstyle`\] Avoid non-character breaks in `over-indentation` (`D208`) ([#8866](https://github.com/astral-sh/ruff/pull/8866))
|
||||
- \[`pydocstyle`\] Ignore underlines when determining docstring logical lines ([#8929](https://github.com/astral-sh/ruff/pull/8929))
|
||||
- \[`pylint`\] Extend `self-assigning-variable` to multi-target assignments ([#8839](https://github.com/astral-sh/ruff/pull/8839))
|
||||
- \[`tryceratops`\] Avoid repeated triggers in nested `tryceratops` diagnostics ([#8772](https://github.com/astral-sh/ruff/pull/8772))
|
||||
|
||||
### Documentation
|
||||
|
||||
- Add advice for fixing RUF008 when mutability is not desired ([#8853](https://github.com/astral-sh/ruff/pull/8853))
|
||||
- Added the command to run ruff using pkgx to the installation.md ([#8955](https://github.com/astral-sh/ruff/pull/8955))
|
||||
- Document fix safety for flake8-comprehensions and some pyupgrade rules ([#8918](https://github.com/astral-sh/ruff/pull/8918))
|
||||
- Fix doc formatting for zero-sleep-call ([#8937](https://github.com/astral-sh/ruff/pull/8937))
|
||||
- Remove duplicate imports from os-stat documentation ([#8930](https://github.com/astral-sh/ruff/pull/8930))
|
||||
- Replace generated reference to MkDocs ([#8806](https://github.com/astral-sh/ruff/pull/8806))
|
||||
- Update Arch Linux package URL in installation.md ([#8802](https://github.com/astral-sh/ruff/pull/8802))
|
||||
- \[`flake8-pyi`\] Fix error in `t-suffixed-type-alias` (`PYI043`) example ([#8963](https://github.com/astral-sh/ruff/pull/8963))
|
||||
- \[`flake8-pyi`\] Improve motivation for `custom-type-var-return-type` (`PYI019`) ([#8766](https://github.com/astral-sh/ruff/pull/8766))
|
||||
|
||||
## 0.1.6
|
||||
|
||||
### Preview features
|
||||
|
||||
64
Cargo.lock
generated
64
Cargo.lock
generated
@@ -808,7 +808,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -848,18 +848,18 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.0"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
|
||||
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fs-err"
|
||||
version = "2.10.0"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb5fd9bcbe8b1087cbd395b51498c01bc997cef73e778a80b77a811af5e2d29f"
|
||||
checksum = "88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
@@ -987,9 +987,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
|
||||
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
@@ -1170,9 +1170,9 @@ checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.65"
|
||||
version = "0.3.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8"
|
||||
checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
@@ -1622,9 +1622,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.0"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
|
||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
@@ -2062,7 +2062,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_cli"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.2",
|
||||
"anyhow",
|
||||
@@ -2198,7 +2198,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_linter"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"annotate-snippets 0.9.2",
|
||||
@@ -2450,7 +2450,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_shrinking"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -2618,9 +2618,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "schemars"
|
||||
version = "0.8.15"
|
||||
version = "0.8.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f7b0ce13155372a76ee2e1c5ffba1fe61ede73fbea5630d61eee6fac4929c0c"
|
||||
checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29"
|
||||
dependencies = [
|
||||
"dyn-clone",
|
||||
"schemars_derive",
|
||||
@@ -2630,9 +2630,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "schemars_derive"
|
||||
version = "0.8.15"
|
||||
version = "0.8.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e85e2a16b12bdb763244c69ab79363d71db2b4b918a2def53f80b02e0574b13c"
|
||||
checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3334,9 +3334,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "ureq"
|
||||
version = "2.8.0"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5ccd538d4a604753ebc2f17cd9946e89b77bf87f6a8e2309667c6f2e87855e3"
|
||||
checksum = "f8cdd25c339e200129fe4de81451814e5228c9b771d57378817d6117cc2b3f97"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"flate2",
|
||||
@@ -3350,9 +3350,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.4.1"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5"
|
||||
checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
@@ -3461,9 +3461,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.88"
|
||||
version = "0.2.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce"
|
||||
checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
@@ -3471,9 +3471,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.88"
|
||||
version = "0.2.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217"
|
||||
checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
@@ -3498,9 +3498,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.88"
|
||||
version = "0.2.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2"
|
||||
checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -3508,9 +3508,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.88"
|
||||
version = "0.2.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907"
|
||||
checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3521,9 +3521,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.88"
|
||||
version = "0.2.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b"
|
||||
checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test"
|
||||
|
||||
@@ -9,7 +9,7 @@ homepage = "https://docs.astral.sh/ruff"
|
||||
documentation = "https://docs.astral.sh/ruff"
|
||||
repository = "https://github.com/astral-sh/ruff"
|
||||
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||
license = "MIT"
|
||||
license = "MIT2"
|
||||
|
||||
[workspace.dependencies]
|
||||
anyhow = { version = "1.0.69" }
|
||||
@@ -33,7 +33,7 @@ proc-macro2 = { version = "1.0.70" }
|
||||
quote = { version = "1.0.23" }
|
||||
regex = { version = "1.10.2" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
schemars = { version = "0.8.15" }
|
||||
schemars = { version = "0.8.16" }
|
||||
serde = { version = "1.0.190", features = ["derive"] }
|
||||
serde_json = { version = "1.0.108" }
|
||||
shellexpand = { version = "3.0.0" }
|
||||
|
||||
@@ -150,7 +150,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff
|
||||
```yaml
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.1.6
|
||||
rev: v0.1.7
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
description = """
|
||||
Convert Flake8 configuration files to Ruff configuration files.
|
||||
"""
|
||||
|
||||
@@ -34,8 +34,8 @@ harness = false
|
||||
once_cell.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
url = "2.3.1"
|
||||
ureq = "2.8.0"
|
||||
url = "2.5.0"
|
||||
ureq = "2.9.1"
|
||||
criterion = { version = "0.5.1", default-features = false }
|
||||
codspeed-criterion-compat = { version="2.3.3", default-features = false, optional = true}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use ruff_benchmark::criterion::{
|
||||
criterion_group, criterion_main, BenchmarkId, Criterion, Throughput,
|
||||
};
|
||||
use ruff_benchmark::{TestCase, TestFile, TestFileDownloadError};
|
||||
use ruff_python_formatter::{format_module_ast, PyFormatOptions};
|
||||
use ruff_python_formatter::{format_module_ast, PreviewMode, PyFormatOptions};
|
||||
use ruff_python_index::CommentRangesBuilder;
|
||||
use ruff_python_parser::lexer::lex;
|
||||
use ruff_python_parser::{parse_tokens, Mode};
|
||||
@@ -69,7 +69,8 @@ fn benchmark_formatter(criterion: &mut Criterion) {
|
||||
.expect("Input to be a valid python program");
|
||||
|
||||
b.iter(|| {
|
||||
let options = PyFormatOptions::from_extension(Path::new(case.name()));
|
||||
let options = PyFormatOptions::from_extension(Path::new(case.name()))
|
||||
.with_preview(PreviewMode::Enabled);
|
||||
let formatted =
|
||||
format_module_ast(&module, &comment_ranges, case.code(), options)
|
||||
.expect("Formatting to succeed");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_cli"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
@@ -69,7 +69,7 @@ insta = { workspace = true, features = ["filters", "json"] }
|
||||
insta-cmd = { version = "0.4.0" }
|
||||
tempfile = "3.8.1"
|
||||
test-case = { workspace = true }
|
||||
ureq = { version = "2.8.0", features = [] }
|
||||
ureq = { version = "2.9.1", features = [] }
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
mimalloc = "0.1.39"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_linter"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
|
||||
@@ -39,3 +39,18 @@ def func():
|
||||
for i in range(1110):
|
||||
if True:
|
||||
break
|
||||
|
||||
# TODO(charlie): The `pass` here does not get properly redirected to the top of the
|
||||
# loop, unlike below.
|
||||
def func():
|
||||
for i in range(5):
|
||||
pass
|
||||
else:
|
||||
return 1
|
||||
|
||||
def func():
|
||||
for i in range(5):
|
||||
pass
|
||||
else:
|
||||
return 1
|
||||
x = 1
|
||||
|
||||
@@ -129,3 +129,11 @@ def func():
|
||||
print("Grass is green")
|
||||
case Color.BLUE:
|
||||
print("I'm feeling the blues :(")
|
||||
|
||||
|
||||
def func(point):
|
||||
match point:
|
||||
case (0, 0):
|
||||
print("Origin")
|
||||
case foo:
|
||||
raise ValueError("oops")
|
||||
|
||||
@@ -63,3 +63,122 @@ def func(x: int):
|
||||
return "str"
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def func(x: int):
|
||||
if x:
|
||||
return 1
|
||||
|
||||
|
||||
def func():
|
||||
x = 1
|
||||
|
||||
|
||||
def func(x: int):
|
||||
if x > 0:
|
||||
return 1
|
||||
|
||||
|
||||
def func(x: int):
|
||||
match x:
|
||||
case [1, 2, 3]:
|
||||
return 1
|
||||
case 4 as y:
|
||||
return "foo"
|
||||
|
||||
|
||||
def func(x: int):
|
||||
for i in range(5):
|
||||
if i > 0:
|
||||
return 1
|
||||
|
||||
|
||||
def func(x: int):
|
||||
for i in range(5):
|
||||
if i > 0:
|
||||
return 1
|
||||
else:
|
||||
return 4
|
||||
|
||||
|
||||
def func(x: int):
|
||||
for i in range(5):
|
||||
if i > 0:
|
||||
break
|
||||
else:
|
||||
return 4
|
||||
|
||||
|
||||
def func(x: int):
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
return 1
|
||||
|
||||
|
||||
def func(x: int):
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
return 1
|
||||
finally:
|
||||
return 2
|
||||
|
||||
|
||||
def func(x: int):
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
return 1
|
||||
else:
|
||||
return 2
|
||||
|
||||
|
||||
def func(x: int):
|
||||
try:
|
||||
return 1
|
||||
except:
|
||||
return 2
|
||||
else:
|
||||
pass
|
||||
|
||||
|
||||
def func(x: int):
|
||||
while x > 0:
|
||||
break
|
||||
return 1
|
||||
|
||||
|
||||
import abc
|
||||
from abc import abstractmethod
|
||||
|
||||
|
||||
class Foo(abc.ABC):
|
||||
@abstractmethod
|
||||
def method(self):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def method(self):
|
||||
"""Docstring."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def method(self):
|
||||
...
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def method():
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def method(cls):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def method(self):
|
||||
if self.x > 0:
|
||||
return 1
|
||||
else:
|
||||
return 1.5
|
||||
|
||||
@@ -10,7 +10,6 @@ Foo.objects.create(**{**bar}) # PIE804
|
||||
|
||||
foo(**{})
|
||||
|
||||
|
||||
foo(**{**data, "foo": "buzz"})
|
||||
foo(**buzz)
|
||||
foo(**{"bar-foo": True})
|
||||
@@ -20,3 +19,5 @@ foo(**{buzz: True})
|
||||
foo(**{"": True})
|
||||
foo(**{f"buzz__{bar}": True})
|
||||
abc(**{"for": 3})
|
||||
|
||||
foo(**{},)
|
||||
|
||||
@@ -22,3 +22,7 @@ Snake_case_alias: TypeAlias = int | float # PYI042, since not camel case
|
||||
|
||||
# check that this edge case doesn't crash
|
||||
_: TypeAlias = str | int
|
||||
|
||||
# PEP 695
|
||||
type foo_bar = int | str
|
||||
type FooBar = int | str
|
||||
|
||||
@@ -22,3 +22,7 @@ Snake_case_alias: TypeAlias = int | float # PYI042, since not camel case
|
||||
|
||||
# check that this edge case doesn't crash
|
||||
_: TypeAlias = str | int
|
||||
|
||||
# PEP 695
|
||||
type foo_bar = int | str
|
||||
type FooBar = int | str
|
||||
|
||||
@@ -21,3 +21,7 @@ _PrivateAliasS2: TypeAlias = Annotated[str, "also okay"]
|
||||
|
||||
# check that this edge case doesn't crash
|
||||
_: TypeAlias = str | int
|
||||
|
||||
# PEP 695
|
||||
type _FooT = str | int
|
||||
type Foo = str | int
|
||||
|
||||
@@ -21,3 +21,7 @@ _PrivateAliasS2: TypeAlias = Annotated[str, "also okay"]
|
||||
|
||||
# check that this edge case doesn't crash
|
||||
_: TypeAlias = str | int
|
||||
|
||||
# PEP 695
|
||||
type _FooT = str | int
|
||||
type Foo = str | int
|
||||
|
||||
@@ -10,3 +10,14 @@ def foo_no_return_typing_extensions(
|
||||
def foo_no_return_kwarg(arg: int, *, arg2: NoReturn): ... # Error: PYI050
|
||||
def foo_no_return_pos_only(arg: int, /, arg2: NoReturn): ... # Error: PYI050
|
||||
def foo_never(arg: Never): ...
|
||||
def foo_args(*args: NoReturn): ... # Error: PYI050
|
||||
def foo_kwargs(**kwargs: NoReturn): ... # Error: PYI050
|
||||
def foo_args_kwargs(*args: NoReturn, **kwargs: NoReturn): ... # Error: PYI050
|
||||
def foo_int_args(*args: int): ...
|
||||
def foo_int_kwargs(**kwargs: int): ...
|
||||
def foo_int_args_kwargs(*args: int, **kwargs: int): ...
|
||||
def foo_int_args_no_return(*args: int, **kwargs: NoReturn): ... # Error: PYI050
|
||||
def foo_int_kwargs_no_return(*args: NoReturn, **kwargs: int): ... # Error: PYI050
|
||||
def foo_args_never(*args: Never): ...
|
||||
def foo_kwargs_never(**kwargs: Never): ...
|
||||
def foo_args_kwargs_never(*args: Never, **kwargs: Never): ...
|
||||
|
||||
@@ -82,3 +82,14 @@ raise IndexError();
|
||||
|
||||
# RSE102
|
||||
raise Foo()
|
||||
|
||||
# OK
|
||||
raise ctypes.WinError()
|
||||
|
||||
|
||||
def func():
|
||||
pass
|
||||
|
||||
|
||||
# OK
|
||||
raise func()
|
||||
|
||||
2
crates/ruff_linter/resources/test/fixtures/isort/force_sort_within_sections_future.py
vendored
Normal file
2
crates/ruff_linter/resources/test/fixtures/isort/force_sort_within_sections_future.py
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
import __future__
|
||||
from __future__ import annotations
|
||||
2
crates/ruff_linter/resources/test/fixtures/isort/future_from.py
vendored
Normal file
2
crates/ruff_linter/resources/test/fixtures/isort/future_from.py
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
import __future__
|
||||
from __future__ import annotations
|
||||
@@ -52,3 +52,6 @@ def model_assign() -> None:
|
||||
|
||||
Bad = import_string("django.core.exceptions.ValidationError") # N806
|
||||
ValidationError = import_string("django.core.exceptions.ValidationError") # OK
|
||||
|
||||
Bad = apps.get_model() # N806
|
||||
Bad = apps.get_model(model_name="Stream") # N806
|
||||
|
||||
@@ -43,3 +43,6 @@ regex = '''
|
||||
''' # noqa
|
||||
|
||||
regex = '\\\_'
|
||||
|
||||
#: W605:1:7
|
||||
u'foo\ bar'
|
||||
|
||||
@@ -1,40 +1,54 @@
|
||||
# Same as `W605_0.py` but using f-strings instead.
|
||||
|
||||
#: W605:1:10
|
||||
regex = '\.png$'
|
||||
regex = f'\.png$'
|
||||
|
||||
#: W605:2:1
|
||||
regex = '''
|
||||
regex = f'''
|
||||
\.png$
|
||||
'''
|
||||
|
||||
#: W605:2:6
|
||||
f(
|
||||
'\_'
|
||||
f'\_'
|
||||
)
|
||||
|
||||
#: W605:4:6
|
||||
"""
|
||||
f"""
|
||||
multi-line
|
||||
literal
|
||||
with \_ somewhere
|
||||
in the middle
|
||||
"""
|
||||
|
||||
#: W605:1:38
|
||||
value = f'new line\nand invalid escape \_ here'
|
||||
|
||||
def f():
|
||||
#: W605:1:11
|
||||
return'\.png$'
|
||||
|
||||
#: Okay
|
||||
regex = r'\.png$'
|
||||
regex = '\\.png$'
|
||||
regex = r'''
|
||||
regex = fr'\.png$'
|
||||
regex = f'\\.png$'
|
||||
regex = fr'''
|
||||
\.png$
|
||||
'''
|
||||
regex = r'''
|
||||
regex = fr'''
|
||||
\\.png$
|
||||
'''
|
||||
s = '\\'
|
||||
regex = '\w' # noqa
|
||||
regex = '''
|
||||
s = f'\\'
|
||||
regex = f'\w' # noqa
|
||||
regex = f'''
|
||||
\w
|
||||
''' # noqa
|
||||
|
||||
regex = f'\\\_'
|
||||
value = f'\{{1}}'
|
||||
value = f'\{1}'
|
||||
value = f'{1:\}'
|
||||
value = f"{f"\{1}"}"
|
||||
value = rf"{f"\{1}"}"
|
||||
|
||||
# Okay
|
||||
value = rf'\{{1}}'
|
||||
value = rf'\{1}'
|
||||
value = rf'{1:\}'
|
||||
value = f"{rf"\{1}"}"
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
# Same as `W605_0.py` but using f-strings instead.
|
||||
|
||||
#: W605:1:10
|
||||
regex = f'\.png$'
|
||||
|
||||
#: W605:2:1
|
||||
regex = f'''
|
||||
\.png$
|
||||
'''
|
||||
|
||||
#: W605:2:6
|
||||
f(
|
||||
f'\_'
|
||||
)
|
||||
|
||||
#: W605:4:6
|
||||
f"""
|
||||
multi-line
|
||||
literal
|
||||
with \_ somewhere
|
||||
in the middle
|
||||
"""
|
||||
|
||||
#: W605:1:38
|
||||
value = f'new line\nand invalid escape \_ here'
|
||||
|
||||
|
||||
#: Okay
|
||||
regex = fr'\.png$'
|
||||
regex = f'\\.png$'
|
||||
regex = fr'''
|
||||
\.png$
|
||||
'''
|
||||
regex = fr'''
|
||||
\\.png$
|
||||
'''
|
||||
s = f'\\'
|
||||
regex = f'\w' # noqa
|
||||
regex = f'''
|
||||
\w
|
||||
''' # noqa
|
||||
|
||||
regex = f'\\\_'
|
||||
value = f'\{{1}}'
|
||||
value = f'\{1}'
|
||||
value = f'{1:\}'
|
||||
value = f"{f"\{1}"}"
|
||||
value = rf"{f"\{1}"}"
|
||||
|
||||
# Okay
|
||||
value = rf'\{{1}}'
|
||||
value = rf'\{1}'
|
||||
value = rf'{1:\}'
|
||||
value = f"{rf"\{1}"}"
|
||||
@@ -1,5 +1,16 @@
|
||||
"""
|
||||
Author
|
||||
"""
|
||||
|
||||
|
||||
class Platform:
|
||||
""" Remove sampler
|
||||
Args:
|
||||
Returns:
|
||||
"""
|
||||
|
||||
|
||||
def memory_test():
|
||||
"""
|
||||
参数含义:precision:精确到小数点后几位
|
||||
"""
|
||||
|
||||
@@ -32,3 +32,16 @@ def f(x, y, z, *, u, v, w): # Too many arguments (6/5)
|
||||
|
||||
def f(x, y, z, a, b, c, *, u, v, w): # Too many arguments (9/5)
|
||||
pass
|
||||
|
||||
|
||||
from typing import override, overload
|
||||
|
||||
|
||||
@override
|
||||
def f(x, y, z, a, b, c, *, u, v, w): # OK
|
||||
pass
|
||||
|
||||
|
||||
@overload
|
||||
def f(x, y, z, a, b, c, *, u, v, w): # OK
|
||||
pass
|
||||
|
||||
30
crates/ruff_linter/resources/test/fixtures/pylint/too_many_positional.py
vendored
Normal file
30
crates/ruff_linter/resources/test/fixtures/pylint/too_many_positional.py
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
def f(x, y, z, t, u, v, w, r): # Too many positional arguments (8/3)
|
||||
pass
|
||||
|
||||
|
||||
def f(x): # OK
|
||||
pass
|
||||
|
||||
|
||||
def f(x, y, z, _t, _u, _v, _w, r): # OK (underscore-prefixed names are ignored
|
||||
pass
|
||||
|
||||
|
||||
def f(x, y, z, *, u=1, v=1, r=1): # OK
|
||||
pass
|
||||
|
||||
|
||||
def f(x=1, y=1, z=1): # OK
|
||||
pass
|
||||
|
||||
|
||||
def f(x, y, z, /, u, v, w): # Too many positional arguments (6/3)
|
||||
pass
|
||||
|
||||
|
||||
def f(x, y, z, *, u, v, w): # OK
|
||||
pass
|
||||
|
||||
|
||||
def f(x, y, z, a, b, c, *, u, v, w): # Too many positional arguments (6/3)
|
||||
pass
|
||||
10
crates/ruff_linter/resources/test/fixtures/pylint/too_many_positional_params.py
vendored
Normal file
10
crates/ruff_linter/resources/test/fixtures/pylint/too_many_positional_params.py
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# Too many positional arguments (7/4) for max_positional=4
|
||||
# OK for dummy_variable_rgx ~ "skip_.*"
|
||||
def f(w, x, y, z, skip_t, skip_u, skip_v):
|
||||
pass
|
||||
|
||||
|
||||
# Too many positional arguments (7/4) for max_args=4
|
||||
# Too many positional arguments (7/3) for dummy_variable_rgx ~ "skip_.*"
|
||||
def f(w, x, y, z, t, u, v):
|
||||
pass
|
||||
40
crates/ruff_linter/resources/test/fixtures/pylint/unnecessary_dict_index_lookup.py
vendored
Normal file
40
crates/ruff_linter/resources/test/fixtures/pylint/unnecessary_dict_index_lookup.py
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
FRUITS = {"apple": 1, "orange": 10, "berry": 22}
|
||||
|
||||
def fix_these():
|
||||
[FRUITS[fruit_name] for fruit_name, fruit_count in FRUITS.items()] # PLR1733
|
||||
{FRUITS[fruit_name] for fruit_name, fruit_count in FRUITS.items()} # PLR1733
|
||||
{fruit_name: FRUITS[fruit_name] for fruit_name, fruit_count in FRUITS.items()} # PLR1733
|
||||
|
||||
for fruit_name, fruit_count in FRUITS.items():
|
||||
print(FRUITS[fruit_name]) # PLR1733
|
||||
blah = FRUITS[fruit_name] # PLR1733
|
||||
assert FRUITS[fruit_name] == "pear" # PLR1733
|
||||
|
||||
|
||||
def dont_fix_these():
|
||||
# once there is an assignment to the dict[index], we stop emitting diagnostics
|
||||
for fruit_name, fruit_count in FRUITS.items():
|
||||
FRUITS[fruit_name] = 0 # OK
|
||||
assert FRUITS[fruit_name] == 0 # OK
|
||||
|
||||
# once there is an assignment to the key, we stop emitting diagnostics
|
||||
for fruit_name, fruit_count in FRUITS.items():
|
||||
fruit_name = 0 # OK
|
||||
assert FRUITS[fruit_name] == 0 # OK
|
||||
|
||||
# once there is an assignment to the value, we stop emitting diagnostics
|
||||
for fruit_name, fruit_count in FRUITS.items():
|
||||
if fruit_count < 5:
|
||||
fruit_count = -fruit_count
|
||||
assert FRUITS[fruit_name] == 0 # OK
|
||||
|
||||
|
||||
def value_intentionally_unused():
|
||||
[FRUITS[fruit_name] for fruit_name, _ in FRUITS.items()] # OK
|
||||
{FRUITS[fruit_name] for fruit_name, _ in FRUITS.items()} # OK
|
||||
{fruit_name: FRUITS[fruit_name] for fruit_name, _ in FRUITS.items()} # OK
|
||||
|
||||
for fruit_name, _ in FRUITS.items():
|
||||
print(FRUITS[fruit_name]) # OK
|
||||
blah = FRUITS[fruit_name] # OK
|
||||
assert FRUITS[fruit_name] == "pear" # OK
|
||||
@@ -12,7 +12,7 @@ def fix_these():
|
||||
print(letters[index]) # PLR1736
|
||||
blah = letters[index] # PLR1736
|
||||
assert letters[index] == "d" # PLR1736
|
||||
|
||||
|
||||
for index, letter in builtins.enumerate(letters):
|
||||
print(letters[index]) # PLR1736
|
||||
blah = letters[index] # PLR1736
|
||||
@@ -22,38 +22,43 @@ def fix_these():
|
||||
def dont_fix_these():
|
||||
# once there is an assignment to the sequence[index], we stop emitting diagnostics
|
||||
for index, letter in enumerate(letters):
|
||||
letters[index] = "d" # Ok
|
||||
letters[index] += "e" # Ok
|
||||
assert letters[index] == "de" # Ok
|
||||
|
||||
letters[index] = "d" # OK
|
||||
letters[index] += "e" # OK
|
||||
assert letters[index] == "de" # OK
|
||||
|
||||
# once there is an assignment to the index, we stop emitting diagnostics
|
||||
for index, letter in enumerate(letters):
|
||||
index += 1 # Ok
|
||||
print(letters[index]) # Ok
|
||||
|
||||
index += 1 # OK
|
||||
print(letters[index]) # OK
|
||||
|
||||
# once there is an assignment to the sequence, we stop emitting diagnostics
|
||||
for index, letter in enumerate(letters):
|
||||
letters = ["d", "e", "f"] # Ok
|
||||
print(letters[index]) # Ok
|
||||
letters = ["d", "e", "f"] # OK
|
||||
print(letters[index]) # OK
|
||||
|
||||
# once there is an assignment to the value, we stop emitting diagnostics
|
||||
for index, letter in enumerate(letters):
|
||||
letter = "d"
|
||||
print(letters[index]) # OK
|
||||
|
||||
# once there is an deletion from or of the sequence or index, we stop emitting diagnostics
|
||||
for index, letter in enumerate(letters):
|
||||
del letters[index] # Ok
|
||||
print(letters[index]) # Ok
|
||||
del letters[index] # OK
|
||||
print(letters[index]) # OK
|
||||
for index, letter in enumerate(letters):
|
||||
del letters # Ok
|
||||
print(letters[index]) # Ok
|
||||
del letters # OK
|
||||
print(letters[index]) # OK
|
||||
for index, letter in enumerate(letters):
|
||||
del index # Ok
|
||||
print(letters[index]) # Ok
|
||||
del index # OK
|
||||
print(letters[index]) # OK
|
||||
|
||||
|
||||
def value_intentionally_unused():
|
||||
[letters[index] for index, _ in enumerate(letters)] # Ok
|
||||
{letters[index] for index, _ in enumerate(letters)} # Ok
|
||||
{index: letters[index] for index, _ in enumerate(letters)} # Ok
|
||||
[letters[index] for index, _ in enumerate(letters)] # OK
|
||||
{letters[index] for index, _ in enumerate(letters)} # OK
|
||||
{index: letters[index] for index, _ in enumerate(letters)} # OK
|
||||
|
||||
for index, _ in enumerate(letters):
|
||||
print(letters[index]) # Ok
|
||||
blah = letters[index] # Ok
|
||||
letters[index] = "d" # Ok
|
||||
print(letters[index]) # OK
|
||||
blah = letters[index] # OK
|
||||
letters[index] = "d" # OK
|
||||
|
||||
@@ -42,3 +42,30 @@ tempfile.SpooledTemporaryFile(0, "w", encoding="utf-8")
|
||||
tempfile.SpooledTemporaryFile(0, "w", -1, "utf-8")
|
||||
tempfile.SpooledTemporaryFile(0, "wb")
|
||||
tempfile.SpooledTemporaryFile(0, )
|
||||
|
||||
open("test.txt",)
|
||||
open()
|
||||
open(
|
||||
"test.txt", # comment
|
||||
)
|
||||
open(
|
||||
"test.txt",
|
||||
# comment
|
||||
)
|
||||
open(("test.txt"),)
|
||||
open(
|
||||
("test.txt"), # comment
|
||||
)
|
||||
open(
|
||||
("test.txt"),
|
||||
# comment
|
||||
)
|
||||
|
||||
open((("test.txt")),)
|
||||
open(
|
||||
(("test.txt")), # comment
|
||||
)
|
||||
open(
|
||||
(("test.txt")),
|
||||
# comment
|
||||
)
|
||||
|
||||
@@ -110,3 +110,10 @@ print('Hello %(arg)s' % bar['bop'])
|
||||
"%s" % (
|
||||
x, # comment
|
||||
)
|
||||
|
||||
|
||||
path = "%s-%s-%s.pem" % (
|
||||
safe_domain_name(cn), # common name, which should be filename safe because it is IDNA-encoded, but in case of a malformed cert make sure it's ok to use as a filename
|
||||
cert.not_valid_after.date().isoformat().replace("-", ""), # expiration date
|
||||
hexlify(cert.fingerprint(hashes.SHA256())).decode("ascii")[0:8], # fingerprint prefix
|
||||
)
|
||||
|
||||
@@ -23,3 +23,6 @@ print(a) # noqa: E501, F821 # comment
|
||||
print(a) # noqa: E501, F821 # comment
|
||||
print(a) # noqa: E501, F821 comment
|
||||
print(a) # noqa: E501, F821 comment
|
||||
|
||||
print(a) # comment with unicode µ # noqa: E501
|
||||
print(a) # comment with unicode µ # noqa: E501, F821
|
||||
|
||||
@@ -543,7 +543,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
flake8_bugbear::rules::no_explicit_stacklevel(checker, call);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryDictKwargs) {
|
||||
flake8_pie::rules::unnecessary_dict_kwargs(checker, expr, keywords);
|
||||
flake8_pie::rules::unnecessary_dict_kwargs(checker, call);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryRangeStart) {
|
||||
flake8_pie::rules::unnecessary_range_start(checker, call);
|
||||
@@ -1333,6 +1333,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::UnnecessaryListIndexLookup) {
|
||||
pylint::rules::unnecessary_list_index_lookup_comprehension(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryDictIndexLookup) {
|
||||
pylint::rules::unnecessary_dict_index_lookup_comprehension(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryComprehension) {
|
||||
flake8_comprehensions::rules::unnecessary_list_set_comprehension(
|
||||
checker, expr, elt, generators,
|
||||
@@ -1360,6 +1363,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::UnnecessaryListIndexLookup) {
|
||||
pylint::rules::unnecessary_list_index_lookup_comprehension(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryDictIndexLookup) {
|
||||
pylint::rules::unnecessary_dict_index_lookup_comprehension(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryComprehension) {
|
||||
flake8_comprehensions::rules::unnecessary_list_set_comprehension(
|
||||
checker, expr, elt, generators,
|
||||
@@ -1386,6 +1392,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::UnnecessaryListIndexLookup) {
|
||||
pylint::rules::unnecessary_list_index_lookup_comprehension(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryDictIndexLookup) {
|
||||
pylint::rules::unnecessary_dict_index_lookup_comprehension(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryComprehension) {
|
||||
flake8_comprehensions::rules::unnecessary_dict_comprehension(
|
||||
checker, expr, key, value, generators,
|
||||
@@ -1413,6 +1422,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::UnnecessaryListIndexLookup) {
|
||||
pylint::rules::unnecessary_list_index_lookup_comprehension(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryDictIndexLookup) {
|
||||
pylint::rules::unnecessary_dict_index_lookup_comprehension(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::FunctionUsesLoopVariable) {
|
||||
flake8_bugbear::rules::function_uses_loop_variable(checker, &Node::Expr(expr));
|
||||
}
|
||||
|
||||
@@ -248,7 +248,10 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
pylint::rules::property_with_parameters(checker, stmt, decorator_list, parameters);
|
||||
}
|
||||
if checker.enabled(Rule::TooManyArguments) {
|
||||
pylint::rules::too_many_arguments(checker, parameters, stmt);
|
||||
pylint::rules::too_many_arguments(checker, function_def);
|
||||
}
|
||||
if checker.enabled(Rule::TooManyPositional) {
|
||||
pylint::rules::too_many_positional(checker, function_def);
|
||||
}
|
||||
if checker.enabled(Rule::TooManyReturnStatements) {
|
||||
if let Some(diagnostic) = pylint::rules::too_many_return_statements(
|
||||
@@ -1280,6 +1283,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::UnnecessaryListIndexLookup) {
|
||||
pylint::rules::unnecessary_list_index_lookup(checker, for_stmt);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryDictIndexLookup) {
|
||||
pylint::rules::unnecessary_dict_index_lookup(checker, for_stmt);
|
||||
}
|
||||
if !is_async {
|
||||
if checker.enabled(Rule::ReimplementedBuiltin) {
|
||||
flake8_simplify::rules::convert_for_loop_to_any_all(checker, stmt);
|
||||
@@ -1531,6 +1537,14 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
}
|
||||
Stmt::TypeAlias(ast::StmtTypeAlias { name, .. }) => {
|
||||
if checker.enabled(Rule::SnakeCaseTypeAlias) {
|
||||
flake8_pyi::rules::snake_case_type_alias(checker, name);
|
||||
}
|
||||
if checker.enabled(Rule::TSuffixedTypeAlias) {
|
||||
flake8_pyi::rules::t_suffixed_type_alias(checker, name);
|
||||
}
|
||||
}
|
||||
Stmt::Delete(delete @ ast::StmtDelete { targets, range: _ }) => {
|
||||
if checker.enabled(Rule::GlobalStatement) {
|
||||
for target in targets {
|
||||
|
||||
@@ -816,7 +816,7 @@ where
|
||||
fn visit_expr(&mut self, expr: &'b Expr) {
|
||||
// Step 0: Pre-processing
|
||||
if !self.semantic.in_f_string()
|
||||
&& !self.semantic.in_literal()
|
||||
&& !self.semantic.in_typing_literal()
|
||||
&& !self.semantic.in_deferred_type_definition()
|
||||
&& self.semantic.in_type_definition()
|
||||
&& self.semantic.future_annotations()
|
||||
@@ -1198,7 +1198,7 @@ where
|
||||
) {
|
||||
// Ex) Literal["Class"]
|
||||
Some(typing::SubscriptKind::Literal) => {
|
||||
self.semantic.flags |= SemanticModelFlags::LITERAL;
|
||||
self.semantic.flags |= SemanticModelFlags::TYPING_LITERAL;
|
||||
|
||||
self.visit_expr(slice);
|
||||
self.visit_expr_context(ctx);
|
||||
@@ -1239,7 +1239,7 @@ where
|
||||
}
|
||||
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
|
||||
if self.semantic.in_type_definition()
|
||||
&& !self.semantic.in_literal()
|
||||
&& !self.semantic.in_typing_literal()
|
||||
&& !self.semantic.in_f_string()
|
||||
{
|
||||
self.deferred.string_type_definitions.push((
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
use std::path::Path;
|
||||
|
||||
use itertools::Itertools;
|
||||
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
||||
use ruff_text_size::{Ranged, TextLen, TextRange};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix};
|
||||
use ruff_python_trivia::CommentRanges;
|
||||
use ruff_python_trivia::{CommentRanges, PythonWhitespace};
|
||||
use ruff_source_file::Locator;
|
||||
|
||||
use crate::noqa;
|
||||
@@ -200,17 +200,11 @@ fn delete_noqa(range: TextRange, locator: &Locator) -> Edit {
|
||||
|
||||
// Compute the leading space.
|
||||
let prefix = locator.slice(TextRange::new(line_range.start(), range.start()));
|
||||
let leading_space = prefix
|
||||
.rfind(|c: char| !c.is_whitespace())
|
||||
.map_or(prefix.len(), |i| prefix.len() - i - 1);
|
||||
let leading_space_len = TextSize::try_from(leading_space).unwrap();
|
||||
let leading_space_len = prefix.text_len() - prefix.trim_whitespace_end().text_len();
|
||||
|
||||
// Compute the trailing space.
|
||||
let suffix = locator.slice(TextRange::new(range.end(), line_range.end()));
|
||||
let trailing_space = suffix
|
||||
.find(|c: char| !c.is_whitespace())
|
||||
.map_or(suffix.len(), |i| i);
|
||||
let trailing_space_len = TextSize::try_from(trailing_space).unwrap();
|
||||
let trailing_space_len = suffix.text_len() - suffix.trim_whitespace_start().text_len();
|
||||
|
||||
// Ex) `# noqa`
|
||||
if line_range
|
||||
|
||||
@@ -254,12 +254,14 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Pylint, "R0913") => (RuleGroup::Stable, rules::pylint::rules::TooManyArguments),
|
||||
(Pylint, "R0915") => (RuleGroup::Stable, rules::pylint::rules::TooManyStatements),
|
||||
(Pylint, "R0916") => (RuleGroup::Preview, rules::pylint::rules::TooManyBooleanExpressions),
|
||||
(Pylint, "R0917") => (RuleGroup::Preview, rules::pylint::rules::TooManyPositional),
|
||||
(Pylint, "R1701") => (RuleGroup::Stable, rules::pylint::rules::RepeatedIsinstanceCalls),
|
||||
(Pylint, "R1704") => (RuleGroup::Preview, rules::pylint::rules::RedefinedArgumentFromLocal),
|
||||
(Pylint, "R1711") => (RuleGroup::Stable, rules::pylint::rules::UselessReturn),
|
||||
(Pylint, "R1714") => (RuleGroup::Stable, rules::pylint::rules::RepeatedEqualityComparison),
|
||||
(Pylint, "R1706") => (RuleGroup::Preview, rules::pylint::rules::AndOrTernary),
|
||||
(Pylint, "R1722") => (RuleGroup::Stable, rules::pylint::rules::SysExitAlias),
|
||||
(Pylint, "R1733") => (RuleGroup::Preview, rules::pylint::rules::UnnecessaryDictIndexLookup),
|
||||
(Pylint, "R1736") => (RuleGroup::Preview, rules::pylint::rules::UnnecessaryListIndexLookup),
|
||||
(Pylint, "R2004") => (RuleGroup::Stable, rules::pylint::rules::MagicValueComparison),
|
||||
(Pylint, "R5501") => (RuleGroup::Stable, rules::pylint::rules::CollapsibleElseIf),
|
||||
|
||||
@@ -3,12 +3,14 @@
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
use ruff_diagnostics::Edit;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
||||
use ruff_python_ast::{self as ast, Arguments, ExceptHandler, Stmt};
|
||||
use ruff_python_ast::{AnyNodeRef, ArgOrKeyword};
|
||||
use ruff_python_codegen::Stylist;
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_python_trivia::{
|
||||
has_leading_content, is_python_whitespace, PythonWhitespace, SimpleTokenKind, SimpleTokenizer,
|
||||
has_leading_content, is_python_whitespace, CommentRanges, PythonWhitespace, SimpleTokenKind,
|
||||
SimpleTokenizer,
|
||||
};
|
||||
use ruff_source_file::{Locator, NewlineWithTrailingNewline, UniversalNewlines};
|
||||
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
||||
@@ -138,6 +140,32 @@ pub(crate) fn remove_argument<T: Ranged>(
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic function to add arguments or keyword arguments to function calls.
|
||||
pub(crate) fn add_argument(
|
||||
argument: &str,
|
||||
arguments: &Arguments,
|
||||
comment_ranges: &CommentRanges,
|
||||
source: &str,
|
||||
) -> Edit {
|
||||
if let Some(last) = arguments.arguments_source_order().last() {
|
||||
// Case 1: existing arguments, so append after the last argument.
|
||||
let last = parenthesized_range(
|
||||
match last {
|
||||
ArgOrKeyword::Arg(arg) => arg.into(),
|
||||
ArgOrKeyword::Keyword(keyword) => (&keyword.value).into(),
|
||||
},
|
||||
arguments.into(),
|
||||
comment_ranges,
|
||||
source,
|
||||
)
|
||||
.unwrap_or(last.range());
|
||||
Edit::insertion(format!(", {argument}"), last.end())
|
||||
} else {
|
||||
// Case 2: no arguments. Add argument, without any trailing comma.
|
||||
Edit::insertion(argument.to_string(), arguments.start() + TextSize::from(1))
|
||||
}
|
||||
}
|
||||
|
||||
/// Determine if a vector contains only one, specific element.
|
||||
fn is_only<T: PartialEq>(vec: &[T], value: &T) -> bool {
|
||||
vec.len() == 1 && vec[0] == *value
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use itertools::Itertools;
|
||||
use ruff_diagnostics::Edit;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::importer::{ImportRequest, Importer};
|
||||
use ruff_diagnostics::Edit;
|
||||
use ruff_python_ast::helpers::{
|
||||
pep_604_union, typing_optional, typing_union, ReturnStatementVisitor,
|
||||
implicit_return, pep_604_union, typing_optional, typing_union, ReturnStatementVisitor,
|
||||
};
|
||||
use ruff_python_ast::visitor::Visitor;
|
||||
use ruff_python_ast::{self as ast, Expr, ExprContext};
|
||||
@@ -13,6 +12,7 @@ use ruff_python_semantic::analyze::visibility;
|
||||
use ruff_python_semantic::{Definition, SemanticModel};
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
|
||||
use crate::importer::{ImportRequest, Importer};
|
||||
use crate::settings::types::PythonVersion;
|
||||
|
||||
/// Return the name of the function, if it's overloaded.
|
||||
@@ -48,14 +48,19 @@ pub(crate) fn auto_return_type(function: &ast::StmtFunctionDef) -> Option<AutoPy
|
||||
let returns = {
|
||||
let mut visitor = ReturnStatementVisitor::default();
|
||||
visitor.visit_body(&function.body);
|
||||
|
||||
// Ignore generators.
|
||||
if visitor.is_generator {
|
||||
return None;
|
||||
}
|
||||
|
||||
visitor.returns
|
||||
};
|
||||
|
||||
// Determine the return type of the first `return` statement.
|
||||
let (return_statement, returns) = returns.split_first()?;
|
||||
let Some((return_statement, returns)) = returns.split_first() else {
|
||||
return Some(AutoPythonType::Atom(PythonType::None));
|
||||
};
|
||||
let mut return_type = return_statement.value.as_deref().map_or(
|
||||
ResolvedPythonType::Atom(PythonType::None),
|
||||
ResolvedPythonType::from,
|
||||
@@ -69,6 +74,16 @@ pub(crate) fn auto_return_type(function: &ast::StmtFunctionDef) -> Option<AutoPy
|
||||
));
|
||||
}
|
||||
|
||||
// If the function has an implicit return, union with `None`, as in:
|
||||
// ```python
|
||||
// def func(x: int):
|
||||
// if x > 0:
|
||||
// return 1
|
||||
// ```
|
||||
if implicit_return(function) {
|
||||
return_type = return_type.union(ResolvedPythonType::Atom(PythonType::None));
|
||||
}
|
||||
|
||||
match return_type {
|
||||
ResolvedPythonType::Atom(python_type) => Some(AutoPythonType::Atom(python_type)),
|
||||
ResolvedPythonType::Union(python_types) => Some(AutoPythonType::Union(python_types)),
|
||||
|
||||
@@ -537,6 +537,19 @@ 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(_)
|
||||
)
|
||||
}
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
|
||||
/// Generate flake8-annotation checks for a given `Definition`.
|
||||
pub(crate) fn definition(
|
||||
checker: &Checker,
|
||||
@@ -725,16 +738,22 @@ pub(crate) fn definition(
|
||||
) {
|
||||
if is_method && visibility::is_classmethod(decorator_list, checker.semantic()) {
|
||||
if checker.enabled(Rule::MissingReturnTypeClassMethod) {
|
||||
let return_type = auto_return_type(function)
|
||||
.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 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 mut diagnostic = Diagnostic::new(
|
||||
MissingReturnTypeClassMethod {
|
||||
name: name.to_string(),
|
||||
@@ -752,16 +771,22 @@ pub(crate) fn definition(
|
||||
}
|
||||
} else if is_method && visibility::is_staticmethod(decorator_list, checker.semantic()) {
|
||||
if checker.enabled(Rule::MissingReturnTypeStaticMethod) {
|
||||
let return_type = 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 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 mut diagnostic = Diagnostic::new(
|
||||
MissingReturnTypeStaticMethod {
|
||||
name: name.to_string(),
|
||||
@@ -818,18 +843,25 @@ pub(crate) fn definition(
|
||||
match visibility {
|
||||
visibility::Visibility::Public => {
|
||||
if checker.enabled(Rule::MissingReturnTypeUndocumentedPublicFunction) {
|
||||
let return_type = 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 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 mut diagnostic = Diagnostic::new(
|
||||
MissingReturnTypeUndocumentedPublicFunction {
|
||||
name: name.to_string(),
|
||||
@@ -853,18 +885,25 @@ pub(crate) fn definition(
|
||||
}
|
||||
visibility::Visibility::Private => {
|
||||
if checker.enabled(Rule::MissingReturnTypePrivateFunction) {
|
||||
let return_type = 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 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 mut diagnostic = Diagnostic::new(
|
||||
MissingReturnTypePrivateFunction {
|
||||
name: name.to_string(),
|
||||
|
||||
@@ -200,4 +200,299 @@ auto_return_type.py:59:5: ANN201 [*] Missing return type annotation for public f
|
||||
61 61 | return 1
|
||||
62 62 | elif x > 5:
|
||||
|
||||
auto_return_type.py:68:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
68 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
69 | if x:
|
||||
70 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `int | None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
65 65 | return None
|
||||
66 66 |
|
||||
67 67 |
|
||||
68 |-def func(x: int):
|
||||
68 |+def func(x: int) -> int | None:
|
||||
69 69 | if x:
|
||||
70 70 | return 1
|
||||
71 71 |
|
||||
|
||||
auto_return_type.py:73:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
73 | def func():
|
||||
| ^^^^ ANN201
|
||||
74 | x = 1
|
||||
|
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
70 70 | return 1
|
||||
71 71 |
|
||||
72 72 |
|
||||
73 |-def func():
|
||||
73 |+def func() -> None:
|
||||
74 74 | x = 1
|
||||
75 75 |
|
||||
76 76 |
|
||||
|
||||
auto_return_type.py:77:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
77 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
78 | if x > 0:
|
||||
79 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `int | None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
74 74 | x = 1
|
||||
75 75 |
|
||||
76 76 |
|
||||
77 |-def func(x: int):
|
||||
77 |+def func(x: int) -> int | None:
|
||||
78 78 | if x > 0:
|
||||
79 79 | return 1
|
||||
80 80 |
|
||||
|
||||
auto_return_type.py:82:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
82 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
83 | match x:
|
||||
84 | case [1, 2, 3]:
|
||||
|
|
||||
= help: Add return type annotation: `str | int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
79 79 | return 1
|
||||
80 80 |
|
||||
81 81 |
|
||||
82 |-def func(x: int):
|
||||
82 |+def func(x: int) -> str | int:
|
||||
83 83 | match x:
|
||||
84 84 | case [1, 2, 3]:
|
||||
85 85 | return 1
|
||||
|
||||
auto_return_type.py:90:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
90 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
91 | for i in range(5):
|
||||
92 | if i > 0:
|
||||
|
|
||||
= help: Add return type annotation: `int | None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
87 87 | return "foo"
|
||||
88 88 |
|
||||
89 89 |
|
||||
90 |-def func(x: int):
|
||||
90 |+def func(x: int) -> int | None:
|
||||
91 91 | for i in range(5):
|
||||
92 92 | if i > 0:
|
||||
93 93 | return 1
|
||||
|
||||
auto_return_type.py:96:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
96 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
97 | for i in range(5):
|
||||
98 | if i > 0:
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
93 93 | return 1
|
||||
94 94 |
|
||||
95 95 |
|
||||
96 |-def func(x: int):
|
||||
96 |+def func(x: int) -> int:
|
||||
97 97 | for i in range(5):
|
||||
98 98 | if i > 0:
|
||||
99 99 | return 1
|
||||
|
||||
auto_return_type.py:104:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
104 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
105 | for i in range(5):
|
||||
106 | if i > 0:
|
||||
|
|
||||
= help: Add return type annotation: `int | None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
101 101 | return 4
|
||||
102 102 |
|
||||
103 103 |
|
||||
104 |-def func(x: int):
|
||||
104 |+def func(x: int) -> int | None:
|
||||
105 105 | for i in range(5):
|
||||
106 106 | if i > 0:
|
||||
107 107 | break
|
||||
|
||||
auto_return_type.py:112:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
112 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
113 | try:
|
||||
114 | pass
|
||||
|
|
||||
= help: Add return type annotation: `int | None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
109 109 | return 4
|
||||
110 110 |
|
||||
111 111 |
|
||||
112 |-def func(x: int):
|
||||
112 |+def func(x: int) -> int | None:
|
||||
113 113 | try:
|
||||
114 114 | pass
|
||||
115 115 | except:
|
||||
|
||||
auto_return_type.py:119:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
119 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
120 | try:
|
||||
121 | pass
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
116 116 | return 1
|
||||
117 117 |
|
||||
118 118 |
|
||||
119 |-def func(x: int):
|
||||
119 |+def func(x: int) -> int:
|
||||
120 120 | try:
|
||||
121 121 | pass
|
||||
122 122 | except:
|
||||
|
||||
auto_return_type.py:128:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
128 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
129 | try:
|
||||
130 | pass
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
125 125 | return 2
|
||||
126 126 |
|
||||
127 127 |
|
||||
128 |-def func(x: int):
|
||||
128 |+def func(x: int) -> int:
|
||||
129 129 | try:
|
||||
130 130 | pass
|
||||
131 131 | except:
|
||||
|
||||
auto_return_type.py:137:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
137 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
138 | try:
|
||||
139 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
134 134 | return 2
|
||||
135 135 |
|
||||
136 136 |
|
||||
137 |-def func(x: int):
|
||||
137 |+def func(x: int) -> int:
|
||||
138 138 | try:
|
||||
139 139 | return 1
|
||||
140 140 | except:
|
||||
|
||||
auto_return_type.py:146:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
146 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
147 | while x > 0:
|
||||
148 | break
|
||||
|
|
||||
= help: Add return type annotation: `int | None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
143 143 | pass
|
||||
144 144 |
|
||||
145 145 |
|
||||
146 |-def func(x: int):
|
||||
146 |+def func(x: int) -> int | None:
|
||||
147 147 | while x > 0:
|
||||
148 148 | break
|
||||
149 149 | return 1
|
||||
|
||||
auto_return_type.py:158:9: ANN201 Missing return type annotation for public function `method`
|
||||
|
|
||||
156 | class Foo(abc.ABC):
|
||||
157 | @abstractmethod
|
||||
158 | def method(self):
|
||||
| ^^^^^^ ANN201
|
||||
159 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
auto_return_type.py:162:9: ANN201 Missing return type annotation for public function `method`
|
||||
|
|
||||
161 | @abc.abstractmethod
|
||||
162 | def method(self):
|
||||
| ^^^^^^ ANN201
|
||||
163 | """Docstring."""
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
auto_return_type.py:166:9: ANN201 Missing return type annotation for public function `method`
|
||||
|
|
||||
165 | @abc.abstractmethod
|
||||
166 | def method(self):
|
||||
| ^^^^^^ ANN201
|
||||
167 | ...
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
auto_return_type.py:171:9: ANN205 Missing return type annotation for staticmethod `method`
|
||||
|
|
||||
169 | @staticmethod
|
||||
170 | @abstractmethod
|
||||
171 | def method():
|
||||
| ^^^^^^ ANN205
|
||||
172 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
auto_return_type.py:176:9: ANN206 Missing return type annotation for classmethod `method`
|
||||
|
|
||||
174 | @classmethod
|
||||
175 | @abstractmethod
|
||||
176 | def method(cls):
|
||||
| ^^^^^^ ANN206
|
||||
177 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
auto_return_type.py:180:9: ANN201 [*] Missing return type annotation for public function `method`
|
||||
|
|
||||
179 | @abstractmethod
|
||||
180 | def method(self):
|
||||
| ^^^^^^ ANN201
|
||||
181 | if self.x > 0:
|
||||
182 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `float`
|
||||
|
||||
ℹ Unsafe fix
|
||||
177 177 | pass
|
||||
178 178 |
|
||||
179 179 | @abstractmethod
|
||||
180 |- def method(self):
|
||||
180 |+ def method(self) -> float:
|
||||
181 181 | if self.x > 0:
|
||||
182 182 | return 1
|
||||
183 183 | else:
|
||||
|
||||
|
||||
|
||||
@@ -220,4 +220,334 @@ auto_return_type.py:59:5: ANN201 [*] Missing return type annotation for public f
|
||||
61 62 | return 1
|
||||
62 63 | elif x > 5:
|
||||
|
||||
auto_return_type.py:68:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
68 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
69 | if x:
|
||||
70 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `Optional[int]`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from typing import Optional
|
||||
1 2 | def func():
|
||||
2 3 | return 1
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
65 66 | return None
|
||||
66 67 |
|
||||
67 68 |
|
||||
68 |-def func(x: int):
|
||||
69 |+def func(x: int) -> Optional[int]:
|
||||
69 70 | if x:
|
||||
70 71 | return 1
|
||||
71 72 |
|
||||
|
||||
auto_return_type.py:73:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
73 | def func():
|
||||
| ^^^^ ANN201
|
||||
74 | x = 1
|
||||
|
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
70 70 | return 1
|
||||
71 71 |
|
||||
72 72 |
|
||||
73 |-def func():
|
||||
73 |+def func() -> None:
|
||||
74 74 | x = 1
|
||||
75 75 |
|
||||
76 76 |
|
||||
|
||||
auto_return_type.py:77:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
77 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
78 | if x > 0:
|
||||
79 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `Optional[int]`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from typing import Optional
|
||||
1 2 | def func():
|
||||
2 3 | return 1
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
74 75 | x = 1
|
||||
75 76 |
|
||||
76 77 |
|
||||
77 |-def func(x: int):
|
||||
78 |+def func(x: int) -> Optional[int]:
|
||||
78 79 | if x > 0:
|
||||
79 80 | return 1
|
||||
80 81 |
|
||||
|
||||
auto_return_type.py:82:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
82 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
83 | match x:
|
||||
84 | case [1, 2, 3]:
|
||||
|
|
||||
= help: Add return type annotation: `Union[str | int]`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from typing import Union
|
||||
1 2 | def func():
|
||||
2 3 | return 1
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
79 80 | return 1
|
||||
80 81 |
|
||||
81 82 |
|
||||
82 |-def func(x: int):
|
||||
83 |+def func(x: int) -> Union[str | int]:
|
||||
83 84 | match x:
|
||||
84 85 | case [1, 2, 3]:
|
||||
85 86 | return 1
|
||||
|
||||
auto_return_type.py:90:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
90 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
91 | for i in range(5):
|
||||
92 | if i > 0:
|
||||
|
|
||||
= help: Add return type annotation: `Optional[int]`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from typing import Optional
|
||||
1 2 | def func():
|
||||
2 3 | return 1
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
87 88 | return "foo"
|
||||
88 89 |
|
||||
89 90 |
|
||||
90 |-def func(x: int):
|
||||
91 |+def func(x: int) -> Optional[int]:
|
||||
91 92 | for i in range(5):
|
||||
92 93 | if i > 0:
|
||||
93 94 | return 1
|
||||
|
||||
auto_return_type.py:96:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
96 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
97 | for i in range(5):
|
||||
98 | if i > 0:
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
93 93 | return 1
|
||||
94 94 |
|
||||
95 95 |
|
||||
96 |-def func(x: int):
|
||||
96 |+def func(x: int) -> int:
|
||||
97 97 | for i in range(5):
|
||||
98 98 | if i > 0:
|
||||
99 99 | return 1
|
||||
|
||||
auto_return_type.py:104:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
104 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
105 | for i in range(5):
|
||||
106 | if i > 0:
|
||||
|
|
||||
= help: Add return type annotation: `Optional[int]`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from typing import Optional
|
||||
1 2 | def func():
|
||||
2 3 | return 1
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
101 102 | return 4
|
||||
102 103 |
|
||||
103 104 |
|
||||
104 |-def func(x: int):
|
||||
105 |+def func(x: int) -> Optional[int]:
|
||||
105 106 | for i in range(5):
|
||||
106 107 | if i > 0:
|
||||
107 108 | break
|
||||
|
||||
auto_return_type.py:112:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
112 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
113 | try:
|
||||
114 | pass
|
||||
|
|
||||
= help: Add return type annotation: `Optional[int]`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from typing import Optional
|
||||
1 2 | def func():
|
||||
2 3 | return 1
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
109 110 | return 4
|
||||
110 111 |
|
||||
111 112 |
|
||||
112 |-def func(x: int):
|
||||
113 |+def func(x: int) -> Optional[int]:
|
||||
113 114 | try:
|
||||
114 115 | pass
|
||||
115 116 | except:
|
||||
|
||||
auto_return_type.py:119:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
119 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
120 | try:
|
||||
121 | pass
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
116 116 | return 1
|
||||
117 117 |
|
||||
118 118 |
|
||||
119 |-def func(x: int):
|
||||
119 |+def func(x: int) -> int:
|
||||
120 120 | try:
|
||||
121 121 | pass
|
||||
122 122 | except:
|
||||
|
||||
auto_return_type.py:128:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
128 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
129 | try:
|
||||
130 | pass
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
125 125 | return 2
|
||||
126 126 |
|
||||
127 127 |
|
||||
128 |-def func(x: int):
|
||||
128 |+def func(x: int) -> int:
|
||||
129 129 | try:
|
||||
130 130 | pass
|
||||
131 131 | except:
|
||||
|
||||
auto_return_type.py:137:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
137 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
138 | try:
|
||||
139 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
134 134 | return 2
|
||||
135 135 |
|
||||
136 136 |
|
||||
137 |-def func(x: int):
|
||||
137 |+def func(x: int) -> int:
|
||||
138 138 | try:
|
||||
139 139 | return 1
|
||||
140 140 | except:
|
||||
|
||||
auto_return_type.py:146:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
146 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
147 | while x > 0:
|
||||
148 | break
|
||||
|
|
||||
= help: Add return type annotation: `Optional[int]`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from typing import Optional
|
||||
1 2 | def func():
|
||||
2 3 | return 1
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
143 144 | pass
|
||||
144 145 |
|
||||
145 146 |
|
||||
146 |-def func(x: int):
|
||||
147 |+def func(x: int) -> Optional[int]:
|
||||
147 148 | while x > 0:
|
||||
148 149 | break
|
||||
149 150 | return 1
|
||||
|
||||
auto_return_type.py:158:9: ANN201 Missing return type annotation for public function `method`
|
||||
|
|
||||
156 | class Foo(abc.ABC):
|
||||
157 | @abstractmethod
|
||||
158 | def method(self):
|
||||
| ^^^^^^ ANN201
|
||||
159 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
auto_return_type.py:162:9: ANN201 Missing return type annotation for public function `method`
|
||||
|
|
||||
161 | @abc.abstractmethod
|
||||
162 | def method(self):
|
||||
| ^^^^^^ ANN201
|
||||
163 | """Docstring."""
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
auto_return_type.py:166:9: ANN201 Missing return type annotation for public function `method`
|
||||
|
|
||||
165 | @abc.abstractmethod
|
||||
166 | def method(self):
|
||||
| ^^^^^^ ANN201
|
||||
167 | ...
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
auto_return_type.py:171:9: ANN205 Missing return type annotation for staticmethod `method`
|
||||
|
|
||||
169 | @staticmethod
|
||||
170 | @abstractmethod
|
||||
171 | def method():
|
||||
| ^^^^^^ ANN205
|
||||
172 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
auto_return_type.py:176:9: ANN206 Missing return type annotation for classmethod `method`
|
||||
|
|
||||
174 | @classmethod
|
||||
175 | @abstractmethod
|
||||
176 | def method(cls):
|
||||
| ^^^^^^ ANN206
|
||||
177 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
auto_return_type.py:180:9: ANN201 [*] Missing return type annotation for public function `method`
|
||||
|
|
||||
179 | @abstractmethod
|
||||
180 | def method(self):
|
||||
| ^^^^^^ ANN201
|
||||
181 | if self.x > 0:
|
||||
182 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `float`
|
||||
|
||||
ℹ Unsafe fix
|
||||
177 177 | pass
|
||||
178 178 |
|
||||
179 179 | @abstractmethod
|
||||
180 |- def method(self):
|
||||
180 |+ def method(self) -> float:
|
||||
181 181 | if self.x > 0:
|
||||
182 182 | return 1
|
||||
183 183 | else:
|
||||
|
||||
|
||||
|
||||
@@ -1,14 +1,24 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_annotations/mod.rs
|
||||
---
|
||||
annotation_presence.py:5:5: ANN201 Missing return type annotation for public function `foo`
|
||||
annotation_presence.py:5:5: ANN201 [*] Missing return type annotation for public function `foo`
|
||||
|
|
||||
4 | # Error
|
||||
5 | def foo(a, b):
|
||||
| ^^^ ANN201
|
||||
6 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
2 2 | from typing_extensions import override
|
||||
3 3 |
|
||||
4 4 | # Error
|
||||
5 |-def foo(a, b):
|
||||
5 |+def foo(a, b) -> None:
|
||||
6 6 | pass
|
||||
7 7 |
|
||||
8 8 |
|
||||
|
||||
annotation_presence.py:5:9: ANN001 Missing type annotation for function argument `a`
|
||||
|
|
||||
@@ -26,14 +36,24 @@ annotation_presence.py:5:12: ANN001 Missing type annotation for function argumen
|
||||
6 | pass
|
||||
|
|
||||
|
||||
annotation_presence.py:10:5: ANN201 Missing return type annotation for public function `foo`
|
||||
annotation_presence.py:10:5: ANN201 [*] Missing return type annotation for public function `foo`
|
||||
|
|
||||
9 | # Error
|
||||
10 | def foo(a: int, b):
|
||||
| ^^^ ANN201
|
||||
11 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
7 7 |
|
||||
8 8 |
|
||||
9 9 | # Error
|
||||
10 |-def foo(a: int, b):
|
||||
10 |+def foo(a: int, b) -> None:
|
||||
11 11 | pass
|
||||
12 12 |
|
||||
13 13 |
|
||||
|
||||
annotation_presence.py:10:17: ANN001 Missing type annotation for function argument `b`
|
||||
|
|
||||
@@ -51,23 +71,43 @@ annotation_presence.py:15:17: ANN001 Missing type annotation for function argume
|
||||
16 | pass
|
||||
|
|
||||
|
||||
annotation_presence.py:20:5: ANN201 Missing return type annotation for public function `foo`
|
||||
annotation_presence.py:20:5: ANN201 [*] Missing return type annotation for public function `foo`
|
||||
|
|
||||
19 | # Error
|
||||
20 | def foo(a: int, b: int):
|
||||
| ^^^ ANN201
|
||||
21 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
annotation_presence.py:25:5: ANN201 Missing return type annotation for public function `foo`
|
||||
ℹ Unsafe fix
|
||||
17 17 |
|
||||
18 18 |
|
||||
19 19 | # Error
|
||||
20 |-def foo(a: int, b: int):
|
||||
20 |+def foo(a: int, b: int) -> None:
|
||||
21 21 | pass
|
||||
22 22 |
|
||||
23 23 |
|
||||
|
||||
annotation_presence.py:25:5: ANN201 [*] Missing return type annotation for public function `foo`
|
||||
|
|
||||
24 | # Error
|
||||
25 | def foo():
|
||||
| ^^^ ANN201
|
||||
26 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
22 22 |
|
||||
23 23 |
|
||||
24 24 | # Error
|
||||
25 |-def foo():
|
||||
25 |+def foo() -> None:
|
||||
26 26 | pass
|
||||
27 27 |
|
||||
28 28 |
|
||||
|
||||
annotation_presence.py:45:12: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `a`
|
||||
|
|
||||
|
||||
@@ -1,13 +1,23 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_annotations/mod.rs
|
||||
---
|
||||
ignore_fully_untyped.py:24:5: ANN201 Missing return type annotation for public function `error_partially_typed_1`
|
||||
ignore_fully_untyped.py:24:5: ANN201 [*] Missing return type annotation for public function `error_partially_typed_1`
|
||||
|
|
||||
24 | def error_partially_typed_1(a: int, b):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ ANN201
|
||||
25 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
21 21 | pass
|
||||
22 22 |
|
||||
23 23 |
|
||||
24 |-def error_partially_typed_1(a: int, b):
|
||||
24 |+def error_partially_typed_1(a: int, b) -> None:
|
||||
25 25 | pass
|
||||
26 26 |
|
||||
27 27 |
|
||||
|
||||
ignore_fully_untyped.py:24:37: ANN001 Missing type annotation for function argument `b`
|
||||
|
|
||||
@@ -23,15 +33,25 @@ ignore_fully_untyped.py:28:37: ANN001 Missing type annotation for function argum
|
||||
29 | pass
|
||||
|
|
||||
|
||||
ignore_fully_untyped.py:32:5: ANN201 Missing return type annotation for public function `error_partially_typed_3`
|
||||
ignore_fully_untyped.py:32:5: ANN201 [*] Missing return type annotation for public function `error_partially_typed_3`
|
||||
|
|
||||
32 | def error_partially_typed_3(a: int, b: int):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ ANN201
|
||||
33 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ignore_fully_untyped.py:43:9: ANN201 Missing return type annotation for public function `error_typed_self`
|
||||
ℹ Unsafe fix
|
||||
29 29 | pass
|
||||
30 30 |
|
||||
31 31 |
|
||||
32 |-def error_partially_typed_3(a: int, b: int):
|
||||
32 |+def error_partially_typed_3(a: int, b: int) -> None:
|
||||
33 33 | pass
|
||||
34 34 |
|
||||
35 35 |
|
||||
|
||||
ignore_fully_untyped.py:43:9: ANN201 [*] Missing return type annotation for public function `error_typed_self`
|
||||
|
|
||||
41 | pass
|
||||
42 |
|
||||
@@ -39,6 +59,14 @@ ignore_fully_untyped.py:43:9: ANN201 Missing return type annotation for public f
|
||||
| ^^^^^^^^^^^^^^^^ ANN201
|
||||
44 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
40 40 | def ok_untyped_method(self):
|
||||
41 41 | pass
|
||||
42 42 |
|
||||
43 |- def error_typed_self(self: X):
|
||||
43 |+ def error_typed_self(self: X) -> None:
|
||||
44 44 | pass
|
||||
|
||||
|
||||
|
||||
@@ -41,14 +41,24 @@ mypy_init_return.py:11:9: ANN204 [*] Missing return type annotation for special
|
||||
13 13 |
|
||||
14 14 |
|
||||
|
||||
mypy_init_return.py:40:5: ANN202 Missing return type annotation for private function `__init__`
|
||||
mypy_init_return.py:40:5: ANN202 [*] Missing return type annotation for private function `__init__`
|
||||
|
|
||||
39 | # Error
|
||||
40 | def __init__(self, foo: int):
|
||||
| ^^^^^^^^ ANN202
|
||||
41 | ...
|
||||
|
|
||||
= help: Add return type annotation
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
37 37 |
|
||||
38 38 |
|
||||
39 39 | # Error
|
||||
40 |-def __init__(self, foo: int):
|
||||
40 |+def __init__(self, foo: int) -> None:
|
||||
41 41 | ...
|
||||
42 42 |
|
||||
43 43 |
|
||||
|
||||
mypy_init_return.py:47:9: ANN204 [*] Missing return type annotation for special method `__init__`
|
||||
|
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use itertools::Itertools;
|
||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||
use ruff_python_ast::{self as ast, Expr, Keyword};
|
||||
use ruff_python_ast::{self as ast, Expr};
|
||||
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_text_size::Ranged;
|
||||
@@ -8,6 +8,7 @@ use ruff_text_size::Ranged;
|
||||
use ruff_python_stdlib::identifiers::is_identifier;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::edits::{remove_argument, Parentheses};
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for unnecessary `dict` kwargs.
|
||||
@@ -52,8 +53,8 @@ impl AlwaysFixableViolation for UnnecessaryDictKwargs {
|
||||
}
|
||||
|
||||
/// PIE804
|
||||
pub(crate) fn unnecessary_dict_kwargs(checker: &mut Checker, expr: &Expr, kwargs: &[Keyword]) {
|
||||
for kw in kwargs {
|
||||
pub(crate) fn unnecessary_dict_kwargs(checker: &mut Checker, call: &ast::ExprCall) {
|
||||
for kw in &call.arguments.keywords {
|
||||
// keyword is a spread operator (indicated by None)
|
||||
if kw.arg.is_some() {
|
||||
continue;
|
||||
@@ -65,7 +66,7 @@ pub(crate) fn unnecessary_dict_kwargs(checker: &mut Checker, expr: &Expr, kwargs
|
||||
|
||||
// Ex) `foo(**{**bar})`
|
||||
if matches!(keys.as_slice(), [None]) {
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryDictKwargs, expr.range());
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryDictKwargs, call.range());
|
||||
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
format!("**{}", checker.locator().slice(values[0].range())),
|
||||
@@ -86,10 +87,18 @@ pub(crate) fn unnecessary_dict_kwargs(checker: &mut Checker, expr: &Expr, kwargs
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryDictKwargs, expr.range());
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryDictKwargs, call.range());
|
||||
|
||||
if values.is_empty() {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::deletion(kw.start(), kw.end())));
|
||||
diagnostic.try_set_fix(|| {
|
||||
remove_argument(
|
||||
kw,
|
||||
&call.arguments,
|
||||
Parentheses::Preserve,
|
||||
checker.locator().contents(),
|
||||
)
|
||||
.map(Fix::safe_edit)
|
||||
});
|
||||
} else {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
kwargs
|
||||
|
||||
@@ -106,6 +106,8 @@ PIE804.py:11:1: PIE804 [*] Unnecessary `dict` kwargs
|
||||
10 |
|
||||
11 | foo(**{})
|
||||
| ^^^^^^^^^ PIE804
|
||||
12 |
|
||||
13 | foo(**{**data, "foo": "buzz"})
|
||||
|
|
||||
= help: Remove unnecessary kwargs
|
||||
|
||||
@@ -116,7 +118,23 @@ PIE804.py:11:1: PIE804 [*] Unnecessary `dict` kwargs
|
||||
11 |-foo(**{})
|
||||
11 |+foo()
|
||||
12 12 |
|
||||
13 13 |
|
||||
14 14 | foo(**{**data, "foo": "buzz"})
|
||||
13 13 | foo(**{**data, "foo": "buzz"})
|
||||
14 14 | foo(**buzz)
|
||||
|
||||
PIE804.py:23:1: PIE804 [*] Unnecessary `dict` kwargs
|
||||
|
|
||||
21 | abc(**{"for": 3})
|
||||
22 |
|
||||
23 | foo(**{},)
|
||||
| ^^^^^^^^^^ PIE804
|
||||
|
|
||||
= help: Remove unnecessary kwargs
|
||||
|
||||
ℹ Safe fix
|
||||
20 20 | foo(**{f"buzz__{bar}": True})
|
||||
21 21 | abc(**{"for": 3})
|
||||
22 22 |
|
||||
23 |-foo(**{},)
|
||||
23 |+foo()
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::fmt;
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::Parameters;
|
||||
use ruff_python_ast::{Expr, Parameters};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
@@ -51,6 +51,9 @@ impl Violation for NoReturnArgumentAnnotationInStub {
|
||||
|
||||
/// PYI050
|
||||
pub(crate) fn no_return_argument_annotation(checker: &mut Checker, parameters: &Parameters) {
|
||||
// Ex) def func(arg: NoReturn): ...
|
||||
// Ex) def func(arg: NoReturn, /): ...
|
||||
// Ex) def func(*, arg: NoReturn): ...
|
||||
for annotation in parameters
|
||||
.posonlyargs
|
||||
.iter()
|
||||
@@ -58,19 +61,37 @@ pub(crate) fn no_return_argument_annotation(checker: &mut Checker, parameters: &
|
||||
.chain(¶meters.kwonlyargs)
|
||||
.filter_map(|arg| arg.parameter.annotation.as_ref())
|
||||
{
|
||||
if checker.semantic().match_typing_expr(annotation, "NoReturn") {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
NoReturnArgumentAnnotationInStub {
|
||||
module: if checker.settings.target_version >= Py311 {
|
||||
TypingModule::Typing
|
||||
} else {
|
||||
TypingModule::TypingExtensions
|
||||
},
|
||||
},
|
||||
annotation.range(),
|
||||
));
|
||||
check_no_return_argument_annotation(checker, annotation);
|
||||
}
|
||||
|
||||
// Ex) def func(*args: NoReturn): ...
|
||||
if let Some(arg) = ¶meters.vararg {
|
||||
if let Some(annotation) = &arg.annotation {
|
||||
check_no_return_argument_annotation(checker, annotation);
|
||||
}
|
||||
}
|
||||
|
||||
// Ex) def func(**kwargs: NoReturn): ...
|
||||
if let Some(arg) = ¶meters.kwarg {
|
||||
if let Some(annotation) = &arg.annotation {
|
||||
check_no_return_argument_annotation(checker, annotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_no_return_argument_annotation(checker: &mut Checker, annotation: &Expr) {
|
||||
if checker.semantic().match_typing_expr(annotation, "NoReturn") {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
NoReturnArgumentAnnotationInStub {
|
||||
module: if checker.settings.target_version >= Py311 {
|
||||
TypingModule::Typing
|
||||
} else {
|
||||
TypingModule::TypingExtensions
|
||||
},
|
||||
},
|
||||
annotation.range(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
|
||||
@@ -46,12 +46,16 @@ impl Violation for SnakeCaseTypeAlias {
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// MyTypeT = int
|
||||
/// from typing import TypeAlias
|
||||
///
|
||||
/// _MyTypeT: TypeAlias = int
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// MyType = int
|
||||
/// from typing import TypeAlias
|
||||
///
|
||||
/// _MyType: TypeAlias = int
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct TSuffixedTypeAlias {
|
||||
|
||||
@@ -29,4 +29,12 @@ PYI042.py:20:1: PYI042 Type alias `_snake_case_alias2` should be CamelCase
|
||||
21 | Snake_case_alias: TypeAlias = int | float # PYI042, since not camel case
|
||||
|
|
||||
|
||||
PYI042.py:27:6: PYI042 Type alias `foo_bar` should be CamelCase
|
||||
|
|
||||
26 | # PEP 695
|
||||
27 | type foo_bar = int | str
|
||||
| ^^^^^^^ PYI042
|
||||
28 | type FooBar = int | str
|
||||
|
|
||||
|
||||
|
||||
|
||||
@@ -29,4 +29,12 @@ PYI042.pyi:20:1: PYI042 Type alias `_snake_case_alias2` should be CamelCase
|
||||
21 | Snake_case_alias: TypeAlias = int | float # PYI042, since not camel case
|
||||
|
|
||||
|
||||
PYI042.pyi:27:6: PYI042 Type alias `foo_bar` should be CamelCase
|
||||
|
|
||||
26 | # PEP 695
|
||||
27 | type foo_bar = int | str
|
||||
| ^^^^^^^ PYI042
|
||||
28 | type FooBar = int | str
|
||||
|
|
||||
|
||||
|
||||
|
||||
@@ -30,4 +30,12 @@ PYI043.py:12:1: PYI043 Private type alias `_PrivateAliasT3` should not be suffix
|
||||
14 | ] # PYI043, since this ends in a T
|
||||
|
|
||||
|
||||
PYI043.py:26:6: PYI043 Private type alias `_FooT` should not be suffixed with `T` (the `T` suffix implies that an object is a `TypeVar`)
|
||||
|
|
||||
25 | # PEP 695
|
||||
26 | type _FooT = str | int
|
||||
| ^^^^^ PYI043
|
||||
27 | type Foo = str | int
|
||||
|
|
||||
|
||||
|
||||
|
||||
@@ -30,4 +30,12 @@ PYI043.pyi:12:1: PYI043 Private type alias `_PrivateAliasT3` should not be suffi
|
||||
14 | ] # PYI043, since this ends in a T
|
||||
|
|
||||
|
||||
PYI043.pyi:26:6: PYI043 Private type alias `_FooT` should not be suffixed with `T` (the `T` suffix implies that an object is a `TypeVar`)
|
||||
|
|
||||
25 | # PEP 695
|
||||
26 | type _FooT = str | int
|
||||
| ^^^^^ PYI043
|
||||
27 | type Foo = str | int
|
||||
|
|
||||
|
||||
|
||||
|
||||
@@ -28,6 +28,67 @@ PYI050.pyi:11:47: PYI050 Prefer `typing.Never` over `NoReturn` for argument anno
|
||||
11 | def foo_no_return_pos_only(arg: int, /, arg2: NoReturn): ... # Error: PYI050
|
||||
| ^^^^^^^^ PYI050
|
||||
12 | def foo_never(arg: Never): ...
|
||||
13 | def foo_args(*args: NoReturn): ... # Error: PYI050
|
||||
|
|
||||
|
||||
PYI050.pyi:13:21: PYI050 Prefer `typing.Never` over `NoReturn` for argument annotations
|
||||
|
|
||||
11 | def foo_no_return_pos_only(arg: int, /, arg2: NoReturn): ... # Error: PYI050
|
||||
12 | def foo_never(arg: Never): ...
|
||||
13 | def foo_args(*args: NoReturn): ... # Error: PYI050
|
||||
| ^^^^^^^^ PYI050
|
||||
14 | def foo_kwargs(**kwargs: NoReturn): ... # Error: PYI050
|
||||
15 | def foo_args_kwargs(*args: NoReturn, **kwargs: NoReturn): ... # Error: PYI050
|
||||
|
|
||||
|
||||
PYI050.pyi:14:26: PYI050 Prefer `typing.Never` over `NoReturn` for argument annotations
|
||||
|
|
||||
12 | def foo_never(arg: Never): ...
|
||||
13 | def foo_args(*args: NoReturn): ... # Error: PYI050
|
||||
14 | def foo_kwargs(**kwargs: NoReturn): ... # Error: PYI050
|
||||
| ^^^^^^^^ PYI050
|
||||
15 | def foo_args_kwargs(*args: NoReturn, **kwargs: NoReturn): ... # Error: PYI050
|
||||
16 | def foo_int_args(*args: int): ...
|
||||
|
|
||||
|
||||
PYI050.pyi:15:28: PYI050 Prefer `typing.Never` over `NoReturn` for argument annotations
|
||||
|
|
||||
13 | def foo_args(*args: NoReturn): ... # Error: PYI050
|
||||
14 | def foo_kwargs(**kwargs: NoReturn): ... # Error: PYI050
|
||||
15 | def foo_args_kwargs(*args: NoReturn, **kwargs: NoReturn): ... # Error: PYI050
|
||||
| ^^^^^^^^ PYI050
|
||||
16 | def foo_int_args(*args: int): ...
|
||||
17 | def foo_int_kwargs(**kwargs: int): ...
|
||||
|
|
||||
|
||||
PYI050.pyi:15:48: PYI050 Prefer `typing.Never` over `NoReturn` for argument annotations
|
||||
|
|
||||
13 | def foo_args(*args: NoReturn): ... # Error: PYI050
|
||||
14 | def foo_kwargs(**kwargs: NoReturn): ... # Error: PYI050
|
||||
15 | def foo_args_kwargs(*args: NoReturn, **kwargs: NoReturn): ... # Error: PYI050
|
||||
| ^^^^^^^^ PYI050
|
||||
16 | def foo_int_args(*args: int): ...
|
||||
17 | def foo_int_kwargs(**kwargs: int): ...
|
||||
|
|
||||
|
||||
PYI050.pyi:19:50: PYI050 Prefer `typing.Never` over `NoReturn` for argument annotations
|
||||
|
|
||||
17 | def foo_int_kwargs(**kwargs: int): ...
|
||||
18 | def foo_int_args_kwargs(*args: int, **kwargs: int): ...
|
||||
19 | def foo_int_args_no_return(*args: int, **kwargs: NoReturn): ... # Error: PYI050
|
||||
| ^^^^^^^^ PYI050
|
||||
20 | def foo_int_kwargs_no_return(*args: NoReturn, **kwargs: int): ... # Error: PYI050
|
||||
21 | def foo_args_never(*args: Never): ...
|
||||
|
|
||||
|
||||
PYI050.pyi:20:37: PYI050 Prefer `typing.Never` over `NoReturn` for argument annotations
|
||||
|
|
||||
18 | def foo_int_args_kwargs(*args: int, **kwargs: int): ...
|
||||
19 | def foo_int_args_no_return(*args: int, **kwargs: NoReturn): ... # Error: PYI050
|
||||
20 | def foo_int_kwargs_no_return(*args: NoReturn, **kwargs: int): ... # Error: PYI050
|
||||
| ^^^^^^^^ PYI050
|
||||
21 | def foo_args_never(*args: Never): ...
|
||||
22 | def foo_kwargs_never(**kwargs: Never): ...
|
||||
|
|
||||
|
||||
|
||||
|
||||
@@ -78,9 +78,7 @@ pub(crate) fn unnecessary_paren_on_raise_exception(checker: &mut Checker, expr:
|
||||
|
||||
// `ctypes.WinError()` is a function, not a class. It's part of the standard library, so
|
||||
// we might as well get it right.
|
||||
if exception_type
|
||||
.as_ref()
|
||||
.is_some_and(ExceptionType::is_builtin)
|
||||
if exception_type.is_none()
|
||||
&& checker
|
||||
.semantic()
|
||||
.resolve_call_path(func)
|
||||
|
||||
@@ -266,6 +266,8 @@ RSE102.py:84:10: RSE102 [*] Unnecessary parentheses on raised exception
|
||||
83 | # RSE102
|
||||
84 | raise Foo()
|
||||
| ^^ RSE102
|
||||
85 |
|
||||
86 | # OK
|
||||
|
|
||||
= help: Remove unnecessary parentheses
|
||||
|
||||
@@ -275,5 +277,8 @@ RSE102.py:84:10: RSE102 [*] Unnecessary parentheses on raised exception
|
||||
83 83 | # RSE102
|
||||
84 |-raise Foo()
|
||||
84 |+raise Foo
|
||||
85 85 |
|
||||
86 86 | # OK
|
||||
87 87 | raise ctypes.WinError()
|
||||
|
||||
|
||||
|
||||
@@ -66,6 +66,10 @@ pub(crate) fn private_member_access(checker: &mut Checker, expr: &Expr) {
|
||||
return;
|
||||
};
|
||||
|
||||
if checker.semantic().in_annotation() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (attr.starts_with("__") && !attr.ends_with("__"))
|
||||
|| (attr.starts_with('_') && !attr.starts_with("__"))
|
||||
{
|
||||
|
||||
@@ -180,7 +180,7 @@ fn format_import_block(
|
||||
continue;
|
||||
};
|
||||
|
||||
let imports = order_imports(import_block, settings);
|
||||
let imports = order_imports(import_block, import_section, settings);
|
||||
|
||||
// Add a blank line between every section.
|
||||
if is_first_block {
|
||||
@@ -291,6 +291,7 @@ mod tests {
|
||||
#[test_case(Path::new("force_sort_within_sections.py"))]
|
||||
#[test_case(Path::new("force_to_top.py"))]
|
||||
#[test_case(Path::new("force_wrap_aliases.py"))]
|
||||
#[test_case(Path::new("future_from.py"))]
|
||||
#[test_case(Path::new("if_elif_else.py"))]
|
||||
#[test_case(Path::new("import_from_after_import.py"))]
|
||||
#[test_case(Path::new("inline_comments.py"))]
|
||||
@@ -701,6 +702,7 @@ mod tests {
|
||||
|
||||
#[test_case(Path::new("force_sort_within_sections.py"))]
|
||||
#[test_case(Path::new("force_sort_within_sections_with_as_names.py"))]
|
||||
#[test_case(Path::new("force_sort_within_sections_future.py"))]
|
||||
fn force_sort_within_sections(path: &Path) -> Result<()> {
|
||||
let snapshot = format!("force_sort_within_sections_{}", path.to_string_lossy());
|
||||
let mut diagnostics = test_path(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::rules::isort::sorting::ImportStyle;
|
||||
use crate::rules::isort::{ImportSection, ImportType};
|
||||
use itertools::Itertools;
|
||||
|
||||
use super::settings::Settings;
|
||||
@@ -8,6 +9,7 @@ use super::types::{AliasData, CommentSet, ImportBlock, ImportFromStatement};
|
||||
|
||||
pub(crate) fn order_imports<'a>(
|
||||
block: ImportBlock<'a>,
|
||||
section: &ImportSection,
|
||||
settings: &Settings,
|
||||
) -> Vec<EitherImport<'a>> {
|
||||
let straight_imports = block.import.into_iter();
|
||||
@@ -52,7 +54,35 @@ pub(crate) fn order_imports<'a>(
|
||||
},
|
||||
);
|
||||
|
||||
let ordered_imports = if settings.force_sort_within_sections {
|
||||
let ordered_imports = if matches!(section, ImportSection::Known(ImportType::Future)) {
|
||||
from_imports
|
||||
.sorted_by_cached_key(|(import_from, _, _, aliases)| {
|
||||
ModuleKey::from_module(
|
||||
import_from.module,
|
||||
None,
|
||||
import_from.level,
|
||||
aliases.first().map(|(alias, _)| (alias.name, alias.asname)),
|
||||
ImportStyle::From,
|
||||
settings,
|
||||
)
|
||||
})
|
||||
.map(ImportFrom)
|
||||
.chain(
|
||||
straight_imports
|
||||
.sorted_by_cached_key(|(alias, _)| {
|
||||
ModuleKey::from_module(
|
||||
Some(alias.name),
|
||||
alias.asname,
|
||||
None,
|
||||
None,
|
||||
ImportStyle::Straight,
|
||||
settings,
|
||||
)
|
||||
})
|
||||
.map(Import),
|
||||
)
|
||||
.collect()
|
||||
} else if settings.force_sort_within_sections {
|
||||
straight_imports
|
||||
.map(Import)
|
||||
.chain(from_imports.map(ImportFrom))
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/isort/mod.rs
|
||||
---
|
||||
force_sort_within_sections_future.py:1:1: I001 [*] Import block is un-sorted or un-formatted
|
||||
|
|
||||
1 | / import __future__
|
||||
2 | | from __future__ import annotations
|
||||
|
|
||||
= help: Organize imports
|
||||
|
||||
ℹ Safe fix
|
||||
1 |+from __future__ import annotations
|
||||
1 2 | import __future__
|
||||
2 |-from __future__ import annotations
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/isort/mod.rs
|
||||
---
|
||||
future_from.py:1:1: I001 [*] Import block is un-sorted or un-formatted
|
||||
|
|
||||
1 | / import __future__
|
||||
2 | | from __future__ import annotations
|
||||
|
|
||||
= help: Organize imports
|
||||
|
||||
ℹ Safe fix
|
||||
1 |+from __future__ import annotations
|
||||
1 2 | import __future__
|
||||
2 |-from __future__ import annotations
|
||||
|
||||
|
||||
@@ -101,11 +101,15 @@ pub(super) fn is_django_model_import(name: &str, stmt: &Stmt, semantic: &Semanti
|
||||
return false;
|
||||
};
|
||||
|
||||
if arguments.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Match against, e.g., `apps.get_model("zerver", "Attachment")`.
|
||||
if let Some(call_path) = collect_call_path(func.as_ref()) {
|
||||
if matches!(call_path.as_slice(), [.., "get_model"]) {
|
||||
if let Some(argument) =
|
||||
arguments.find_argument("model_name", arguments.args.len() - 1)
|
||||
arguments.find_argument("model_name", arguments.args.len().saturating_sub(1))
|
||||
{
|
||||
if let Some(string_literal) = argument.as_string_literal_expr() {
|
||||
return string_literal.value.to_str() == name;
|
||||
|
||||
@@ -38,4 +38,20 @@ N806.py:53:5: N806 Variable `Bad` in function should be lowercase
|
||||
54 | ValidationError = import_string("django.core.exceptions.ValidationError") # OK
|
||||
|
|
||||
|
||||
N806.py:56:5: N806 Variable `Bad` in function should be lowercase
|
||||
|
|
||||
54 | ValidationError = import_string("django.core.exceptions.ValidationError") # OK
|
||||
55 |
|
||||
56 | Bad = apps.get_model() # N806
|
||||
| ^^^ N806
|
||||
57 | Bad = apps.get_model(model_name="Stream") # N806
|
||||
|
|
||||
|
||||
N806.py:57:5: N806 Variable `Bad` in function should be lowercase
|
||||
|
|
||||
56 | Bad = apps.get_model() # N806
|
||||
57 | Bad = apps.get_model(model_name="Stream") # N806
|
||||
| ^^^ N806
|
||||
|
|
||||
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ mod tests {
|
||||
#[test_case(Rule::BlankLineWithWhitespace, Path::new("W29.py"))]
|
||||
#[test_case(Rule::InvalidEscapeSequence, Path::new("W605_0.py"))]
|
||||
#[test_case(Rule::InvalidEscapeSequence, Path::new("W605_1.py"))]
|
||||
#[test_case(Rule::InvalidEscapeSequence, Path::new("W605_2.py"))]
|
||||
#[test_case(Rule::LineTooLong, Path::new("E501.py"))]
|
||||
#[test_case(Rule::LineTooLong, Path::new("E501_3.py"))]
|
||||
#[test_case(Rule::MixedSpacesAndTabs, Path::new("E101.py"))]
|
||||
|
||||
@@ -3,9 +3,9 @@ use memchr::memchr_iter;
|
||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_python_parser::Tok;
|
||||
use ruff_python_parser::{StringKind, Tok};
|
||||
use ruff_source_file::Locator;
|
||||
use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
||||
|
||||
use crate::fix::edits::pad_start;
|
||||
|
||||
@@ -58,18 +58,6 @@ impl AlwaysFixableViolation for InvalidEscapeSequence {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum FixTitle {
|
||||
AddBackslash,
|
||||
UseRawStringLiteral,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct InvalidEscapeChar {
|
||||
ch: char,
|
||||
range: TextRange,
|
||||
}
|
||||
|
||||
/// W605
|
||||
pub(crate) fn invalid_escape_sequence(
|
||||
diagnostics: &mut Vec<Diagnostic>,
|
||||
@@ -195,41 +183,77 @@ pub(crate) fn invalid_escape_sequence(
|
||||
if contains_valid_escape_sequence {
|
||||
// Escape with backslash.
|
||||
for invalid_escape_char in &invalid_escape_chars {
|
||||
let diagnostic = Diagnostic::new(
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
InvalidEscapeSequence {
|
||||
ch: invalid_escape_char.ch,
|
||||
fix_title: FixTitle::AddBackslash,
|
||||
},
|
||||
invalid_escape_char.range,
|
||||
)
|
||||
.with_fix(Fix::safe_edit(Edit::insertion(
|
||||
invalid_escape_char.range(),
|
||||
);
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::insertion(
|
||||
r"\".to_string(),
|
||||
invalid_escape_char.range.start() + TextSize::from(1),
|
||||
invalid_escape_char.start() + TextSize::from(1),
|
||||
)));
|
||||
invalid_escape_sequence.push(diagnostic);
|
||||
}
|
||||
} else {
|
||||
// Turn into raw string.
|
||||
for invalid_escape_char in &invalid_escape_chars {
|
||||
let diagnostic = Diagnostic::new(
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
InvalidEscapeSequence {
|
||||
ch: invalid_escape_char.ch,
|
||||
fix_title: FixTitle::UseRawStringLiteral,
|
||||
},
|
||||
invalid_escape_char.range,
|
||||
)
|
||||
.with_fix(
|
||||
// If necessary, add a space between any leading keyword (`return`, `yield`,
|
||||
// `assert`, etc.) and the string. For example, `return"foo"` is valid, but
|
||||
// `returnr"foo"` is not.
|
||||
Fix::safe_edit(Edit::insertion(
|
||||
pad_start("r".to_string(), string_start_location, locator),
|
||||
string_start_location,
|
||||
)),
|
||||
invalid_escape_char.range(),
|
||||
);
|
||||
|
||||
if matches!(
|
||||
token,
|
||||
Tok::String {
|
||||
kind: StringKind::Unicode,
|
||||
..
|
||||
}
|
||||
) {
|
||||
// Replace the Unicode prefix with `r`.
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::replacement(
|
||||
"r".to_string(),
|
||||
string_start_location,
|
||||
string_start_location + TextSize::from(1),
|
||||
)));
|
||||
} else {
|
||||
// Insert the `r` prefix.
|
||||
diagnostic.set_fix(
|
||||
// If necessary, add a space between any leading keyword (`return`, `yield`,
|
||||
// `assert`, etc.) and the string. For example, `return"foo"` is valid, but
|
||||
// `returnr"foo"` is not.
|
||||
Fix::safe_edit(Edit::insertion(
|
||||
pad_start("r".to_string(), string_start_location, locator),
|
||||
string_start_location,
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
invalid_escape_sequence.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
diagnostics.extend(invalid_escape_sequence);
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum FixTitle {
|
||||
AddBackslash,
|
||||
UseRawStringLiteral,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct InvalidEscapeChar {
|
||||
ch: char,
|
||||
range: TextRange,
|
||||
}
|
||||
|
||||
impl Ranged for InvalidEscapeChar {
|
||||
fn range(&self) -> TextRange {
|
||||
self.range
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,6 +125,8 @@ W605_0.py:45:12: W605 [*] Invalid escape sequence: `\_`
|
||||
44 |
|
||||
45 | regex = '\\\_'
|
||||
| ^^ W605
|
||||
46 |
|
||||
47 | #: W605:1:7
|
||||
|
|
||||
= help: Add backslash to escape sequence
|
||||
|
||||
@@ -134,5 +136,23 @@ W605_0.py:45:12: W605 [*] Invalid escape sequence: `\_`
|
||||
44 44 |
|
||||
45 |-regex = '\\\_'
|
||||
45 |+regex = '\\\\_'
|
||||
46 46 |
|
||||
47 47 | #: W605:1:7
|
||||
48 48 | u'foo\ bar'
|
||||
|
||||
W605_0.py:48:6: W605 [*] Invalid escape sequence: `\ `
|
||||
|
|
||||
47 | #: W605:1:7
|
||||
48 | u'foo\ bar'
|
||||
| ^^ W605
|
||||
|
|
||||
= help: Use a raw string literal
|
||||
|
||||
ℹ Safe fix
|
||||
45 45 | regex = '\\\_'
|
||||
46 46 |
|
||||
47 47 | #: W605:1:7
|
||||
48 |-u'foo\ bar'
|
||||
48 |+r'foo\ bar'
|
||||
|
||||
|
||||
|
||||
@@ -1,104 +1,227 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
|
||||
---
|
||||
W605_1.py:2:10: W605 [*] Invalid escape sequence: `\.`
|
||||
W605_1.py:4:11: W605 [*] Invalid escape sequence: `\.`
|
||||
|
|
||||
1 | #: W605:1:10
|
||||
2 | regex = '\.png$'
|
||||
| ^^ W605
|
||||
3 |
|
||||
4 | #: W605:2:1
|
||||
3 | #: W605:1:10
|
||||
4 | regex = f'\.png$'
|
||||
| ^^ W605
|
||||
5 |
|
||||
6 | #: W605:2:1
|
||||
|
|
||||
= help: Use a raw string literal
|
||||
|
||||
ℹ Safe fix
|
||||
1 1 | #: W605:1:10
|
||||
2 |-regex = '\.png$'
|
||||
2 |+regex = r'\.png$'
|
||||
3 3 |
|
||||
4 4 | #: W605:2:1
|
||||
5 5 | regex = '''
|
||||
1 1 | # Same as `W605_0.py` but using f-strings instead.
|
||||
2 2 |
|
||||
3 3 | #: W605:1:10
|
||||
4 |-regex = f'\.png$'
|
||||
4 |+regex = rf'\.png$'
|
||||
5 5 |
|
||||
6 6 | #: W605:2:1
|
||||
7 7 | regex = f'''
|
||||
|
||||
W605_1.py:6:1: W605 [*] Invalid escape sequence: `\.`
|
||||
W605_1.py:8:1: W605 [*] Invalid escape sequence: `\.`
|
||||
|
|
||||
4 | #: W605:2:1
|
||||
5 | regex = '''
|
||||
6 | \.png$
|
||||
6 | #: W605:2:1
|
||||
7 | regex = f'''
|
||||
8 | \.png$
|
||||
| ^^ W605
|
||||
7 | '''
|
||||
9 | '''
|
||||
|
|
||||
= help: Use a raw string literal
|
||||
|
||||
ℹ Safe fix
|
||||
2 2 | regex = '\.png$'
|
||||
3 3 |
|
||||
4 4 | #: W605:2:1
|
||||
5 |-regex = '''
|
||||
5 |+regex = r'''
|
||||
6 6 | \.png$
|
||||
7 7 | '''
|
||||
8 8 |
|
||||
4 4 | regex = f'\.png$'
|
||||
5 5 |
|
||||
6 6 | #: W605:2:1
|
||||
7 |-regex = f'''
|
||||
7 |+regex = rf'''
|
||||
8 8 | \.png$
|
||||
9 9 | '''
|
||||
10 10 |
|
||||
|
||||
W605_1.py:11:6: W605 [*] Invalid escape sequence: `\_`
|
||||
W605_1.py:13:7: W605 [*] Invalid escape sequence: `\_`
|
||||
|
|
||||
9 | #: W605:2:6
|
||||
10 | f(
|
||||
11 | '\_'
|
||||
11 | #: W605:2:6
|
||||
12 | f(
|
||||
13 | f'\_'
|
||||
| ^^ W605
|
||||
14 | )
|
||||
|
|
||||
= help: Use a raw string literal
|
||||
|
||||
ℹ Safe fix
|
||||
10 10 |
|
||||
11 11 | #: W605:2:6
|
||||
12 12 | f(
|
||||
13 |- f'\_'
|
||||
13 |+ rf'\_'
|
||||
14 14 | )
|
||||
15 15 |
|
||||
16 16 | #: W605:4:6
|
||||
|
||||
W605_1.py:20:6: W605 [*] Invalid escape sequence: `\_`
|
||||
|
|
||||
18 | multi-line
|
||||
19 | literal
|
||||
20 | with \_ somewhere
|
||||
| ^^ W605
|
||||
12 | )
|
||||
21 | in the middle
|
||||
22 | """
|
||||
|
|
||||
= help: Use a raw string literal
|
||||
|
||||
ℹ Safe fix
|
||||
8 8 |
|
||||
9 9 | #: W605:2:6
|
||||
10 10 | f(
|
||||
11 |- '\_'
|
||||
11 |+ r'\_'
|
||||
12 12 | )
|
||||
13 13 |
|
||||
14 14 | #: W605:4:6
|
||||
14 14 | )
|
||||
15 15 |
|
||||
16 16 | #: W605:4:6
|
||||
17 |-f"""
|
||||
17 |+rf"""
|
||||
18 18 | multi-line
|
||||
19 19 | literal
|
||||
20 20 | with \_ somewhere
|
||||
|
||||
W605_1.py:18:6: W605 [*] Invalid escape sequence: `\_`
|
||||
W605_1.py:25:40: W605 [*] Invalid escape sequence: `\_`
|
||||
|
|
||||
16 | multi-line
|
||||
17 | literal
|
||||
18 | with \_ somewhere
|
||||
| ^^ W605
|
||||
19 | in the middle
|
||||
20 | """
|
||||
24 | #: W605:1:38
|
||||
25 | value = f'new line\nand invalid escape \_ here'
|
||||
| ^^ W605
|
||||
|
|
||||
= help: Use a raw string literal
|
||||
= help: Add backslash to escape sequence
|
||||
|
||||
ℹ Safe fix
|
||||
12 12 | )
|
||||
13 13 |
|
||||
14 14 | #: W605:4:6
|
||||
15 |-"""
|
||||
15 |+r"""
|
||||
16 16 | multi-line
|
||||
17 17 | literal
|
||||
18 18 | with \_ somewhere
|
||||
|
||||
W605_1.py:25:12: W605 [*] Invalid escape sequence: `\.`
|
||||
|
|
||||
23 | def f():
|
||||
24 | #: W605:1:11
|
||||
25 | return'\.png$'
|
||||
| ^^ W605
|
||||
26 |
|
||||
27 | #: Okay
|
||||
|
|
||||
= help: Use a raw string literal
|
||||
|
||||
ℹ Safe fix
|
||||
22 22 |
|
||||
23 23 | def f():
|
||||
24 24 | #: W605:1:11
|
||||
25 |- return'\.png$'
|
||||
25 |+ return r'\.png$'
|
||||
22 22 | """
|
||||
23 23 |
|
||||
24 24 | #: W605:1:38
|
||||
25 |-value = f'new line\nand invalid escape \_ here'
|
||||
25 |+value = f'new line\nand invalid escape \\_ here'
|
||||
26 26 |
|
||||
27 27 | #: Okay
|
||||
28 28 | regex = r'\.png$'
|
||||
27 27 |
|
||||
28 28 | #: Okay
|
||||
|
||||
W605_1.py:43:13: W605 [*] Invalid escape sequence: `\_`
|
||||
|
|
||||
41 | ''' # noqa
|
||||
42 |
|
||||
43 | regex = f'\\\_'
|
||||
| ^^ W605
|
||||
44 | value = f'\{{1}}'
|
||||
45 | value = f'\{1}'
|
||||
|
|
||||
= help: Add backslash to escape sequence
|
||||
|
||||
ℹ Safe fix
|
||||
40 40 | \w
|
||||
41 41 | ''' # noqa
|
||||
42 42 |
|
||||
43 |-regex = f'\\\_'
|
||||
43 |+regex = f'\\\\_'
|
||||
44 44 | value = f'\{{1}}'
|
||||
45 45 | value = f'\{1}'
|
||||
46 46 | value = f'{1:\}'
|
||||
|
||||
W605_1.py:44:11: W605 [*] Invalid escape sequence: `\{`
|
||||
|
|
||||
43 | regex = f'\\\_'
|
||||
44 | value = f'\{{1}}'
|
||||
| ^^ W605
|
||||
45 | value = f'\{1}'
|
||||
46 | value = f'{1:\}'
|
||||
|
|
||||
= help: Use a raw string literal
|
||||
|
||||
ℹ Safe fix
|
||||
41 41 | ''' # noqa
|
||||
42 42 |
|
||||
43 43 | regex = f'\\\_'
|
||||
44 |-value = f'\{{1}}'
|
||||
44 |+value = rf'\{{1}}'
|
||||
45 45 | value = f'\{1}'
|
||||
46 46 | value = f'{1:\}'
|
||||
47 47 | value = f"{f"\{1}"}"
|
||||
|
||||
W605_1.py:45:11: W605 [*] Invalid escape sequence: `\{`
|
||||
|
|
||||
43 | regex = f'\\\_'
|
||||
44 | value = f'\{{1}}'
|
||||
45 | value = f'\{1}'
|
||||
| ^^ W605
|
||||
46 | value = f'{1:\}'
|
||||
47 | value = f"{f"\{1}"}"
|
||||
|
|
||||
= help: Use a raw string literal
|
||||
|
||||
ℹ Safe fix
|
||||
42 42 |
|
||||
43 43 | regex = f'\\\_'
|
||||
44 44 | value = f'\{{1}}'
|
||||
45 |-value = f'\{1}'
|
||||
45 |+value = rf'\{1}'
|
||||
46 46 | value = f'{1:\}'
|
||||
47 47 | value = f"{f"\{1}"}"
|
||||
48 48 | value = rf"{f"\{1}"}"
|
||||
|
||||
W605_1.py:46:14: W605 [*] Invalid escape sequence: `\}`
|
||||
|
|
||||
44 | value = f'\{{1}}'
|
||||
45 | value = f'\{1}'
|
||||
46 | value = f'{1:\}'
|
||||
| ^^ W605
|
||||
47 | value = f"{f"\{1}"}"
|
||||
48 | value = rf"{f"\{1}"}"
|
||||
|
|
||||
= help: Use a raw string literal
|
||||
|
||||
ℹ Safe fix
|
||||
43 43 | regex = f'\\\_'
|
||||
44 44 | value = f'\{{1}}'
|
||||
45 45 | value = f'\{1}'
|
||||
46 |-value = f'{1:\}'
|
||||
46 |+value = rf'{1:\}'
|
||||
47 47 | value = f"{f"\{1}"}"
|
||||
48 48 | value = rf"{f"\{1}"}"
|
||||
49 49 |
|
||||
|
||||
W605_1.py:47:14: W605 [*] Invalid escape sequence: `\{`
|
||||
|
|
||||
45 | value = f'\{1}'
|
||||
46 | value = f'{1:\}'
|
||||
47 | value = f"{f"\{1}"}"
|
||||
| ^^ W605
|
||||
48 | value = rf"{f"\{1}"}"
|
||||
|
|
||||
= help: Use a raw string literal
|
||||
|
||||
ℹ Safe fix
|
||||
44 44 | value = f'\{{1}}'
|
||||
45 45 | value = f'\{1}'
|
||||
46 46 | value = f'{1:\}'
|
||||
47 |-value = f"{f"\{1}"}"
|
||||
47 |+value = f"{rf"\{1}"}"
|
||||
48 48 | value = rf"{f"\{1}"}"
|
||||
49 49 |
|
||||
50 50 | # Okay
|
||||
|
||||
W605_1.py:48:15: W605 [*] Invalid escape sequence: `\{`
|
||||
|
|
||||
46 | value = f'{1:\}'
|
||||
47 | value = f"{f"\{1}"}"
|
||||
48 | value = rf"{f"\{1}"}"
|
||||
| ^^ W605
|
||||
49 |
|
||||
50 | # Okay
|
||||
|
|
||||
= help: Use a raw string literal
|
||||
|
||||
ℹ Safe fix
|
||||
45 45 | value = f'\{1}'
|
||||
46 46 | value = f'{1:\}'
|
||||
47 47 | value = f"{f"\{1}"}"
|
||||
48 |-value = rf"{f"\{1}"}"
|
||||
48 |+value = rf"{rf"\{1}"}"
|
||||
49 49 |
|
||||
50 50 | # Okay
|
||||
51 51 | value = rf'\{{1}}'
|
||||
|
||||
|
||||
|
||||
@@ -1,227 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
|
||||
---
|
||||
W605_2.py:4:11: W605 [*] Invalid escape sequence: `\.`
|
||||
|
|
||||
3 | #: W605:1:10
|
||||
4 | regex = f'\.png$'
|
||||
| ^^ W605
|
||||
5 |
|
||||
6 | #: W605:2:1
|
||||
|
|
||||
= help: Use a raw string literal
|
||||
|
||||
ℹ Safe fix
|
||||
1 1 | # Same as `W605_0.py` but using f-strings instead.
|
||||
2 2 |
|
||||
3 3 | #: W605:1:10
|
||||
4 |-regex = f'\.png$'
|
||||
4 |+regex = rf'\.png$'
|
||||
5 5 |
|
||||
6 6 | #: W605:2:1
|
||||
7 7 | regex = f'''
|
||||
|
||||
W605_2.py:8:1: W605 [*] Invalid escape sequence: `\.`
|
||||
|
|
||||
6 | #: W605:2:1
|
||||
7 | regex = f'''
|
||||
8 | \.png$
|
||||
| ^^ W605
|
||||
9 | '''
|
||||
|
|
||||
= help: Use a raw string literal
|
||||
|
||||
ℹ Safe fix
|
||||
4 4 | regex = f'\.png$'
|
||||
5 5 |
|
||||
6 6 | #: W605:2:1
|
||||
7 |-regex = f'''
|
||||
7 |+regex = rf'''
|
||||
8 8 | \.png$
|
||||
9 9 | '''
|
||||
10 10 |
|
||||
|
||||
W605_2.py:13:7: W605 [*] Invalid escape sequence: `\_`
|
||||
|
|
||||
11 | #: W605:2:6
|
||||
12 | f(
|
||||
13 | f'\_'
|
||||
| ^^ W605
|
||||
14 | )
|
||||
|
|
||||
= help: Use a raw string literal
|
||||
|
||||
ℹ Safe fix
|
||||
10 10 |
|
||||
11 11 | #: W605:2:6
|
||||
12 12 | f(
|
||||
13 |- f'\_'
|
||||
13 |+ rf'\_'
|
||||
14 14 | )
|
||||
15 15 |
|
||||
16 16 | #: W605:4:6
|
||||
|
||||
W605_2.py:20:6: W605 [*] Invalid escape sequence: `\_`
|
||||
|
|
||||
18 | multi-line
|
||||
19 | literal
|
||||
20 | with \_ somewhere
|
||||
| ^^ W605
|
||||
21 | in the middle
|
||||
22 | """
|
||||
|
|
||||
= help: Use a raw string literal
|
||||
|
||||
ℹ Safe fix
|
||||
14 14 | )
|
||||
15 15 |
|
||||
16 16 | #: W605:4:6
|
||||
17 |-f"""
|
||||
17 |+rf"""
|
||||
18 18 | multi-line
|
||||
19 19 | literal
|
||||
20 20 | with \_ somewhere
|
||||
|
||||
W605_2.py:25:40: W605 [*] Invalid escape sequence: `\_`
|
||||
|
|
||||
24 | #: W605:1:38
|
||||
25 | value = f'new line\nand invalid escape \_ here'
|
||||
| ^^ W605
|
||||
|
|
||||
= help: Add backslash to escape sequence
|
||||
|
||||
ℹ Safe fix
|
||||
22 22 | """
|
||||
23 23 |
|
||||
24 24 | #: W605:1:38
|
||||
25 |-value = f'new line\nand invalid escape \_ here'
|
||||
25 |+value = f'new line\nand invalid escape \\_ here'
|
||||
26 26 |
|
||||
27 27 |
|
||||
28 28 | #: Okay
|
||||
|
||||
W605_2.py:43:13: W605 [*] Invalid escape sequence: `\_`
|
||||
|
|
||||
41 | ''' # noqa
|
||||
42 |
|
||||
43 | regex = f'\\\_'
|
||||
| ^^ W605
|
||||
44 | value = f'\{{1}}'
|
||||
45 | value = f'\{1}'
|
||||
|
|
||||
= help: Add backslash to escape sequence
|
||||
|
||||
ℹ Safe fix
|
||||
40 40 | \w
|
||||
41 41 | ''' # noqa
|
||||
42 42 |
|
||||
43 |-regex = f'\\\_'
|
||||
43 |+regex = f'\\\\_'
|
||||
44 44 | value = f'\{{1}}'
|
||||
45 45 | value = f'\{1}'
|
||||
46 46 | value = f'{1:\}'
|
||||
|
||||
W605_2.py:44:11: W605 [*] Invalid escape sequence: `\{`
|
||||
|
|
||||
43 | regex = f'\\\_'
|
||||
44 | value = f'\{{1}}'
|
||||
| ^^ W605
|
||||
45 | value = f'\{1}'
|
||||
46 | value = f'{1:\}'
|
||||
|
|
||||
= help: Use a raw string literal
|
||||
|
||||
ℹ Safe fix
|
||||
41 41 | ''' # noqa
|
||||
42 42 |
|
||||
43 43 | regex = f'\\\_'
|
||||
44 |-value = f'\{{1}}'
|
||||
44 |+value = rf'\{{1}}'
|
||||
45 45 | value = f'\{1}'
|
||||
46 46 | value = f'{1:\}'
|
||||
47 47 | value = f"{f"\{1}"}"
|
||||
|
||||
W605_2.py:45:11: W605 [*] Invalid escape sequence: `\{`
|
||||
|
|
||||
43 | regex = f'\\\_'
|
||||
44 | value = f'\{{1}}'
|
||||
45 | value = f'\{1}'
|
||||
| ^^ W605
|
||||
46 | value = f'{1:\}'
|
||||
47 | value = f"{f"\{1}"}"
|
||||
|
|
||||
= help: Use a raw string literal
|
||||
|
||||
ℹ Safe fix
|
||||
42 42 |
|
||||
43 43 | regex = f'\\\_'
|
||||
44 44 | value = f'\{{1}}'
|
||||
45 |-value = f'\{1}'
|
||||
45 |+value = rf'\{1}'
|
||||
46 46 | value = f'{1:\}'
|
||||
47 47 | value = f"{f"\{1}"}"
|
||||
48 48 | value = rf"{f"\{1}"}"
|
||||
|
||||
W605_2.py:46:14: W605 [*] Invalid escape sequence: `\}`
|
||||
|
|
||||
44 | value = f'\{{1}}'
|
||||
45 | value = f'\{1}'
|
||||
46 | value = f'{1:\}'
|
||||
| ^^ W605
|
||||
47 | value = f"{f"\{1}"}"
|
||||
48 | value = rf"{f"\{1}"}"
|
||||
|
|
||||
= help: Use a raw string literal
|
||||
|
||||
ℹ Safe fix
|
||||
43 43 | regex = f'\\\_'
|
||||
44 44 | value = f'\{{1}}'
|
||||
45 45 | value = f'\{1}'
|
||||
46 |-value = f'{1:\}'
|
||||
46 |+value = rf'{1:\}'
|
||||
47 47 | value = f"{f"\{1}"}"
|
||||
48 48 | value = rf"{f"\{1}"}"
|
||||
49 49 |
|
||||
|
||||
W605_2.py:47:14: W605 [*] Invalid escape sequence: `\{`
|
||||
|
|
||||
45 | value = f'\{1}'
|
||||
46 | value = f'{1:\}'
|
||||
47 | value = f"{f"\{1}"}"
|
||||
| ^^ W605
|
||||
48 | value = rf"{f"\{1}"}"
|
||||
|
|
||||
= help: Use a raw string literal
|
||||
|
||||
ℹ Safe fix
|
||||
44 44 | value = f'\{{1}}'
|
||||
45 45 | value = f'\{1}'
|
||||
46 46 | value = f'{1:\}'
|
||||
47 |-value = f"{f"\{1}"}"
|
||||
47 |+value = f"{rf"\{1}"}"
|
||||
48 48 | value = rf"{f"\{1}"}"
|
||||
49 49 |
|
||||
50 50 | # Okay
|
||||
|
||||
W605_2.py:48:15: W605 [*] Invalid escape sequence: `\{`
|
||||
|
|
||||
46 | value = f'{1:\}'
|
||||
47 | value = f"{f"\{1}"}"
|
||||
48 | value = rf"{f"\{1}"}"
|
||||
| ^^ W605
|
||||
49 |
|
||||
50 | # Okay
|
||||
|
|
||||
= help: Use a raw string literal
|
||||
|
||||
ℹ Safe fix
|
||||
45 45 | value = f'\{1}'
|
||||
46 46 | value = f'{1:\}'
|
||||
47 47 | value = f"{f"\{1}"}"
|
||||
48 |-value = rf"{f"\{1}"}"
|
||||
48 |+value = rf"{rf"\{1}"}"
|
||||
49 49 |
|
||||
50 50 | # Okay
|
||||
51 51 | value = rf'\{{1}}'
|
||||
|
||||
|
||||
@@ -172,8 +172,9 @@ pub(crate) fn indent(checker: &mut Checker, docstring: &Docstring) {
|
||||
let mut has_seen_tab = docstring.indentation.contains('\t');
|
||||
let mut is_over_indented = true;
|
||||
let mut over_indented_lines = vec![];
|
||||
let mut over_indented_offset = usize::MAX;
|
||||
let mut over_indented_size = usize::MAX;
|
||||
|
||||
let docstring_indent_size = docstring.indentation.chars().count();
|
||||
for i in 0..lines.len() {
|
||||
// First lines and continuations doesn't need any indentation.
|
||||
if i == 0 || lines[i - 1].ends_with('\\') {
|
||||
@@ -189,6 +190,7 @@ pub(crate) fn indent(checker: &mut Checker, docstring: &Docstring) {
|
||||
}
|
||||
|
||||
let line_indent = leading_space(line);
|
||||
let line_indent_size = line_indent.chars().count();
|
||||
|
||||
// We only report tab indentation once, so only check if we haven't seen a tab
|
||||
// yet.
|
||||
@@ -197,9 +199,7 @@ pub(crate) fn indent(checker: &mut Checker, docstring: &Docstring) {
|
||||
if checker.enabled(Rule::UnderIndentation) {
|
||||
// We report under-indentation on every line. This isn't great, but enables
|
||||
// fix.
|
||||
if (i == lines.len() - 1 || !is_blank)
|
||||
&& line_indent.len() < docstring.indentation.len()
|
||||
{
|
||||
if (i == lines.len() - 1 || !is_blank) && line_indent_size < docstring_indent_size {
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(UnderIndentation, TextRange::empty(line.start()));
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
@@ -217,14 +217,12 @@ pub(crate) fn indent(checker: &mut Checker, docstring: &Docstring) {
|
||||
// until we've viewed all the lines, so for now, just track
|
||||
// the over-indentation status of every line.
|
||||
if i < lines.len() - 1 {
|
||||
if line_indent.len() > docstring.indentation.len() {
|
||||
if line_indent_size > docstring_indent_size {
|
||||
over_indented_lines.push(line);
|
||||
|
||||
// Track the _smallest_ offset we see, in terms of characters.
|
||||
over_indented_offset = std::cmp::min(
|
||||
line_indent.chars().count() - docstring.indentation.chars().count(),
|
||||
over_indented_offset,
|
||||
);
|
||||
over_indented_size =
|
||||
std::cmp::min(line_indent_size - docstring_indent_size, over_indented_size);
|
||||
} else {
|
||||
is_over_indented = false;
|
||||
}
|
||||
@@ -250,21 +248,21 @@ pub(crate) fn indent(checker: &mut Checker, docstring: &Docstring) {
|
||||
// enables the fix capability.
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(OverIndentation, TextRange::empty(line.start()));
|
||||
|
||||
let edit = if indent.is_empty() {
|
||||
Edit::deletion(line.start(), line_indent.text_len())
|
||||
// Delete the entire indent.
|
||||
Edit::range_deletion(TextRange::at(line.start(), line_indent.text_len()))
|
||||
} else {
|
||||
// Convert the character count to an offset within the source.
|
||||
let offset = checker
|
||||
.locator()
|
||||
.after(line.start() + indent.text_len())
|
||||
.chars()
|
||||
.take(over_indented_offset)
|
||||
.take(over_indented_size)
|
||||
.map(TextLen::text_len)
|
||||
.sum::<TextSize>();
|
||||
Edit::range_replacement(
|
||||
indent.clone(),
|
||||
TextRange::at(line.start(), indent.text_len() + offset),
|
||||
)
|
||||
let range = TextRange::at(line.start(), indent.text_len() + offset);
|
||||
Edit::range_replacement(indent, range)
|
||||
};
|
||||
diagnostic.set_fix(Fix::safe_edit(edit));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
@@ -274,7 +272,8 @@ pub(crate) fn indent(checker: &mut Checker, docstring: &Docstring) {
|
||||
// If the last line is over-indented...
|
||||
if let Some(last) = lines.last() {
|
||||
let line_indent = leading_space(last);
|
||||
if line_indent.len() > docstring.indentation.len() {
|
||||
let line_indent_size = line_indent.chars().count();
|
||||
if line_indent_size > docstring_indent_size {
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(OverIndentation, TextRange::empty(last.start()));
|
||||
let indent = clean_space(docstring.indentation);
|
||||
|
||||
@@ -1,57 +1,81 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pydocstyle/mod.rs
|
||||
---
|
||||
D208.py:3:1: D208 [*] Docstring is over-indented
|
||||
D208.py:2:1: D208 [*] Docstring is over-indented
|
||||
|
|
||||
1 | class Platform:
|
||||
2 | """ Remove sampler
|
||||
3 | Args:
|
||||
1 | """
|
||||
2 | Author
|
||||
| D208
|
||||
4 | Returns:
|
||||
5 | """
|
||||
3 | """
|
||||
|
|
||||
= help: Remove over-indentation
|
||||
|
||||
ℹ Safe fix
|
||||
1 1 | class Platform:
|
||||
2 2 | """ Remove sampler
|
||||
3 |- Args:
|
||||
3 |+ Args:
|
||||
4 4 | Returns:
|
||||
5 5 | """
|
||||
1 1 | """
|
||||
2 |- Author
|
||||
2 |+Author
|
||||
3 3 | """
|
||||
4 4 |
|
||||
5 5 |
|
||||
|
||||
D208.py:4:1: D208 [*] Docstring is over-indented
|
||||
|
|
||||
2 | """ Remove sampler
|
||||
3 | Args:
|
||||
4 | Returns:
|
||||
| D208
|
||||
5 | """
|
||||
|
|
||||
= help: Remove over-indentation
|
||||
D208.py:8:1: D208 [*] Docstring is over-indented
|
||||
|
|
||||
6 | class Platform:
|
||||
7 | """ Remove sampler
|
||||
8 | Args:
|
||||
| D208
|
||||
9 | Returns:
|
||||
10 | """
|
||||
|
|
||||
= help: Remove over-indentation
|
||||
|
||||
ℹ Safe fix
|
||||
1 1 | class Platform:
|
||||
2 2 | """ Remove sampler
|
||||
3 3 | Args:
|
||||
4 |- Returns:
|
||||
4 |+ Returns:
|
||||
5 5 | """
|
||||
5 5 |
|
||||
6 6 | class Platform:
|
||||
7 7 | """ Remove sampler
|
||||
8 |- Args:
|
||||
8 |+ Args:
|
||||
9 9 | Returns:
|
||||
10 10 | """
|
||||
11 11 |
|
||||
|
||||
D208.py:5:1: D208 [*] Docstring is over-indented
|
||||
|
|
||||
3 | Args:
|
||||
4 | Returns:
|
||||
5 | """
|
||||
| D208
|
||||
|
|
||||
= help: Remove over-indentation
|
||||
D208.py:9:1: D208 [*] Docstring is over-indented
|
||||
|
|
||||
7 | """ Remove sampler
|
||||
8 | Args:
|
||||
9 | Returns:
|
||||
| D208
|
||||
10 | """
|
||||
|
|
||||
= help: Remove over-indentation
|
||||
|
||||
ℹ Safe fix
|
||||
2 2 | """ Remove sampler
|
||||
3 3 | Args:
|
||||
4 4 | Returns:
|
||||
5 |- """
|
||||
5 |+ """
|
||||
6 6 | class Platform:
|
||||
7 7 | """ Remove sampler
|
||||
8 8 | Args:
|
||||
9 |- Returns:
|
||||
9 |+ Returns:
|
||||
10 10 | """
|
||||
11 11 |
|
||||
12 12 |
|
||||
|
||||
D208.py:10:1: D208 [*] Docstring is over-indented
|
||||
|
|
||||
8 | Args:
|
||||
9 | Returns:
|
||||
10 | """
|
||||
| D208
|
||||
|
|
||||
= help: Remove over-indentation
|
||||
|
||||
ℹ Safe fix
|
||||
7 7 | """ Remove sampler
|
||||
8 8 | Args:
|
||||
9 9 | Returns:
|
||||
10 |- """
|
||||
10 |+ """
|
||||
11 11 |
|
||||
12 12 |
|
||||
13 13 | def memory_test():
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
use std::fmt;
|
||||
|
||||
use ruff_python_ast as ast;
|
||||
use ruff_python_ast::{Arguments, CmpOp, Expr};
|
||||
use ruff_python_ast::visitor::Visitor;
|
||||
use ruff_python_ast::{visitor, Arguments, CmpOp, Expr, Stmt};
|
||||
use ruff_python_semantic::analyze::function_type;
|
||||
use ruff_python_semantic::{ScopeKind, SemanticModel};
|
||||
use ruff_text_size::TextRange;
|
||||
|
||||
use crate::settings::LinterSettings;
|
||||
|
||||
@@ -82,3 +84,116 @@ impl fmt::Display for CmpOpExt {
|
||||
write!(f, "{representation}")
|
||||
}
|
||||
}
|
||||
|
||||
/// Visitor to track reads from an iterable in a loop.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct SequenceIndexVisitor<'a> {
|
||||
/// `letters`, given `for index, letter in enumerate(letters)`.
|
||||
sequence_name: &'a str,
|
||||
/// `index`, given `for index, letter in enumerate(letters)`.
|
||||
index_name: &'a str,
|
||||
/// `letter`, given `for index, letter in enumerate(letters)`.
|
||||
value_name: &'a str,
|
||||
/// The ranges of any `letters[index]` accesses.
|
||||
accesses: Vec<TextRange>,
|
||||
/// Whether any of the variables have been modified.
|
||||
modified: bool,
|
||||
}
|
||||
|
||||
impl<'a> SequenceIndexVisitor<'a> {
|
||||
pub(crate) fn new(sequence_name: &'a str, index_name: &'a str, value_name: &'a str) -> Self {
|
||||
Self {
|
||||
sequence_name,
|
||||
index_name,
|
||||
value_name,
|
||||
accesses: Vec::new(),
|
||||
modified: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn into_accesses(self) -> Vec<TextRange> {
|
||||
self.accesses
|
||||
}
|
||||
}
|
||||
|
||||
impl SequenceIndexVisitor<'_> {
|
||||
fn is_assignment(&self, expr: &Expr) -> bool {
|
||||
// If we see the sequence, a subscript, or the index being modified, we'll stop emitting
|
||||
// diagnostics.
|
||||
match expr {
|
||||
Expr::Name(ast::ExprName { id, .. }) => {
|
||||
id == self.sequence_name || id == self.index_name || id == self.value_name
|
||||
}
|
||||
Expr::Subscript(ast::ExprSubscript { value, slice, .. }) => {
|
||||
let Expr::Name(ast::ExprName { id, .. }) = value.as_ref() else {
|
||||
return false;
|
||||
};
|
||||
if id == self.sequence_name {
|
||||
let Expr::Name(ast::ExprName { id, .. }) = slice.as_ref() else {
|
||||
return false;
|
||||
};
|
||||
if id == self.index_name {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Visitor<'_> for SequenceIndexVisitor<'a> {
|
||||
fn visit_stmt(&mut self, stmt: &Stmt) {
|
||||
if self.modified {
|
||||
return;
|
||||
}
|
||||
match stmt {
|
||||
Stmt::Assign(ast::StmtAssign { targets, value, .. }) => {
|
||||
self.modified = targets.iter().any(|target| self.is_assignment(target));
|
||||
self.visit_expr(value);
|
||||
}
|
||||
Stmt::AnnAssign(ast::StmtAnnAssign { target, value, .. }) => {
|
||||
if let Some(value) = value {
|
||||
self.modified = self.is_assignment(target);
|
||||
self.visit_expr(value);
|
||||
}
|
||||
}
|
||||
Stmt::AugAssign(ast::StmtAugAssign { target, value, .. }) => {
|
||||
self.modified = self.is_assignment(target);
|
||||
self.visit_expr(value);
|
||||
}
|
||||
Stmt::Delete(ast::StmtDelete { targets, .. }) => {
|
||||
self.modified = targets.iter().any(|target| self.is_assignment(target));
|
||||
}
|
||||
_ => visitor::walk_stmt(self, stmt),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, expr: &Expr) {
|
||||
if self.modified {
|
||||
return;
|
||||
}
|
||||
match expr {
|
||||
Expr::Subscript(ast::ExprSubscript {
|
||||
value,
|
||||
slice,
|
||||
range,
|
||||
..
|
||||
}) => {
|
||||
let Expr::Name(ast::ExprName { id, .. }) = value.as_ref() else {
|
||||
return;
|
||||
};
|
||||
if id == self.sequence_name {
|
||||
let Expr::Name(ast::ExprName { id, .. }) = slice.as_ref() else {
|
||||
return;
|
||||
};
|
||||
if id == self.index_name {
|
||||
self.accesses.push(*range);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => visitor::walk_expr(self, expr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,7 @@ mod tests {
|
||||
#[test_case(Rule::RedefinedLoopName, Path::new("redefined_loop_name.py"))]
|
||||
#[test_case(Rule::ReturnInInit, Path::new("return_in_init.py"))]
|
||||
#[test_case(Rule::TooManyArguments, Path::new("too_many_arguments.py"))]
|
||||
#[test_case(Rule::TooManyPositional, Path::new("too_many_positional.py"))]
|
||||
#[test_case(Rule::TooManyBranches, Path::new("too_many_branches.py"))]
|
||||
#[test_case(
|
||||
Rule::TooManyReturnStatements,
|
||||
@@ -160,6 +161,10 @@ mod tests {
|
||||
)]
|
||||
#[test_case(Rule::NoClassmethodDecorator, Path::new("no_method_decorator.py"))]
|
||||
#[test_case(Rule::NoStaticmethodDecorator, Path::new("no_method_decorator.py"))]
|
||||
#[test_case(
|
||||
Rule::UnnecessaryDictIndexLookup,
|
||||
Path::new("unnecessary_dict_index_lookup.py")
|
||||
)]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
@@ -245,6 +250,22 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn max_positional_args() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
Path::new("pylint/too_many_positional_params.py"),
|
||||
&LinterSettings {
|
||||
pylint: pylint::settings::Settings {
|
||||
max_positional_args: 4,
|
||||
..pylint::settings::Settings::default()
|
||||
},
|
||||
..LinterSettings::for_rule(Rule::TooManyPositional)
|
||||
},
|
||||
)?;
|
||||
assert_messages!(diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn max_branches() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
@@ -324,4 +345,15 @@ mod tests {
|
||||
assert_messages!(diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unspecified_encoding_python39_or_lower() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
Path::new("pylint/unspecified_encoding.py"),
|
||||
&LinterSettings::for_rule(Rule::UnspecifiedEncoding)
|
||||
.with_target_version(PythonVersion::Py39),
|
||||
)?;
|
||||
assert_messages!(diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,14 @@ use crate::rules::pylint::helpers::CmpOpExt;
|
||||
/// foo == foo
|
||||
/// ```
|
||||
///
|
||||
/// In some cases, self-comparisons are used to determine whether a float is
|
||||
/// NaN. Instead, prefer `math.isnan`:
|
||||
/// ```python
|
||||
/// import math
|
||||
///
|
||||
/// math.isnan(foo)
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: Comparisons](https://docs.python.org/3/reference/expressions.html#comparisons)
|
||||
#[violation]
|
||||
|
||||
@@ -55,6 +55,7 @@ pub(crate) use sys_exit_alias::*;
|
||||
pub(crate) use too_many_arguments::*;
|
||||
pub(crate) use too_many_boolean_expressions::*;
|
||||
pub(crate) use too_many_branches::*;
|
||||
pub(crate) use too_many_positional::*;
|
||||
pub(crate) use too_many_public_methods::*;
|
||||
pub(crate) use too_many_return_statements::*;
|
||||
pub(crate) use too_many_statements::*;
|
||||
@@ -62,6 +63,7 @@ pub(crate) use type_bivariance::*;
|
||||
pub(crate) use type_name_incorrect_variance::*;
|
||||
pub(crate) use type_param_name_mismatch::*;
|
||||
pub(crate) use unexpected_special_method_signature::*;
|
||||
pub(crate) use unnecessary_dict_index_lookup::*;
|
||||
pub(crate) use unnecessary_direct_lambda_call::*;
|
||||
pub(crate) use unnecessary_lambda::*;
|
||||
pub(crate) use unnecessary_list_index_lookup::*;
|
||||
@@ -130,6 +132,7 @@ mod sys_exit_alias;
|
||||
mod too_many_arguments;
|
||||
mod too_many_boolean_expressions;
|
||||
mod too_many_branches;
|
||||
mod too_many_positional;
|
||||
mod too_many_public_methods;
|
||||
mod too_many_return_statements;
|
||||
mod too_many_statements;
|
||||
@@ -137,6 +140,7 @@ mod type_bivariance;
|
||||
mod type_name_incorrect_variance;
|
||||
mod type_param_name_mismatch;
|
||||
mod unexpected_special_method_signature;
|
||||
mod unnecessary_dict_index_lookup;
|
||||
mod unnecessary_direct_lambda_call;
|
||||
mod unnecessary_lambda;
|
||||
mod unnecessary_list_index_lookup;
|
||||
|
||||
@@ -53,7 +53,7 @@ impl AlwaysFixableViolation for NoClassmethodDecorator {
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// class Foo:
|
||||
/// def bar(cls):
|
||||
/// def bar(arg1, arg2):
|
||||
/// ...
|
||||
///
|
||||
/// bar = staticmethod(bar)
|
||||
@@ -63,7 +63,7 @@ impl AlwaysFixableViolation for NoClassmethodDecorator {
|
||||
/// ```python
|
||||
/// class Foo:
|
||||
/// @staticmethod
|
||||
/// def bar(cls):
|
||||
/// def bar(arg1, arg2):
|
||||
/// ...
|
||||
/// ```
|
||||
#[violation]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use ruff_python_ast::{Parameters, Stmt};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast as ast;
|
||||
use ruff_python_ast::identifier::Identifier;
|
||||
use ruff_python_semantic::analyze::visibility;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
@@ -58,12 +58,13 @@ impl Violation for TooManyArguments {
|
||||
}
|
||||
|
||||
/// PLR0913
|
||||
pub(crate) fn too_many_arguments(checker: &mut Checker, parameters: &Parameters, stmt: &Stmt) {
|
||||
let num_arguments = parameters
|
||||
pub(crate) fn too_many_arguments(checker: &mut Checker, function_def: &ast::StmtFunctionDef) {
|
||||
let num_arguments = function_def
|
||||
.parameters
|
||||
.args
|
||||
.iter()
|
||||
.chain(¶meters.kwonlyargs)
|
||||
.chain(¶meters.posonlyargs)
|
||||
.chain(&function_def.parameters.kwonlyargs)
|
||||
.chain(&function_def.parameters.posonlyargs)
|
||||
.filter(|arg| {
|
||||
!checker
|
||||
.settings
|
||||
@@ -71,13 +72,22 @@ pub(crate) fn too_many_arguments(checker: &mut Checker, parameters: &Parameters,
|
||||
.is_match(&arg.parameter.name)
|
||||
})
|
||||
.count();
|
||||
|
||||
if num_arguments > checker.settings.pylint.max_args {
|
||||
// Allow excessive arguments in `@override` or `@overload` methods, since they're required
|
||||
// to adhere to the parent signature.
|
||||
if visibility::is_override(&function_def.decorator_list, checker.semantic())
|
||||
|| visibility::is_overload(&function_def.decorator_list, checker.semantic())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
TooManyArguments {
|
||||
c_args: num_arguments,
|
||||
max_args: checker.settings.pylint.max_args,
|
||||
},
|
||||
stmt.identifier(),
|
||||
function_def.identifier(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::{self as ast, identifier::Identifier};
|
||||
use ruff_python_semantic::analyze::visibility;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for function definitions that include too many positional arguments.
|
||||
///
|
||||
/// By default, this rule allows up to five arguments, as configured by the
|
||||
/// [`pylint.max-positional-args`] option.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Functions with many arguments are harder to understand, maintain, and call.
|
||||
/// This is especially true for functions with many positional arguments, as
|
||||
/// providing arguments positionally is more error-prone and less clear to
|
||||
/// readers than providing arguments by name.
|
||||
///
|
||||
/// Consider refactoring functions with many arguments into smaller functions
|
||||
/// with fewer arguments, using objects to group related arguments, or
|
||||
/// migrating to keyword-only arguments.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// def plot(x, y, z, color, mark, add_trendline):
|
||||
/// ...
|
||||
///
|
||||
///
|
||||
/// plot(1, 2, 3, "r", "*", True)
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// def plot(x, y, z, *, color, mark, add_trendline):
|
||||
/// ...
|
||||
///
|
||||
///
|
||||
/// plot(1, 2, 3, color="r", mark="*", add_trendline=True)
|
||||
/// ```
|
||||
///
|
||||
/// ## Options
|
||||
/// - `pylint.max-positional-args`
|
||||
#[violation]
|
||||
pub struct TooManyPositional {
|
||||
c_pos: usize,
|
||||
max_pos: usize,
|
||||
}
|
||||
|
||||
impl Violation for TooManyPositional {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let TooManyPositional { c_pos, max_pos } = self;
|
||||
format!("Too many positional arguments: ({c_pos}/{max_pos})")
|
||||
}
|
||||
}
|
||||
|
||||
/// PLR0917
|
||||
pub(crate) fn too_many_positional(checker: &mut Checker, function_def: &ast::StmtFunctionDef) {
|
||||
let num_positional_args = function_def
|
||||
.parameters
|
||||
.args
|
||||
.iter()
|
||||
.chain(&function_def.parameters.posonlyargs)
|
||||
.filter(|arg| {
|
||||
!checker
|
||||
.settings
|
||||
.dummy_variable_rgx
|
||||
.is_match(&arg.parameter.name)
|
||||
})
|
||||
.count();
|
||||
|
||||
if num_positional_args > checker.settings.pylint.max_positional_args {
|
||||
// Allow excessive arguments in `@override` or `@overload` methods, since they're required
|
||||
// to adhere to the parent signature.
|
||||
if visibility::is_override(&function_def.decorator_list, checker.semantic())
|
||||
|| visibility::is_overload(&function_def.decorator_list, checker.semantic())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
TooManyPositional {
|
||||
c_pos: num_positional_args,
|
||||
max_pos: checker.settings.pylint.max_positional_args,
|
||||
},
|
||||
function_def.identifier(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::visitor::Visitor;
|
||||
use ruff_python_ast::{self as ast, Expr, StmtFor};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::rules::pylint::helpers::SequenceIndexVisitor;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for key-based dict accesses during `.items()` iterations.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// When iterating over a dict via `.items()`, the current value is already
|
||||
/// available alongside its key. Using the key to look up the value is
|
||||
/// unnecessary.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// FRUITS = {"apple": 1, "orange": 10, "berry": 22}
|
||||
///
|
||||
/// for fruit_name, fruit_count in FRUITS.items():
|
||||
/// print(FRUITS[fruit_name])
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// FRUITS = {"apple": 1, "orange": 10, "berry": 22}
|
||||
///
|
||||
/// for fruit_name, fruit_count in FRUITS.items():
|
||||
/// print(fruit_count)
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct UnnecessaryDictIndexLookup;
|
||||
|
||||
impl AlwaysFixableViolation for UnnecessaryDictIndexLookup {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Unnecessary lookup of dictionary value by key")
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> String {
|
||||
format!("Use existing variable")
|
||||
}
|
||||
}
|
||||
|
||||
/// PLR1733
|
||||
pub(crate) fn unnecessary_dict_index_lookup(checker: &mut Checker, stmt_for: &StmtFor) {
|
||||
let Some((dict_name, index_name, value_name)) = dict_items(&stmt_for.iter, &stmt_for.target)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let ranges = {
|
||||
let mut visitor = SequenceIndexVisitor::new(&dict_name.id, &index_name.id, &value_name.id);
|
||||
visitor.visit_body(&stmt_for.body);
|
||||
visitor.visit_body(&stmt_for.orelse);
|
||||
visitor.into_accesses()
|
||||
};
|
||||
|
||||
for range in ranges {
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryDictIndexLookup, range);
|
||||
diagnostic.set_fix(Fix::safe_edits(
|
||||
Edit::range_replacement(value_name.id.to_string(), range),
|
||||
[noop(index_name), noop(value_name)],
|
||||
));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
/// PLR1733
|
||||
pub(crate) fn unnecessary_dict_index_lookup_comprehension(checker: &mut Checker, expr: &Expr) {
|
||||
let (Expr::GeneratorExp(ast::ExprGeneratorExp {
|
||||
elt, generators, ..
|
||||
})
|
||||
| Expr::DictComp(ast::ExprDictComp {
|
||||
value: elt,
|
||||
generators,
|
||||
..
|
||||
})
|
||||
| Expr::SetComp(ast::ExprSetComp {
|
||||
elt, generators, ..
|
||||
})
|
||||
| Expr::ListComp(ast::ExprListComp {
|
||||
elt, generators, ..
|
||||
})) = expr
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
for comp in generators {
|
||||
let Some((dict_name, index_name, value_name)) = dict_items(&comp.iter, &comp.target) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let ranges = {
|
||||
let mut visitor =
|
||||
SequenceIndexVisitor::new(&dict_name.id, &index_name.id, &value_name.id);
|
||||
visitor.visit_expr(elt.as_ref());
|
||||
for expr in &comp.ifs {
|
||||
visitor.visit_expr(expr);
|
||||
}
|
||||
visitor.into_accesses()
|
||||
};
|
||||
|
||||
for range in ranges {
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryDictIndexLookup, range);
|
||||
diagnostic.set_fix(Fix::safe_edits(
|
||||
Edit::range_replacement(value_name.id.to_string(), range),
|
||||
[noop(index_name), noop(value_name)],
|
||||
));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn dict_items<'a>(
|
||||
call_expr: &'a Expr,
|
||||
tuple_expr: &'a Expr,
|
||||
) -> Option<(&'a ast::ExprName, &'a ast::ExprName, &'a ast::ExprName)> {
|
||||
let ast::ExprCall {
|
||||
func, arguments, ..
|
||||
} = call_expr.as_call_expr()?;
|
||||
|
||||
if !arguments.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let Expr::Attribute(ast::ExprAttribute { value, attr, .. }) = func.as_ref() else {
|
||||
return None;
|
||||
};
|
||||
if attr != "items" {
|
||||
return None;
|
||||
}
|
||||
|
||||
let Expr::Name(dict_name) = value.as_ref() else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let Expr::Tuple(ast::ExprTuple { elts, .. }) = tuple_expr else {
|
||||
return None;
|
||||
};
|
||||
let [index, value] = elts.as_slice() else {
|
||||
return None;
|
||||
};
|
||||
|
||||
// Grab the variable names.
|
||||
let Expr::Name(index_name) = index else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let Expr::Name(value_name) = value else {
|
||||
return None;
|
||||
};
|
||||
|
||||
// If either of the variable names are intentionally ignored by naming them `_`, then don't
|
||||
// emit.
|
||||
if index_name.id == "_" || value_name.id == "_" {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some((dict_name, index_name, value_name))
|
||||
}
|
||||
|
||||
/// Return a no-op edit for the given name.
|
||||
fn noop(name: &ast::ExprName) -> Edit {
|
||||
Edit::range_replacement(name.id.to_string(), name.range())
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
use ruff_python_ast::{self as ast, Expr, Stmt, StmtFor};
|
||||
|
||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::visitor;
|
||||
use ruff_python_ast::visitor::Visitor;
|
||||
use ruff_python_ast::{self as ast, Expr, StmtFor};
|
||||
use ruff_python_semantic::SemanticModel;
|
||||
use ruff_text_size::TextRange;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::rules::pylint::helpers::SequenceIndexVisitor;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for index-based list accesses during `enumerate` iterations.
|
||||
@@ -55,18 +54,18 @@ pub(crate) fn unnecessary_list_index_lookup(checker: &mut Checker, stmt_for: &St
|
||||
};
|
||||
|
||||
let ranges = {
|
||||
let mut visitor = SubscriptVisitor::new(sequence, index_name);
|
||||
let mut visitor = SequenceIndexVisitor::new(&sequence.id, &index_name.id, &value_name.id);
|
||||
visitor.visit_body(&stmt_for.body);
|
||||
visitor.visit_body(&stmt_for.orelse);
|
||||
visitor.diagnostic_ranges
|
||||
visitor.into_accesses()
|
||||
};
|
||||
|
||||
for range in ranges {
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryListIndexLookup, range);
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
value_name.to_string(),
|
||||
range,
|
||||
)));
|
||||
diagnostic.set_fix(Fix::safe_edits(
|
||||
Edit::range_replacement(value_name.id.to_string(), range),
|
||||
[noop(index_name), noop(value_name)],
|
||||
));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
@@ -99,17 +98,18 @@ pub(crate) fn unnecessary_list_index_lookup_comprehension(checker: &mut Checker,
|
||||
};
|
||||
|
||||
let ranges = {
|
||||
let mut visitor = SubscriptVisitor::new(sequence, index_name);
|
||||
let mut visitor =
|
||||
SequenceIndexVisitor::new(&sequence.id, &index_name.id, &value_name.id);
|
||||
visitor.visit_expr(elt.as_ref());
|
||||
visitor.diagnostic_ranges
|
||||
visitor.into_accesses()
|
||||
};
|
||||
|
||||
for range in ranges {
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryListIndexLookup, range);
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
value_name.to_string(),
|
||||
range,
|
||||
)));
|
||||
diagnostic.set_fix(Fix::safe_edits(
|
||||
Edit::range_replacement(value_name.id.to_string(), range),
|
||||
[noop(index_name), noop(value_name)],
|
||||
));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
@@ -119,7 +119,7 @@ fn enumerate_items<'a>(
|
||||
call_expr: &'a Expr,
|
||||
tuple_expr: &'a Expr,
|
||||
semantic: &SemanticModel,
|
||||
) -> Option<(&'a str, &'a str, &'a str)> {
|
||||
) -> Option<(&'a ast::ExprName, &'a ast::ExprName, &'a ast::ExprName)> {
|
||||
let ast::ExprCall {
|
||||
func, arguments, ..
|
||||
} = call_expr.as_call_expr()?;
|
||||
@@ -140,125 +140,29 @@ fn enumerate_items<'a>(
|
||||
};
|
||||
|
||||
// Grab the variable names.
|
||||
let Expr::Name(ast::ExprName { id: index_name, .. }) = index else {
|
||||
let Expr::Name(index_name) = index else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let Expr::Name(ast::ExprName { id: value_name, .. }) = value else {
|
||||
let Expr::Name(value_name) = value else {
|
||||
return None;
|
||||
};
|
||||
|
||||
// If either of the variable names are intentionally ignored by naming them `_`, then don't
|
||||
// emit.
|
||||
if index_name == "_" || value_name == "_" {
|
||||
if index_name.id == "_" || value_name.id == "_" {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Get the first argument of the enumerate call.
|
||||
let Some(Expr::Name(ast::ExprName { id: sequence, .. })) = arguments.args.first() else {
|
||||
let Some(Expr::Name(sequence)) = arguments.args.first() else {
|
||||
return None;
|
||||
};
|
||||
|
||||
Some((sequence, index_name, value_name))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SubscriptVisitor<'a> {
|
||||
sequence_name: &'a str,
|
||||
index_name: &'a str,
|
||||
diagnostic_ranges: Vec<TextRange>,
|
||||
modified: bool,
|
||||
}
|
||||
|
||||
impl<'a> SubscriptVisitor<'a> {
|
||||
fn new(sequence_name: &'a str, index_name: &'a str) -> Self {
|
||||
Self {
|
||||
sequence_name,
|
||||
index_name,
|
||||
diagnostic_ranges: Vec::new(),
|
||||
modified: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SubscriptVisitor<'_> {
|
||||
fn is_assignment(&self, expr: &Expr) -> bool {
|
||||
// If we see the sequence, a subscript, or the index being modified, we'll stop emitting
|
||||
// diagnostics.
|
||||
match expr {
|
||||
Expr::Name(ast::ExprName { id, .. }) => {
|
||||
id == self.sequence_name || id == self.index_name
|
||||
}
|
||||
Expr::Subscript(ast::ExprSubscript { value, slice, .. }) => {
|
||||
let Expr::Name(ast::ExprName { id, .. }) = value.as_ref() else {
|
||||
return false;
|
||||
};
|
||||
if id == self.sequence_name {
|
||||
let Expr::Name(ast::ExprName { id, .. }) = slice.as_ref() else {
|
||||
return false;
|
||||
};
|
||||
if id == self.index_name {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Visitor<'_> for SubscriptVisitor<'a> {
|
||||
fn visit_stmt(&mut self, stmt: &Stmt) {
|
||||
if self.modified {
|
||||
return;
|
||||
}
|
||||
match stmt {
|
||||
Stmt::Assign(ast::StmtAssign { targets, value, .. }) => {
|
||||
self.modified = targets.iter().any(|target| self.is_assignment(target));
|
||||
self.visit_expr(value);
|
||||
}
|
||||
Stmt::AnnAssign(ast::StmtAnnAssign { target, value, .. }) => {
|
||||
if let Some(value) = value {
|
||||
self.modified = self.is_assignment(target);
|
||||
self.visit_expr(value);
|
||||
}
|
||||
}
|
||||
Stmt::AugAssign(ast::StmtAugAssign { target, value, .. }) => {
|
||||
self.modified = self.is_assignment(target);
|
||||
self.visit_expr(value);
|
||||
}
|
||||
Stmt::Delete(ast::StmtDelete { targets, .. }) => {
|
||||
self.modified = targets.iter().any(|target| self.is_assignment(target));
|
||||
}
|
||||
_ => visitor::walk_stmt(self, stmt),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, expr: &Expr) {
|
||||
if self.modified {
|
||||
return;
|
||||
}
|
||||
match expr {
|
||||
Expr::Subscript(ast::ExprSubscript {
|
||||
value,
|
||||
slice,
|
||||
range,
|
||||
..
|
||||
}) => {
|
||||
let Expr::Name(ast::ExprName { id, .. }) = value.as_ref() else {
|
||||
return;
|
||||
};
|
||||
if id == self.sequence_name {
|
||||
let Expr::Name(ast::ExprName { id, .. }) = slice.as_ref() else {
|
||||
return;
|
||||
};
|
||||
if id == self.index_name {
|
||||
self.diagnostic_ranges.push(*range);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => visitor::walk_expr(self, expr),
|
||||
}
|
||||
}
|
||||
/// Return a no-op edit for the given name.
|
||||
fn noop(name: &ast::ExprName) -> Edit {
|
||||
Edit::range_replacement(name.id.to_string(), name.range())
|
||||
}
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use anyhow::Result;
|
||||
|
||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast as ast;
|
||||
use ruff_python_ast::call_path::{format_call_path, CallPath};
|
||||
use ruff_text_size::Ranged;
|
||||
use ruff_python_ast::Expr;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::edits::add_argument;
|
||||
use crate::importer::ImportRequest;
|
||||
use crate::settings::types::PythonVersion;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `open` and related calls without an explicit `encoding`
|
||||
@@ -15,7 +21,9 @@ use crate::checkers::ast::Checker;
|
||||
/// non-portable code, with differing behavior across platforms.
|
||||
///
|
||||
/// Instead, consider using the `encoding` parameter to enforce a specific
|
||||
/// encoding.
|
||||
/// encoding. [PEP 597] recommends using `locale.getpreferredencoding(False)`
|
||||
/// as the default encoding on versions earlier than Python 3.10, and
|
||||
/// `encoding="locale"` on Python 3.10 and later.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
@@ -29,13 +37,15 @@ use crate::checkers::ast::Checker;
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: `open`](https://docs.python.org/3/library/functions.html#open)
|
||||
///
|
||||
/// [PEP 597]: https://peps.python.org/pep-0597/
|
||||
#[violation]
|
||||
pub struct UnspecifiedEncoding {
|
||||
function_name: String,
|
||||
mode: Mode,
|
||||
}
|
||||
|
||||
impl Violation for UnspecifiedEncoding {
|
||||
impl AlwaysFixableViolation for UnspecifiedEncoding {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let UnspecifiedEncoding {
|
||||
@@ -52,6 +62,10 @@ impl Violation for UnspecifiedEncoding {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> String {
|
||||
format!("Add explicit `encoding` argument")
|
||||
}
|
||||
}
|
||||
|
||||
/// PLW1514
|
||||
@@ -70,17 +84,63 @@ pub(crate) fn unspecified_encoding(checker: &mut Checker, call: &ast::ExprCall)
|
||||
return;
|
||||
};
|
||||
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
UnspecifiedEncoding {
|
||||
function_name,
|
||||
mode,
|
||||
},
|
||||
call.func.range(),
|
||||
));
|
||||
);
|
||||
|
||||
if checker.settings.target_version >= PythonVersion::Py310 {
|
||||
diagnostic.set_fix(generate_keyword_fix(checker, call));
|
||||
} else {
|
||||
diagnostic.try_set_fix(|| generate_import_fix(checker, call));
|
||||
}
|
||||
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
/// Generate an [`Edit`] for Python 3.10 and later.
|
||||
fn generate_keyword_fix(checker: &Checker, call: &ast::ExprCall) -> Fix {
|
||||
Fix::unsafe_edit(add_argument(
|
||||
&format!(
|
||||
"encoding={}",
|
||||
checker
|
||||
.generator()
|
||||
.expr(&Expr::StringLiteral(ast::ExprStringLiteral {
|
||||
value: ast::StringLiteralValue::single(ast::StringLiteral {
|
||||
value: "locale".to_string(),
|
||||
unicode: false,
|
||||
range: TextRange::default(),
|
||||
}),
|
||||
range: TextRange::default(),
|
||||
}))
|
||||
),
|
||||
&call.arguments,
|
||||
checker.indexer().comment_ranges(),
|
||||
checker.locator().contents(),
|
||||
))
|
||||
}
|
||||
|
||||
/// Generate an [`Edit`] for Python 3.9 and earlier.
|
||||
fn generate_import_fix(checker: &Checker, call: &ast::ExprCall) -> Result<Fix> {
|
||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
||||
&ImportRequest::import("locale", "getpreferredencoding"),
|
||||
call.start(),
|
||||
checker.semantic(),
|
||||
)?;
|
||||
let argument_edit = add_argument(
|
||||
&format!("encoding={binding}(False)"),
|
||||
&call.arguments,
|
||||
checker.indexer().comment_ranges(),
|
||||
checker.locator().contents(),
|
||||
);
|
||||
Ok(Fix::unsafe_edits(import_edit, [argument_edit]))
|
||||
}
|
||||
|
||||
/// Returns `true` if the given expression is a string literal containing a `b` character.
|
||||
fn is_binary_mode(expr: &ast::Expr) -> Option<bool> {
|
||||
fn is_binary_mode(expr: &Expr) -> Option<bool> {
|
||||
Some(
|
||||
expr.as_string_literal_expr()?
|
||||
.value
|
||||
@@ -92,12 +152,7 @@ fn is_binary_mode(expr: &ast::Expr) -> Option<bool> {
|
||||
/// Returns `true` if the given call lacks an explicit `encoding`.
|
||||
fn is_violation(call: &ast::ExprCall, call_path: &CallPath) -> bool {
|
||||
// If we have something like `*args`, which might contain the encoding argument, abort.
|
||||
if call
|
||||
.arguments
|
||||
.args
|
||||
.iter()
|
||||
.any(ruff_python_ast::Expr::is_starred_expr)
|
||||
{
|
||||
if call.arguments.args.iter().any(Expr::is_starred_expr) {
|
||||
return false;
|
||||
}
|
||||
// If we have something like `**kwargs`, which might contain the encoding argument, abort.
|
||||
|
||||
@@ -39,6 +39,7 @@ pub struct Settings {
|
||||
pub allow_magic_value_types: Vec<ConstantType>,
|
||||
pub allow_dunder_method_names: FxHashSet<String>,
|
||||
pub max_args: usize,
|
||||
pub max_positional_args: usize,
|
||||
pub max_returns: usize,
|
||||
pub max_bool_expr: usize,
|
||||
pub max_branches: usize,
|
||||
@@ -52,6 +53,7 @@ impl Default for Settings {
|
||||
allow_magic_value_types: vec![ConstantType::Str, ConstantType::Bytes],
|
||||
allow_dunder_method_names: FxHashSet::default(),
|
||||
max_args: 5,
|
||||
max_positional_args: 5,
|
||||
max_returns: 6,
|
||||
max_bool_expr: 5,
|
||||
max_branches: 12,
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pylint/mod.rs
|
||||
---
|
||||
too_many_positional.py:1:5: PLR0917 Too many positional arguments: (8/5)
|
||||
|
|
||||
1 | def f(x, y, z, t, u, v, w, r): # Too many positional arguments (8/3)
|
||||
| ^ PLR0917
|
||||
2 | pass
|
||||
|
|
||||
|
||||
too_many_positional.py:21:5: PLR0917 Too many positional arguments: (6/5)
|
||||
|
|
||||
21 | def f(x, y, z, /, u, v, w): # Too many positional arguments (6/3)
|
||||
| ^ PLR0917
|
||||
22 | pass
|
||||
|
|
||||
|
||||
too_many_positional.py:29:5: PLR0917 Too many positional arguments: (6/5)
|
||||
|
|
||||
29 | def f(x, y, z, a, b, c, *, u, v, w): # Too many positional arguments (6/3)
|
||||
| ^ PLR0917
|
||||
30 | pass
|
||||
|
|
||||
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pylint/mod.rs
|
||||
---
|
||||
unnecessary_dict_index_lookup.py:4:6: PLR1733 [*] Unnecessary lookup of dictionary value by key
|
||||
|
|
||||
3 | def fix_these():
|
||||
4 | [FRUITS[fruit_name] for fruit_name, fruit_count in FRUITS.items()] # PLR1733
|
||||
| ^^^^^^^^^^^^^^^^^^ PLR1733
|
||||
5 | {FRUITS[fruit_name] for fruit_name, fruit_count in FRUITS.items()} # PLR1733
|
||||
6 | {fruit_name: FRUITS[fruit_name] for fruit_name, fruit_count in FRUITS.items()} # PLR1733
|
||||
|
|
||||
= help: Use existing variable
|
||||
|
||||
ℹ Safe fix
|
||||
1 1 | FRUITS = {"apple": 1, "orange": 10, "berry": 22}
|
||||
2 2 |
|
||||
3 3 | def fix_these():
|
||||
4 |- [FRUITS[fruit_name] for fruit_name, fruit_count in FRUITS.items()] # PLR1733
|
||||
4 |+ [fruit_count for fruit_name, fruit_count in FRUITS.items()] # PLR1733
|
||||
5 5 | {FRUITS[fruit_name] for fruit_name, fruit_count in FRUITS.items()} # PLR1733
|
||||
6 6 | {fruit_name: FRUITS[fruit_name] for fruit_name, fruit_count in FRUITS.items()} # PLR1733
|
||||
7 7 |
|
||||
|
||||
unnecessary_dict_index_lookup.py:5:6: PLR1733 [*] Unnecessary lookup of dictionary value by key
|
||||
|
|
||||
3 | def fix_these():
|
||||
4 | [FRUITS[fruit_name] for fruit_name, fruit_count in FRUITS.items()] # PLR1733
|
||||
5 | {FRUITS[fruit_name] for fruit_name, fruit_count in FRUITS.items()} # PLR1733
|
||||
| ^^^^^^^^^^^^^^^^^^ PLR1733
|
||||
6 | {fruit_name: FRUITS[fruit_name] for fruit_name, fruit_count in FRUITS.items()} # PLR1733
|
||||
|
|
||||
= help: Use existing variable
|
||||
|
||||
ℹ Safe fix
|
||||
2 2 |
|
||||
3 3 | def fix_these():
|
||||
4 4 | [FRUITS[fruit_name] for fruit_name, fruit_count in FRUITS.items()] # PLR1733
|
||||
5 |- {FRUITS[fruit_name] for fruit_name, fruit_count in FRUITS.items()} # PLR1733
|
||||
5 |+ {fruit_count for fruit_name, fruit_count in FRUITS.items()} # PLR1733
|
||||
6 6 | {fruit_name: FRUITS[fruit_name] for fruit_name, fruit_count in FRUITS.items()} # PLR1733
|
||||
7 7 |
|
||||
8 8 | for fruit_name, fruit_count in FRUITS.items():
|
||||
|
||||
unnecessary_dict_index_lookup.py:6:18: PLR1733 [*] Unnecessary lookup of dictionary value by key
|
||||
|
|
||||
4 | [FRUITS[fruit_name] for fruit_name, fruit_count in FRUITS.items()] # PLR1733
|
||||
5 | {FRUITS[fruit_name] for fruit_name, fruit_count in FRUITS.items()} # PLR1733
|
||||
6 | {fruit_name: FRUITS[fruit_name] for fruit_name, fruit_count in FRUITS.items()} # PLR1733
|
||||
| ^^^^^^^^^^^^^^^^^^ PLR1733
|
||||
7 |
|
||||
8 | for fruit_name, fruit_count in FRUITS.items():
|
||||
|
|
||||
= help: Use existing variable
|
||||
|
||||
ℹ Safe fix
|
||||
3 3 | def fix_these():
|
||||
4 4 | [FRUITS[fruit_name] for fruit_name, fruit_count in FRUITS.items()] # PLR1733
|
||||
5 5 | {FRUITS[fruit_name] for fruit_name, fruit_count in FRUITS.items()} # PLR1733
|
||||
6 |- {fruit_name: FRUITS[fruit_name] for fruit_name, fruit_count in FRUITS.items()} # PLR1733
|
||||
6 |+ {fruit_name: fruit_count for fruit_name, fruit_count in FRUITS.items()} # PLR1733
|
||||
7 7 |
|
||||
8 8 | for fruit_name, fruit_count in FRUITS.items():
|
||||
9 9 | print(FRUITS[fruit_name]) # PLR1733
|
||||
|
||||
unnecessary_dict_index_lookup.py:9:15: PLR1733 [*] Unnecessary lookup of dictionary value by key
|
||||
|
|
||||
8 | for fruit_name, fruit_count in FRUITS.items():
|
||||
9 | print(FRUITS[fruit_name]) # PLR1733
|
||||
| ^^^^^^^^^^^^^^^^^^ PLR1733
|
||||
10 | blah = FRUITS[fruit_name] # PLR1733
|
||||
11 | assert FRUITS[fruit_name] == "pear" # PLR1733
|
||||
|
|
||||
= help: Use existing variable
|
||||
|
||||
ℹ Safe fix
|
||||
6 6 | {fruit_name: FRUITS[fruit_name] for fruit_name, fruit_count in FRUITS.items()} # PLR1733
|
||||
7 7 |
|
||||
8 8 | for fruit_name, fruit_count in FRUITS.items():
|
||||
9 |- print(FRUITS[fruit_name]) # PLR1733
|
||||
9 |+ print(fruit_count) # PLR1733
|
||||
10 10 | blah = FRUITS[fruit_name] # PLR1733
|
||||
11 11 | assert FRUITS[fruit_name] == "pear" # PLR1733
|
||||
12 12 |
|
||||
|
||||
unnecessary_dict_index_lookup.py:10:16: PLR1733 [*] Unnecessary lookup of dictionary value by key
|
||||
|
|
||||
8 | for fruit_name, fruit_count in FRUITS.items():
|
||||
9 | print(FRUITS[fruit_name]) # PLR1733
|
||||
10 | blah = FRUITS[fruit_name] # PLR1733
|
||||
| ^^^^^^^^^^^^^^^^^^ PLR1733
|
||||
11 | assert FRUITS[fruit_name] == "pear" # PLR1733
|
||||
|
|
||||
= help: Use existing variable
|
||||
|
||||
ℹ Safe fix
|
||||
7 7 |
|
||||
8 8 | for fruit_name, fruit_count in FRUITS.items():
|
||||
9 9 | print(FRUITS[fruit_name]) # PLR1733
|
||||
10 |- blah = FRUITS[fruit_name] # PLR1733
|
||||
10 |+ blah = fruit_count # PLR1733
|
||||
11 11 | assert FRUITS[fruit_name] == "pear" # PLR1733
|
||||
12 12 |
|
||||
13 13 |
|
||||
|
||||
unnecessary_dict_index_lookup.py:11:16: PLR1733 [*] Unnecessary lookup of dictionary value by key
|
||||
|
|
||||
9 | print(FRUITS[fruit_name]) # PLR1733
|
||||
10 | blah = FRUITS[fruit_name] # PLR1733
|
||||
11 | assert FRUITS[fruit_name] == "pear" # PLR1733
|
||||
| ^^^^^^^^^^^^^^^^^^ PLR1733
|
||||
|
|
||||
= help: Use existing variable
|
||||
|
||||
ℹ Safe fix
|
||||
8 8 | for fruit_name, fruit_count in FRUITS.items():
|
||||
9 9 | print(FRUITS[fruit_name]) # PLR1733
|
||||
10 10 | blah = FRUITS[fruit_name] # PLR1733
|
||||
11 |- assert FRUITS[fruit_name] == "pear" # PLR1733
|
||||
11 |+ assert fruit_count == "pear" # PLR1733
|
||||
12 12 |
|
||||
13 13 |
|
||||
14 14 | def dont_fix_these():
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ unnecessary_list_index_lookup.py:12:15: PLR1736 [*] Unnecessary lookup of list i
|
||||
12 |+ print(letter) # PLR1736
|
||||
13 13 | blah = letters[index] # PLR1736
|
||||
14 14 | assert letters[index] == "d" # PLR1736
|
||||
15 15 |
|
||||
15 15 |
|
||||
|
||||
unnecessary_list_index_lookup.py:13:16: PLR1736 [*] Unnecessary lookup of list item by index
|
||||
|
|
||||
@@ -99,7 +99,7 @@ unnecessary_list_index_lookup.py:13:16: PLR1736 [*] Unnecessary lookup of list i
|
||||
13 |- blah = letters[index] # PLR1736
|
||||
13 |+ blah = letter # PLR1736
|
||||
14 14 | assert letters[index] == "d" # PLR1736
|
||||
15 15 |
|
||||
15 15 |
|
||||
16 16 | for index, letter in builtins.enumerate(letters):
|
||||
|
||||
unnecessary_list_index_lookup.py:14:16: PLR1736 [*] Unnecessary lookup of list item by index
|
||||
@@ -108,7 +108,7 @@ unnecessary_list_index_lookup.py:14:16: PLR1736 [*] Unnecessary lookup of list i
|
||||
13 | blah = letters[index] # PLR1736
|
||||
14 | assert letters[index] == "d" # PLR1736
|
||||
| ^^^^^^^^^^^^^^ PLR1736
|
||||
15 |
|
||||
15 |
|
||||
16 | for index, letter in builtins.enumerate(letters):
|
||||
|
|
||||
= help: Use existing variable
|
||||
@@ -119,7 +119,7 @@ unnecessary_list_index_lookup.py:14:16: PLR1736 [*] Unnecessary lookup of list i
|
||||
13 13 | blah = letters[index] # PLR1736
|
||||
14 |- assert letters[index] == "d" # PLR1736
|
||||
14 |+ assert letter == "d" # PLR1736
|
||||
15 15 |
|
||||
15 15 |
|
||||
16 16 | for index, letter in builtins.enumerate(letters):
|
||||
17 17 | print(letters[index]) # PLR1736
|
||||
|
||||
@@ -135,7 +135,7 @@ unnecessary_list_index_lookup.py:17:15: PLR1736 [*] Unnecessary lookup of list i
|
||||
|
||||
ℹ Safe fix
|
||||
14 14 | assert letters[index] == "d" # PLR1736
|
||||
15 15 |
|
||||
15 15 |
|
||||
16 16 | for index, letter in builtins.enumerate(letters):
|
||||
17 |- print(letters[index]) # PLR1736
|
||||
17 |+ print(letter) # PLR1736
|
||||
@@ -154,7 +154,7 @@ unnecessary_list_index_lookup.py:18:16: PLR1736 [*] Unnecessary lookup of list i
|
||||
= help: Use existing variable
|
||||
|
||||
ℹ Safe fix
|
||||
15 15 |
|
||||
15 15 |
|
||||
16 16 | for index, letter in builtins.enumerate(letters):
|
||||
17 17 | print(letters[index]) # PLR1736
|
||||
18 |- blah = letters[index] # PLR1736
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pylint/mod.rs
|
||||
---
|
||||
unspecified_encoding.py:8:1: PLW1514 `open` in text mode without explicit `encoding` argument
|
||||
unspecified_encoding.py:8:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument
|
||||
|
|
||||
7 | # Errors.
|
||||
8 | open("test.txt")
|
||||
@@ -9,8 +9,19 @@ unspecified_encoding.py:8:1: PLW1514 `open` in text mode without explicit `encod
|
||||
9 | io.TextIOWrapper(io.FileIO("test.txt"))
|
||||
10 | hugo.TextIOWrapper(hugo.FileIO("test.txt"))
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
unspecified_encoding.py:9:1: PLW1514 `io.TextIOWrapper` without explicit `encoding` argument
|
||||
ℹ Unsafe fix
|
||||
5 5 | import codecs
|
||||
6 6 |
|
||||
7 7 | # Errors.
|
||||
8 |-open("test.txt")
|
||||
8 |+open("test.txt", encoding="locale")
|
||||
9 9 | io.TextIOWrapper(io.FileIO("test.txt"))
|
||||
10 10 | hugo.TextIOWrapper(hugo.FileIO("test.txt"))
|
||||
11 11 | tempfile.NamedTemporaryFile("w")
|
||||
|
||||
unspecified_encoding.py:9:1: PLW1514 [*] `io.TextIOWrapper` without explicit `encoding` argument
|
||||
|
|
||||
7 | # Errors.
|
||||
8 | open("test.txt")
|
||||
@@ -19,8 +30,19 @@ unspecified_encoding.py:9:1: PLW1514 `io.TextIOWrapper` without explicit `encodi
|
||||
10 | hugo.TextIOWrapper(hugo.FileIO("test.txt"))
|
||||
11 | tempfile.NamedTemporaryFile("w")
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
unspecified_encoding.py:10:1: PLW1514 `io.TextIOWrapper` without explicit `encoding` argument
|
||||
ℹ Unsafe fix
|
||||
6 6 |
|
||||
7 7 | # Errors.
|
||||
8 8 | open("test.txt")
|
||||
9 |-io.TextIOWrapper(io.FileIO("test.txt"))
|
||||
9 |+io.TextIOWrapper(io.FileIO("test.txt"), encoding="locale")
|
||||
10 10 | hugo.TextIOWrapper(hugo.FileIO("test.txt"))
|
||||
11 11 | tempfile.NamedTemporaryFile("w")
|
||||
12 12 | tempfile.TemporaryFile("w")
|
||||
|
||||
unspecified_encoding.py:10:1: PLW1514 [*] `io.TextIOWrapper` without explicit `encoding` argument
|
||||
|
|
||||
8 | open("test.txt")
|
||||
9 | io.TextIOWrapper(io.FileIO("test.txt"))
|
||||
@@ -29,8 +51,19 @@ unspecified_encoding.py:10:1: PLW1514 `io.TextIOWrapper` without explicit `encod
|
||||
11 | tempfile.NamedTemporaryFile("w")
|
||||
12 | tempfile.TemporaryFile("w")
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
unspecified_encoding.py:11:1: PLW1514 `tempfile.NamedTemporaryFile` in text mode without explicit `encoding` argument
|
||||
ℹ Unsafe fix
|
||||
7 7 | # Errors.
|
||||
8 8 | open("test.txt")
|
||||
9 9 | io.TextIOWrapper(io.FileIO("test.txt"))
|
||||
10 |-hugo.TextIOWrapper(hugo.FileIO("test.txt"))
|
||||
10 |+hugo.TextIOWrapper(hugo.FileIO("test.txt"), encoding="locale")
|
||||
11 11 | tempfile.NamedTemporaryFile("w")
|
||||
12 12 | tempfile.TemporaryFile("w")
|
||||
13 13 | codecs.open("test.txt")
|
||||
|
||||
unspecified_encoding.py:11:1: PLW1514 [*] `tempfile.NamedTemporaryFile` in text mode without explicit `encoding` argument
|
||||
|
|
||||
9 | io.TextIOWrapper(io.FileIO("test.txt"))
|
||||
10 | hugo.TextIOWrapper(hugo.FileIO("test.txt"))
|
||||
@@ -39,8 +72,19 @@ unspecified_encoding.py:11:1: PLW1514 `tempfile.NamedTemporaryFile` in text mode
|
||||
12 | tempfile.TemporaryFile("w")
|
||||
13 | codecs.open("test.txt")
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
unspecified_encoding.py:12:1: PLW1514 `tempfile.TemporaryFile` in text mode without explicit `encoding` argument
|
||||
ℹ Unsafe fix
|
||||
8 8 | open("test.txt")
|
||||
9 9 | io.TextIOWrapper(io.FileIO("test.txt"))
|
||||
10 10 | hugo.TextIOWrapper(hugo.FileIO("test.txt"))
|
||||
11 |-tempfile.NamedTemporaryFile("w")
|
||||
11 |+tempfile.NamedTemporaryFile("w", encoding="locale")
|
||||
12 12 | tempfile.TemporaryFile("w")
|
||||
13 13 | codecs.open("test.txt")
|
||||
14 14 | tempfile.SpooledTemporaryFile(0, "w")
|
||||
|
||||
unspecified_encoding.py:12:1: PLW1514 [*] `tempfile.TemporaryFile` in text mode without explicit `encoding` argument
|
||||
|
|
||||
10 | hugo.TextIOWrapper(hugo.FileIO("test.txt"))
|
||||
11 | tempfile.NamedTemporaryFile("w")
|
||||
@@ -49,8 +93,19 @@ unspecified_encoding.py:12:1: PLW1514 `tempfile.TemporaryFile` in text mode with
|
||||
13 | codecs.open("test.txt")
|
||||
14 | tempfile.SpooledTemporaryFile(0, "w")
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
unspecified_encoding.py:13:1: PLW1514 `codecs.open` in text mode without explicit `encoding` argument
|
||||
ℹ Unsafe fix
|
||||
9 9 | io.TextIOWrapper(io.FileIO("test.txt"))
|
||||
10 10 | hugo.TextIOWrapper(hugo.FileIO("test.txt"))
|
||||
11 11 | tempfile.NamedTemporaryFile("w")
|
||||
12 |-tempfile.TemporaryFile("w")
|
||||
12 |+tempfile.TemporaryFile("w", encoding="locale")
|
||||
13 13 | codecs.open("test.txt")
|
||||
14 14 | tempfile.SpooledTemporaryFile(0, "w")
|
||||
15 15 |
|
||||
|
||||
unspecified_encoding.py:13:1: PLW1514 [*] `codecs.open` in text mode without explicit `encoding` argument
|
||||
|
|
||||
11 | tempfile.NamedTemporaryFile("w")
|
||||
12 | tempfile.TemporaryFile("w")
|
||||
@@ -58,8 +113,19 @@ unspecified_encoding.py:13:1: PLW1514 `codecs.open` in text mode without explici
|
||||
| ^^^^^^^^^^^ PLW1514
|
||||
14 | tempfile.SpooledTemporaryFile(0, "w")
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
unspecified_encoding.py:14:1: PLW1514 `tempfile.SpooledTemporaryFile` in text mode without explicit `encoding` argument
|
||||
ℹ Unsafe fix
|
||||
10 10 | hugo.TextIOWrapper(hugo.FileIO("test.txt"))
|
||||
11 11 | tempfile.NamedTemporaryFile("w")
|
||||
12 12 | tempfile.TemporaryFile("w")
|
||||
13 |-codecs.open("test.txt")
|
||||
13 |+codecs.open("test.txt", encoding="locale")
|
||||
14 14 | tempfile.SpooledTemporaryFile(0, "w")
|
||||
15 15 |
|
||||
16 16 | # Non-errors.
|
||||
|
||||
unspecified_encoding.py:14:1: PLW1514 [*] `tempfile.SpooledTemporaryFile` in text mode without explicit `encoding` argument
|
||||
|
|
||||
12 | tempfile.TemporaryFile("w")
|
||||
13 | codecs.open("test.txt")
|
||||
@@ -68,5 +134,223 @@ unspecified_encoding.py:14:1: PLW1514 `tempfile.SpooledTemporaryFile` in text mo
|
||||
15 |
|
||||
16 | # Non-errors.
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
11 11 | tempfile.NamedTemporaryFile("w")
|
||||
12 12 | tempfile.TemporaryFile("w")
|
||||
13 13 | codecs.open("test.txt")
|
||||
14 |-tempfile.SpooledTemporaryFile(0, "w")
|
||||
14 |+tempfile.SpooledTemporaryFile(0, "w", encoding="locale")
|
||||
15 15 |
|
||||
16 16 | # Non-errors.
|
||||
17 17 | open("test.txt", encoding="utf-8")
|
||||
|
||||
unspecified_encoding.py:46:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument
|
||||
|
|
||||
44 | tempfile.SpooledTemporaryFile(0, )
|
||||
45 |
|
||||
46 | open("test.txt",)
|
||||
| ^^^^ PLW1514
|
||||
47 | open()
|
||||
48 | open(
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
43 43 | tempfile.SpooledTemporaryFile(0, "wb")
|
||||
44 44 | tempfile.SpooledTemporaryFile(0, )
|
||||
45 45 |
|
||||
46 |-open("test.txt",)
|
||||
46 |+open("test.txt", encoding="locale",)
|
||||
47 47 | open()
|
||||
48 48 | open(
|
||||
49 49 | "test.txt", # comment
|
||||
|
||||
unspecified_encoding.py:47:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument
|
||||
|
|
||||
46 | open("test.txt",)
|
||||
47 | open()
|
||||
| ^^^^ PLW1514
|
||||
48 | open(
|
||||
49 | "test.txt", # comment
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
44 44 | tempfile.SpooledTemporaryFile(0, )
|
||||
45 45 |
|
||||
46 46 | open("test.txt",)
|
||||
47 |-open()
|
||||
47 |+open(encoding="locale")
|
||||
48 48 | open(
|
||||
49 49 | "test.txt", # comment
|
||||
50 50 | )
|
||||
|
||||
unspecified_encoding.py:48:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument
|
||||
|
|
||||
46 | open("test.txt",)
|
||||
47 | open()
|
||||
48 | open(
|
||||
| ^^^^ PLW1514
|
||||
49 | "test.txt", # comment
|
||||
50 | )
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
46 46 | open("test.txt",)
|
||||
47 47 | open()
|
||||
48 48 | open(
|
||||
49 |- "test.txt", # comment
|
||||
49 |+ "test.txt", encoding="locale", # comment
|
||||
50 50 | )
|
||||
51 51 | open(
|
||||
52 52 | "test.txt",
|
||||
|
||||
unspecified_encoding.py:51:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument
|
||||
|
|
||||
49 | "test.txt", # comment
|
||||
50 | )
|
||||
51 | open(
|
||||
| ^^^^ PLW1514
|
||||
52 | "test.txt",
|
||||
53 | # comment
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
49 49 | "test.txt", # comment
|
||||
50 50 | )
|
||||
51 51 | open(
|
||||
52 |- "test.txt",
|
||||
52 |+ "test.txt", encoding="locale",
|
||||
53 53 | # comment
|
||||
54 54 | )
|
||||
55 55 | open(("test.txt"),)
|
||||
|
||||
unspecified_encoding.py:55:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument
|
||||
|
|
||||
53 | # comment
|
||||
54 | )
|
||||
55 | open(("test.txt"),)
|
||||
| ^^^^ PLW1514
|
||||
56 | open(
|
||||
57 | ("test.txt"), # comment
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
52 52 | "test.txt",
|
||||
53 53 | # comment
|
||||
54 54 | )
|
||||
55 |-open(("test.txt"),)
|
||||
55 |+open(("test.txt"), encoding="locale",)
|
||||
56 56 | open(
|
||||
57 57 | ("test.txt"), # comment
|
||||
58 58 | )
|
||||
|
||||
unspecified_encoding.py:56:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument
|
||||
|
|
||||
54 | )
|
||||
55 | open(("test.txt"),)
|
||||
56 | open(
|
||||
| ^^^^ PLW1514
|
||||
57 | ("test.txt"), # comment
|
||||
58 | )
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
54 54 | )
|
||||
55 55 | open(("test.txt"),)
|
||||
56 56 | open(
|
||||
57 |- ("test.txt"), # comment
|
||||
57 |+ ("test.txt"), encoding="locale", # comment
|
||||
58 58 | )
|
||||
59 59 | open(
|
||||
60 60 | ("test.txt"),
|
||||
|
||||
unspecified_encoding.py:59:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument
|
||||
|
|
||||
57 | ("test.txt"), # comment
|
||||
58 | )
|
||||
59 | open(
|
||||
| ^^^^ PLW1514
|
||||
60 | ("test.txt"),
|
||||
61 | # comment
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
57 57 | ("test.txt"), # comment
|
||||
58 58 | )
|
||||
59 59 | open(
|
||||
60 |- ("test.txt"),
|
||||
60 |+ ("test.txt"), encoding="locale",
|
||||
61 61 | # comment
|
||||
62 62 | )
|
||||
63 63 |
|
||||
|
||||
unspecified_encoding.py:64:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument
|
||||
|
|
||||
62 | )
|
||||
63 |
|
||||
64 | open((("test.txt")),)
|
||||
| ^^^^ PLW1514
|
||||
65 | open(
|
||||
66 | (("test.txt")), # comment
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
61 61 | # comment
|
||||
62 62 | )
|
||||
63 63 |
|
||||
64 |-open((("test.txt")),)
|
||||
64 |+open((("test.txt")), encoding="locale",)
|
||||
65 65 | open(
|
||||
66 66 | (("test.txt")), # comment
|
||||
67 67 | )
|
||||
|
||||
unspecified_encoding.py:65:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument
|
||||
|
|
||||
64 | open((("test.txt")),)
|
||||
65 | open(
|
||||
| ^^^^ PLW1514
|
||||
66 | (("test.txt")), # comment
|
||||
67 | )
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
63 63 |
|
||||
64 64 | open((("test.txt")),)
|
||||
65 65 | open(
|
||||
66 |- (("test.txt")), # comment
|
||||
66 |+ (("test.txt")), encoding="locale", # comment
|
||||
67 67 | )
|
||||
68 68 | open(
|
||||
69 69 | (("test.txt")),
|
||||
|
||||
unspecified_encoding.py:68:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument
|
||||
|
|
||||
66 | (("test.txt")), # comment
|
||||
67 | )
|
||||
68 | open(
|
||||
| ^^^^ PLW1514
|
||||
69 | (("test.txt")),
|
||||
70 | # comment
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
66 66 | (("test.txt")), # comment
|
||||
67 67 | )
|
||||
68 68 | open(
|
||||
69 |- (("test.txt")),
|
||||
69 |+ (("test.txt")), encoding="locale",
|
||||
70 70 | # comment
|
||||
71 71 | )
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pylint/mod.rs
|
||||
---
|
||||
too_many_positional_params.py:3:5: PLR0917 Too many positional arguments: (7/4)
|
||||
|
|
||||
1 | # Too many positional arguments (7/4) for max_positional=4
|
||||
2 | # OK for dummy_variable_rgx ~ "skip_.*"
|
||||
3 | def f(w, x, y, z, skip_t, skip_u, skip_v):
|
||||
| ^ PLR0917
|
||||
4 | pass
|
||||
|
|
||||
|
||||
too_many_positional_params.py:9:5: PLR0917 Too many positional arguments: (7/4)
|
||||
|
|
||||
7 | # Too many positional arguments (7/4) for max_args=4
|
||||
8 | # Too many positional arguments (7/3) for dummy_variable_rgx ~ "skip_.*"
|
||||
9 | def f(w, x, y, z, t, u, v):
|
||||
| ^ PLR0917
|
||||
10 | pass
|
||||
|
|
||||
|
||||
|
||||
@@ -0,0 +1,477 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pylint/mod.rs
|
||||
---
|
||||
unspecified_encoding.py:8:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument
|
||||
|
|
||||
7 | # Errors.
|
||||
8 | open("test.txt")
|
||||
| ^^^^ PLW1514
|
||||
9 | io.TextIOWrapper(io.FileIO("test.txt"))
|
||||
10 | hugo.TextIOWrapper(hugo.FileIO("test.txt"))
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
3 3 | import tempfile
|
||||
4 4 | import io as hugo
|
||||
5 5 | import codecs
|
||||
6 |+import locale
|
||||
6 7 |
|
||||
7 8 | # Errors.
|
||||
8 |-open("test.txt")
|
||||
9 |+open("test.txt", encoding=locale.getpreferredencoding(False))
|
||||
9 10 | io.TextIOWrapper(io.FileIO("test.txt"))
|
||||
10 11 | hugo.TextIOWrapper(hugo.FileIO("test.txt"))
|
||||
11 12 | tempfile.NamedTemporaryFile("w")
|
||||
|
||||
unspecified_encoding.py:9:1: PLW1514 [*] `io.TextIOWrapper` without explicit `encoding` argument
|
||||
|
|
||||
7 | # Errors.
|
||||
8 | open("test.txt")
|
||||
9 | io.TextIOWrapper(io.FileIO("test.txt"))
|
||||
| ^^^^^^^^^^^^^^^^ PLW1514
|
||||
10 | hugo.TextIOWrapper(hugo.FileIO("test.txt"))
|
||||
11 | tempfile.NamedTemporaryFile("w")
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
3 3 | import tempfile
|
||||
4 4 | import io as hugo
|
||||
5 5 | import codecs
|
||||
6 |+import locale
|
||||
6 7 |
|
||||
7 8 | # Errors.
|
||||
8 9 | open("test.txt")
|
||||
9 |-io.TextIOWrapper(io.FileIO("test.txt"))
|
||||
10 |+io.TextIOWrapper(io.FileIO("test.txt"), encoding=locale.getpreferredencoding(False))
|
||||
10 11 | hugo.TextIOWrapper(hugo.FileIO("test.txt"))
|
||||
11 12 | tempfile.NamedTemporaryFile("w")
|
||||
12 13 | tempfile.TemporaryFile("w")
|
||||
|
||||
unspecified_encoding.py:10:1: PLW1514 [*] `io.TextIOWrapper` without explicit `encoding` argument
|
||||
|
|
||||
8 | open("test.txt")
|
||||
9 | io.TextIOWrapper(io.FileIO("test.txt"))
|
||||
10 | hugo.TextIOWrapper(hugo.FileIO("test.txt"))
|
||||
| ^^^^^^^^^^^^^^^^^^ PLW1514
|
||||
11 | tempfile.NamedTemporaryFile("w")
|
||||
12 | tempfile.TemporaryFile("w")
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
3 3 | import tempfile
|
||||
4 4 | import io as hugo
|
||||
5 5 | import codecs
|
||||
6 |+import locale
|
||||
6 7 |
|
||||
7 8 | # Errors.
|
||||
8 9 | open("test.txt")
|
||||
9 10 | io.TextIOWrapper(io.FileIO("test.txt"))
|
||||
10 |-hugo.TextIOWrapper(hugo.FileIO("test.txt"))
|
||||
11 |+hugo.TextIOWrapper(hugo.FileIO("test.txt"), encoding=locale.getpreferredencoding(False))
|
||||
11 12 | tempfile.NamedTemporaryFile("w")
|
||||
12 13 | tempfile.TemporaryFile("w")
|
||||
13 14 | codecs.open("test.txt")
|
||||
|
||||
unspecified_encoding.py:11:1: PLW1514 [*] `tempfile.NamedTemporaryFile` in text mode without explicit `encoding` argument
|
||||
|
|
||||
9 | io.TextIOWrapper(io.FileIO("test.txt"))
|
||||
10 | hugo.TextIOWrapper(hugo.FileIO("test.txt"))
|
||||
11 | tempfile.NamedTemporaryFile("w")
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW1514
|
||||
12 | tempfile.TemporaryFile("w")
|
||||
13 | codecs.open("test.txt")
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
3 3 | import tempfile
|
||||
4 4 | import io as hugo
|
||||
5 5 | import codecs
|
||||
6 |+import locale
|
||||
6 7 |
|
||||
7 8 | # Errors.
|
||||
8 9 | open("test.txt")
|
||||
9 10 | io.TextIOWrapper(io.FileIO("test.txt"))
|
||||
10 11 | hugo.TextIOWrapper(hugo.FileIO("test.txt"))
|
||||
11 |-tempfile.NamedTemporaryFile("w")
|
||||
12 |+tempfile.NamedTemporaryFile("w", encoding=locale.getpreferredencoding(False))
|
||||
12 13 | tempfile.TemporaryFile("w")
|
||||
13 14 | codecs.open("test.txt")
|
||||
14 15 | tempfile.SpooledTemporaryFile(0, "w")
|
||||
|
||||
unspecified_encoding.py:12:1: PLW1514 [*] `tempfile.TemporaryFile` in text mode without explicit `encoding` argument
|
||||
|
|
||||
10 | hugo.TextIOWrapper(hugo.FileIO("test.txt"))
|
||||
11 | tempfile.NamedTemporaryFile("w")
|
||||
12 | tempfile.TemporaryFile("w")
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ PLW1514
|
||||
13 | codecs.open("test.txt")
|
||||
14 | tempfile.SpooledTemporaryFile(0, "w")
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
3 3 | import tempfile
|
||||
4 4 | import io as hugo
|
||||
5 5 | import codecs
|
||||
6 |+import locale
|
||||
6 7 |
|
||||
7 8 | # Errors.
|
||||
8 9 | open("test.txt")
|
||||
9 10 | io.TextIOWrapper(io.FileIO("test.txt"))
|
||||
10 11 | hugo.TextIOWrapper(hugo.FileIO("test.txt"))
|
||||
11 12 | tempfile.NamedTemporaryFile("w")
|
||||
12 |-tempfile.TemporaryFile("w")
|
||||
13 |+tempfile.TemporaryFile("w", encoding=locale.getpreferredencoding(False))
|
||||
13 14 | codecs.open("test.txt")
|
||||
14 15 | tempfile.SpooledTemporaryFile(0, "w")
|
||||
15 16 |
|
||||
|
||||
unspecified_encoding.py:13:1: PLW1514 [*] `codecs.open` in text mode without explicit `encoding` argument
|
||||
|
|
||||
11 | tempfile.NamedTemporaryFile("w")
|
||||
12 | tempfile.TemporaryFile("w")
|
||||
13 | codecs.open("test.txt")
|
||||
| ^^^^^^^^^^^ PLW1514
|
||||
14 | tempfile.SpooledTemporaryFile(0, "w")
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
3 3 | import tempfile
|
||||
4 4 | import io as hugo
|
||||
5 5 | import codecs
|
||||
6 |+import locale
|
||||
6 7 |
|
||||
7 8 | # Errors.
|
||||
8 9 | open("test.txt")
|
||||
--------------------------------------------------------------------------------
|
||||
10 11 | hugo.TextIOWrapper(hugo.FileIO("test.txt"))
|
||||
11 12 | tempfile.NamedTemporaryFile("w")
|
||||
12 13 | tempfile.TemporaryFile("w")
|
||||
13 |-codecs.open("test.txt")
|
||||
14 |+codecs.open("test.txt", encoding=locale.getpreferredencoding(False))
|
||||
14 15 | tempfile.SpooledTemporaryFile(0, "w")
|
||||
15 16 |
|
||||
16 17 | # Non-errors.
|
||||
|
||||
unspecified_encoding.py:14:1: PLW1514 [*] `tempfile.SpooledTemporaryFile` in text mode without explicit `encoding` argument
|
||||
|
|
||||
12 | tempfile.TemporaryFile("w")
|
||||
13 | codecs.open("test.txt")
|
||||
14 | tempfile.SpooledTemporaryFile(0, "w")
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLW1514
|
||||
15 |
|
||||
16 | # Non-errors.
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
3 3 | import tempfile
|
||||
4 4 | import io as hugo
|
||||
5 5 | import codecs
|
||||
6 |+import locale
|
||||
6 7 |
|
||||
7 8 | # Errors.
|
||||
8 9 | open("test.txt")
|
||||
--------------------------------------------------------------------------------
|
||||
11 12 | tempfile.NamedTemporaryFile("w")
|
||||
12 13 | tempfile.TemporaryFile("w")
|
||||
13 14 | codecs.open("test.txt")
|
||||
14 |-tempfile.SpooledTemporaryFile(0, "w")
|
||||
15 |+tempfile.SpooledTemporaryFile(0, "w", encoding=locale.getpreferredencoding(False))
|
||||
15 16 |
|
||||
16 17 | # Non-errors.
|
||||
17 18 | open("test.txt", encoding="utf-8")
|
||||
|
||||
unspecified_encoding.py:46:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument
|
||||
|
|
||||
44 | tempfile.SpooledTemporaryFile(0, )
|
||||
45 |
|
||||
46 | open("test.txt",)
|
||||
| ^^^^ PLW1514
|
||||
47 | open()
|
||||
48 | open(
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
3 3 | import tempfile
|
||||
4 4 | import io as hugo
|
||||
5 5 | import codecs
|
||||
6 |+import locale
|
||||
6 7 |
|
||||
7 8 | # Errors.
|
||||
8 9 | open("test.txt")
|
||||
--------------------------------------------------------------------------------
|
||||
43 44 | tempfile.SpooledTemporaryFile(0, "wb")
|
||||
44 45 | tempfile.SpooledTemporaryFile(0, )
|
||||
45 46 |
|
||||
46 |-open("test.txt",)
|
||||
47 |+open("test.txt", encoding=locale.getpreferredencoding(False),)
|
||||
47 48 | open()
|
||||
48 49 | open(
|
||||
49 50 | "test.txt", # comment
|
||||
|
||||
unspecified_encoding.py:47:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument
|
||||
|
|
||||
46 | open("test.txt",)
|
||||
47 | open()
|
||||
| ^^^^ PLW1514
|
||||
48 | open(
|
||||
49 | "test.txt", # comment
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
3 3 | import tempfile
|
||||
4 4 | import io as hugo
|
||||
5 5 | import codecs
|
||||
6 |+import locale
|
||||
6 7 |
|
||||
7 8 | # Errors.
|
||||
8 9 | open("test.txt")
|
||||
--------------------------------------------------------------------------------
|
||||
44 45 | tempfile.SpooledTemporaryFile(0, )
|
||||
45 46 |
|
||||
46 47 | open("test.txt",)
|
||||
47 |-open()
|
||||
48 |+open(encoding=locale.getpreferredencoding(False))
|
||||
48 49 | open(
|
||||
49 50 | "test.txt", # comment
|
||||
50 51 | )
|
||||
|
||||
unspecified_encoding.py:48:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument
|
||||
|
|
||||
46 | open("test.txt",)
|
||||
47 | open()
|
||||
48 | open(
|
||||
| ^^^^ PLW1514
|
||||
49 | "test.txt", # comment
|
||||
50 | )
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
3 3 | import tempfile
|
||||
4 4 | import io as hugo
|
||||
5 5 | import codecs
|
||||
6 |+import locale
|
||||
6 7 |
|
||||
7 8 | # Errors.
|
||||
8 9 | open("test.txt")
|
||||
--------------------------------------------------------------------------------
|
||||
46 47 | open("test.txt",)
|
||||
47 48 | open()
|
||||
48 49 | open(
|
||||
49 |- "test.txt", # comment
|
||||
50 |+ "test.txt", encoding=locale.getpreferredencoding(False), # comment
|
||||
50 51 | )
|
||||
51 52 | open(
|
||||
52 53 | "test.txt",
|
||||
|
||||
unspecified_encoding.py:51:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument
|
||||
|
|
||||
49 | "test.txt", # comment
|
||||
50 | )
|
||||
51 | open(
|
||||
| ^^^^ PLW1514
|
||||
52 | "test.txt",
|
||||
53 | # comment
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
3 3 | import tempfile
|
||||
4 4 | import io as hugo
|
||||
5 5 | import codecs
|
||||
6 |+import locale
|
||||
6 7 |
|
||||
7 8 | # Errors.
|
||||
8 9 | open("test.txt")
|
||||
--------------------------------------------------------------------------------
|
||||
49 50 | "test.txt", # comment
|
||||
50 51 | )
|
||||
51 52 | open(
|
||||
52 |- "test.txt",
|
||||
53 |+ "test.txt", encoding=locale.getpreferredencoding(False),
|
||||
53 54 | # comment
|
||||
54 55 | )
|
||||
55 56 | open(("test.txt"),)
|
||||
|
||||
unspecified_encoding.py:55:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument
|
||||
|
|
||||
53 | # comment
|
||||
54 | )
|
||||
55 | open(("test.txt"),)
|
||||
| ^^^^ PLW1514
|
||||
56 | open(
|
||||
57 | ("test.txt"), # comment
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
3 3 | import tempfile
|
||||
4 4 | import io as hugo
|
||||
5 5 | import codecs
|
||||
6 |+import locale
|
||||
6 7 |
|
||||
7 8 | # Errors.
|
||||
8 9 | open("test.txt")
|
||||
--------------------------------------------------------------------------------
|
||||
52 53 | "test.txt",
|
||||
53 54 | # comment
|
||||
54 55 | )
|
||||
55 |-open(("test.txt"),)
|
||||
56 |+open(("test.txt"), encoding=locale.getpreferredencoding(False),)
|
||||
56 57 | open(
|
||||
57 58 | ("test.txt"), # comment
|
||||
58 59 | )
|
||||
|
||||
unspecified_encoding.py:56:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument
|
||||
|
|
||||
54 | )
|
||||
55 | open(("test.txt"),)
|
||||
56 | open(
|
||||
| ^^^^ PLW1514
|
||||
57 | ("test.txt"), # comment
|
||||
58 | )
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
3 3 | import tempfile
|
||||
4 4 | import io as hugo
|
||||
5 5 | import codecs
|
||||
6 |+import locale
|
||||
6 7 |
|
||||
7 8 | # Errors.
|
||||
8 9 | open("test.txt")
|
||||
--------------------------------------------------------------------------------
|
||||
54 55 | )
|
||||
55 56 | open(("test.txt"),)
|
||||
56 57 | open(
|
||||
57 |- ("test.txt"), # comment
|
||||
58 |+ ("test.txt"), encoding=locale.getpreferredencoding(False), # comment
|
||||
58 59 | )
|
||||
59 60 | open(
|
||||
60 61 | ("test.txt"),
|
||||
|
||||
unspecified_encoding.py:59:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument
|
||||
|
|
||||
57 | ("test.txt"), # comment
|
||||
58 | )
|
||||
59 | open(
|
||||
| ^^^^ PLW1514
|
||||
60 | ("test.txt"),
|
||||
61 | # comment
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
3 3 | import tempfile
|
||||
4 4 | import io as hugo
|
||||
5 5 | import codecs
|
||||
6 |+import locale
|
||||
6 7 |
|
||||
7 8 | # Errors.
|
||||
8 9 | open("test.txt")
|
||||
--------------------------------------------------------------------------------
|
||||
57 58 | ("test.txt"), # comment
|
||||
58 59 | )
|
||||
59 60 | open(
|
||||
60 |- ("test.txt"),
|
||||
61 |+ ("test.txt"), encoding=locale.getpreferredencoding(False),
|
||||
61 62 | # comment
|
||||
62 63 | )
|
||||
63 64 |
|
||||
|
||||
unspecified_encoding.py:64:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument
|
||||
|
|
||||
62 | )
|
||||
63 |
|
||||
64 | open((("test.txt")),)
|
||||
| ^^^^ PLW1514
|
||||
65 | open(
|
||||
66 | (("test.txt")), # comment
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
3 3 | import tempfile
|
||||
4 4 | import io as hugo
|
||||
5 5 | import codecs
|
||||
6 |+import locale
|
||||
6 7 |
|
||||
7 8 | # Errors.
|
||||
8 9 | open("test.txt")
|
||||
--------------------------------------------------------------------------------
|
||||
61 62 | # comment
|
||||
62 63 | )
|
||||
63 64 |
|
||||
64 |-open((("test.txt")),)
|
||||
65 |+open((("test.txt")), encoding=locale.getpreferredencoding(False),)
|
||||
65 66 | open(
|
||||
66 67 | (("test.txt")), # comment
|
||||
67 68 | )
|
||||
|
||||
unspecified_encoding.py:65:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument
|
||||
|
|
||||
64 | open((("test.txt")),)
|
||||
65 | open(
|
||||
| ^^^^ PLW1514
|
||||
66 | (("test.txt")), # comment
|
||||
67 | )
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
3 3 | import tempfile
|
||||
4 4 | import io as hugo
|
||||
5 5 | import codecs
|
||||
6 |+import locale
|
||||
6 7 |
|
||||
7 8 | # Errors.
|
||||
8 9 | open("test.txt")
|
||||
--------------------------------------------------------------------------------
|
||||
63 64 |
|
||||
64 65 | open((("test.txt")),)
|
||||
65 66 | open(
|
||||
66 |- (("test.txt")), # comment
|
||||
67 |+ (("test.txt")), encoding=locale.getpreferredencoding(False), # comment
|
||||
67 68 | )
|
||||
68 69 | open(
|
||||
69 70 | (("test.txt")),
|
||||
|
||||
unspecified_encoding.py:68:1: PLW1514 [*] `open` in text mode without explicit `encoding` argument
|
||||
|
|
||||
66 | (("test.txt")), # comment
|
||||
67 | )
|
||||
68 | open(
|
||||
| ^^^^ PLW1514
|
||||
69 | (("test.txt")),
|
||||
70 | # comment
|
||||
|
|
||||
= help: Add explicit `encoding` argument
|
||||
|
||||
ℹ Unsafe fix
|
||||
3 3 | import tempfile
|
||||
4 4 | import io as hugo
|
||||
5 5 | import codecs
|
||||
6 |+import locale
|
||||
6 7 |
|
||||
7 8 | # Errors.
|
||||
8 9 | open("test.txt")
|
||||
--------------------------------------------------------------------------------
|
||||
66 67 | (("test.txt")), # comment
|
||||
67 68 | )
|
||||
68 69 | open(
|
||||
69 |- (("test.txt")),
|
||||
70 |+ (("test.txt")), encoding=locale.getpreferredencoding(False),
|
||||
70 71 | # comment
|
||||
71 72 | )
|
||||
|
||||
|
||||
@@ -490,18 +490,10 @@ pub(crate) fn printf_string_formatting(checker: &mut Checker, expr: &Expr, right
|
||||
contents.push_str(&format!(".format{params_string}"));
|
||||
|
||||
let mut diagnostic = Diagnostic::new(PrintfStringFormatting, expr.range());
|
||||
// Avoid fix if there are comments within the right-hand side:
|
||||
// ```
|
||||
// "%s" % (
|
||||
// 0, # 0
|
||||
// )
|
||||
// ```
|
||||
if !checker.indexer().comment_ranges().intersects(right.range()) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
contents,
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
contents,
|
||||
expr.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
|
||||
@@ -896,7 +896,7 @@ UP031_0.py:104:5: UP031 [*] Use format specifiers instead of percent format
|
||||
109 108 |
|
||||
110 109 | "%s" % (
|
||||
|
||||
UP031_0.py:110:1: UP031 Use format specifiers instead of percent format
|
||||
UP031_0.py:110:1: UP031 [*] Use format specifiers instead of percent format
|
||||
|
|
||||
108 | )
|
||||
109 |
|
||||
@@ -907,4 +907,36 @@ UP031_0.py:110:1: UP031 Use format specifiers instead of percent format
|
||||
|
|
||||
= help: Replace with format specifiers
|
||||
|
||||
ℹ Unsafe fix
|
||||
107 107 | % (x,)
|
||||
108 108 | )
|
||||
109 109 |
|
||||
110 |-"%s" % (
|
||||
110 |+"{}".format(
|
||||
111 111 | x, # comment
|
||||
112 112 | )
|
||||
113 113 |
|
||||
|
||||
UP031_0.py:115:8: UP031 [*] Use format specifiers instead of percent format
|
||||
|
|
||||
115 | path = "%s-%s-%s.pem" % (
|
||||
| ________^
|
||||
116 | | safe_domain_name(cn), # common name, which should be filename safe because it is IDNA-encoded, but in case of a malformed cert make sure it's ok to use as a filename
|
||||
117 | | cert.not_valid_after.date().isoformat().replace("-", ""), # expiration date
|
||||
118 | | hexlify(cert.fingerprint(hashes.SHA256())).decode("ascii")[0:8], # fingerprint prefix
|
||||
119 | | )
|
||||
| |_^ UP031
|
||||
|
|
||||
= help: Replace with format specifiers
|
||||
|
||||
ℹ Unsafe fix
|
||||
112 112 | )
|
||||
113 113 |
|
||||
114 114 |
|
||||
115 |-path = "%s-%s-%s.pem" % (
|
||||
115 |+path = "{}-{}-{}.pem".format(
|
||||
116 116 | safe_domain_name(cn), # common name, which should be filename safe because it is IDNA-encoded, but in case of a malformed cert make sure it's ok to use as a filename
|
||||
117 117 | cert.not_valid_after.date().isoformat().replace("-", ""), # expiration date
|
||||
118 118 | hexlify(cert.fingerprint(hashes.SHA256())).decode("ascii")[0:8], # fingerprint prefix
|
||||
|
||||
|
||||
|
||||
@@ -238,4 +238,65 @@ flowchart TD
|
||||
block0 --> return
|
||||
```
|
||||
|
||||
## Function 8
|
||||
### Source
|
||||
```python
|
||||
def func():
|
||||
for i in range(5):
|
||||
pass
|
||||
else:
|
||||
return 1
|
||||
```
|
||||
|
||||
### Control Flow Graph
|
||||
```mermaid
|
||||
flowchart TD
|
||||
start(("Start"))
|
||||
return(("End"))
|
||||
block0["pass\n"]
|
||||
block1["return 1\n"]
|
||||
block2["for i in range(5):
|
||||
pass
|
||||
else:
|
||||
return 1\n"]
|
||||
|
||||
start --> block2
|
||||
block2 -- "range(5)" --> block0
|
||||
block2 -- "else" --> block1
|
||||
block1 --> return
|
||||
block0 --> return
|
||||
```
|
||||
|
||||
## Function 9
|
||||
### Source
|
||||
```python
|
||||
def func():
|
||||
for i in range(5):
|
||||
pass
|
||||
else:
|
||||
return 1
|
||||
x = 1
|
||||
```
|
||||
|
||||
### Control Flow Graph
|
||||
```mermaid
|
||||
flowchart TD
|
||||
start(("Start"))
|
||||
return(("End"))
|
||||
block0["x = 1\n"]
|
||||
block1["pass\n"]
|
||||
block2["return 1\n"]
|
||||
block3["for i in range(5):
|
||||
pass
|
||||
else:
|
||||
return 1\n"]
|
||||
|
||||
start --> block3
|
||||
block3 -- "range(5)" --> block1
|
||||
block3 -- "else" --> block2
|
||||
block2 --> return
|
||||
block1 --> block3
|
||||
block0 --> return
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -773,4 +773,43 @@ flowchart TD
|
||||
block0 --> return
|
||||
```
|
||||
|
||||
## Function 14
|
||||
### Source
|
||||
```python
|
||||
def func(point):
|
||||
match point:
|
||||
case (0, 0):
|
||||
print("Origin")
|
||||
case foo:
|
||||
raise ValueError("oops")
|
||||
```
|
||||
|
||||
### Control Flow Graph
|
||||
```mermaid
|
||||
flowchart TD
|
||||
start(("Start"))
|
||||
return(("End"))
|
||||
block0[["`*(empty)*`"]]
|
||||
block1["raise ValueError(#quot;oops#quot;)\n"]
|
||||
block2["match point:
|
||||
case (0, 0):
|
||||
print(#quot;Origin#quot;)
|
||||
case foo:
|
||||
raise ValueError(#quot;oops#quot;)\n"]
|
||||
block3["print(#quot;Origin#quot;)\n"]
|
||||
block4["match point:
|
||||
case (0, 0):
|
||||
print(#quot;Origin#quot;)
|
||||
case foo:
|
||||
raise ValueError(#quot;oops#quot;)\n"]
|
||||
|
||||
start --> block4
|
||||
block4 -- "case (0, 0)" --> block3
|
||||
block4 -- "else" --> block2
|
||||
block3 --> block0
|
||||
block2 --> block1
|
||||
block1 --> return
|
||||
block0 --> return
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ use std::{fmt, iter, usize};
|
||||
|
||||
use log::error;
|
||||
use ruff_python_ast::{
|
||||
Expr, ExprBooleanLiteral, Identifier, MatchCase, Pattern, PatternMatchAs, Stmt, StmtFor,
|
||||
StmtMatch, StmtReturn, StmtTry, StmtWhile, StmtWith,
|
||||
Expr, ExprBooleanLiteral, Identifier, MatchCase, Pattern, PatternMatchAs, PatternMatchOr, Stmt,
|
||||
StmtFor, StmtMatch, StmtReturn, StmtTry, StmtWhile, StmtWith,
|
||||
};
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
||||
@@ -416,13 +416,6 @@ fn match_case<'stmt>(
|
||||
}
|
||||
last_statement_index
|
||||
};
|
||||
// TODO: handle named arguments, e.g.
|
||||
// ```python
|
||||
// match $subject:
|
||||
// case $binding:
|
||||
// print($binding)
|
||||
// ```
|
||||
// These should also return `NextBlock::Always`.
|
||||
let next = if is_wildcard(case) {
|
||||
// Wildcard case is always taken.
|
||||
NextBlock::Always(next_block_index)
|
||||
@@ -436,10 +429,25 @@ fn match_case<'stmt>(
|
||||
BasicBlock { stmts, next }
|
||||
}
|
||||
|
||||
/// Returns true if `pattern` is a wildcard (`_`) pattern.
|
||||
/// Returns true if the [`MatchCase`] is a wildcard pattern.
|
||||
fn is_wildcard(pattern: &MatchCase) -> bool {
|
||||
pattern.guard.is_none()
|
||||
&& matches!(&pattern.pattern, Pattern::MatchAs(PatternMatchAs { pattern, name, .. }) if pattern.is_none() && name.is_none())
|
||||
/// Returns true if the [`Pattern`] is a wildcard pattern.
|
||||
fn is_wildcard_pattern(pattern: &Pattern) -> bool {
|
||||
match pattern {
|
||||
Pattern::MatchValue(_)
|
||||
| Pattern::MatchSingleton(_)
|
||||
| Pattern::MatchSequence(_)
|
||||
| Pattern::MatchMapping(_)
|
||||
| Pattern::MatchClass(_)
|
||||
| Pattern::MatchStar(_) => false,
|
||||
Pattern::MatchAs(PatternMatchAs { pattern, .. }) => pattern.is_none(),
|
||||
Pattern::MatchOr(PatternMatchOr { patterns, .. }) => {
|
||||
patterns.iter().all(is_wildcard_pattern)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pattern.guard.is_none() && is_wildcard_pattern(&pattern.pattern)
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@@ -477,6 +485,8 @@ impl<'stmt> BasicBlocksBuilder<'stmt> {
|
||||
| Stmt::AugAssign(_)
|
||||
| Stmt::AnnAssign(_)
|
||||
| Stmt::Break(_)
|
||||
| Stmt::TypeAlias(_)
|
||||
| Stmt::IpyEscapeCommand(_)
|
||||
| Stmt::Pass(_) => self.unconditional_next_block(after),
|
||||
Stmt::Continue(_) => {
|
||||
// NOTE: the next branch gets fixed up in `change_next_block`.
|
||||
@@ -638,6 +648,7 @@ impl<'stmt> BasicBlocksBuilder<'stmt> {
|
||||
| Expr::Starred(_)
|
||||
| Expr::Name(_)
|
||||
| Expr::List(_)
|
||||
| Expr::IpyEscapeCommand(_)
|
||||
| Expr::Tuple(_)
|
||||
| Expr::Slice(_) => self.unconditional_next_block(after),
|
||||
// TODO: handle these expressions.
|
||||
@@ -651,13 +662,10 @@ impl<'stmt> BasicBlocksBuilder<'stmt> {
|
||||
| Expr::Await(_)
|
||||
| Expr::Yield(_)
|
||||
| Expr::YieldFrom(_) => self.unconditional_next_block(after),
|
||||
Expr::IpyEscapeCommand(_) => todo!(),
|
||||
}
|
||||
}
|
||||
// The tough branches are done, here is an easy one.
|
||||
Stmt::Return(_) => NextBlock::Terminate,
|
||||
Stmt::TypeAlias(_) => todo!(),
|
||||
Stmt::IpyEscapeCommand(_) => todo!(),
|
||||
};
|
||||
|
||||
// Include any statements in the block that don't divert the control flow.
|
||||
@@ -890,6 +898,8 @@ fn needs_next_block(stmts: &[Stmt]) -> bool {
|
||||
| Stmt::AnnAssign(_)
|
||||
| Stmt::Expr(_)
|
||||
| Stmt::Pass(_)
|
||||
| Stmt::TypeAlias(_)
|
||||
| Stmt::IpyEscapeCommand(_)
|
||||
// TODO: check below.
|
||||
| Stmt::Break(_)
|
||||
| Stmt::Continue(_)
|
||||
@@ -899,8 +909,6 @@ fn needs_next_block(stmts: &[Stmt]) -> bool {
|
||||
| Stmt::Match(_)
|
||||
| Stmt::Try(_)
|
||||
| Stmt::Assert(_) => true,
|
||||
Stmt::TypeAlias(_) => todo!(),
|
||||
Stmt::IpyEscapeCommand(_) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -919,6 +927,8 @@ fn is_control_flow_stmt(stmt: &Stmt) -> bool {
|
||||
| Stmt::AugAssign(_)
|
||||
| Stmt::AnnAssign(_)
|
||||
| Stmt::Expr(_)
|
||||
| Stmt::TypeAlias(_)
|
||||
| Stmt::IpyEscapeCommand(_)
|
||||
| Stmt::Pass(_) => false,
|
||||
Stmt::Return(_)
|
||||
| Stmt::For(_)
|
||||
@@ -931,8 +941,6 @@ fn is_control_flow_stmt(stmt: &Stmt) -> bool {
|
||||
| Stmt::Assert(_)
|
||||
| Stmt::Break(_)
|
||||
| Stmt::Continue(_) => true,
|
||||
Stmt::TypeAlias(_) => todo!(),
|
||||
Stmt::IpyEscapeCommand(_) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -344,6 +344,7 @@ RUF100_3.py:23:11: RUF100 [*] Unused `noqa` directive (unused: `E501`)
|
||||
23 |+print(a) # noqa: F821 # comment
|
||||
24 24 | print(a) # noqa: E501, F821 comment
|
||||
25 25 | print(a) # noqa: E501, F821 comment
|
||||
26 26 |
|
||||
|
||||
RUF100_3.py:24:11: RUF100 [*] Unused `noqa` directive (unused: `E501`)
|
||||
|
|
||||
@@ -362,6 +363,8 @@ RUF100_3.py:24:11: RUF100 [*] Unused `noqa` directive (unused: `E501`)
|
||||
24 |-print(a) # noqa: E501, F821 comment
|
||||
24 |+print(a) # noqa: F821 comment
|
||||
25 25 | print(a) # noqa: E501, F821 comment
|
||||
26 26 |
|
||||
27 27 | print(a) # comment with unicode µ # noqa: E501
|
||||
|
||||
RUF100_3.py:25:11: RUF100 [*] Unused `noqa` directive (unused: `E501`)
|
||||
|
|
||||
@@ -369,6 +372,8 @@ RUF100_3.py:25:11: RUF100 [*] Unused `noqa` directive (unused: `E501`)
|
||||
24 | print(a) # noqa: E501, F821 comment
|
||||
25 | print(a) # noqa: E501, F821 comment
|
||||
| ^^^^^^^^^^^^^^^^^^ RUF100
|
||||
26 |
|
||||
27 | print(a) # comment with unicode µ # noqa: E501
|
||||
|
|
||||
= help: Remove unused `noqa` directive
|
||||
|
||||
@@ -378,5 +383,50 @@ RUF100_3.py:25:11: RUF100 [*] Unused `noqa` directive (unused: `E501`)
|
||||
24 24 | print(a) # noqa: E501, F821 comment
|
||||
25 |-print(a) # noqa: E501, F821 comment
|
||||
25 |+print(a) # noqa: F821 comment
|
||||
26 26 |
|
||||
27 27 | print(a) # comment with unicode µ # noqa: E501
|
||||
28 28 | print(a) # comment with unicode µ # noqa: E501, F821
|
||||
|
||||
RUF100_3.py:27:7: F821 Undefined name `a`
|
||||
|
|
||||
25 | print(a) # noqa: E501, F821 comment
|
||||
26 |
|
||||
27 | print(a) # comment with unicode µ # noqa: E501
|
||||
| ^ F821
|
||||
28 | print(a) # comment with unicode µ # noqa: E501, F821
|
||||
|
|
||||
|
||||
RUF100_3.py:27:39: RUF100 [*] Unused `noqa` directive (unused: `E501`)
|
||||
|
|
||||
25 | print(a) # noqa: E501, F821 comment
|
||||
26 |
|
||||
27 | print(a) # comment with unicode µ # noqa: E501
|
||||
| ^^^^^^^^^^^^ RUF100
|
||||
28 | print(a) # comment with unicode µ # noqa: E501, F821
|
||||
|
|
||||
= help: Remove unused `noqa` directive
|
||||
|
||||
ℹ Safe fix
|
||||
24 24 | print(a) # noqa: E501, F821 comment
|
||||
25 25 | print(a) # noqa: E501, F821 comment
|
||||
26 26 |
|
||||
27 |-print(a) # comment with unicode µ # noqa: E501
|
||||
27 |+print(a) # comment with unicode µ
|
||||
28 28 | print(a) # comment with unicode µ # noqa: E501, F821
|
||||
|
||||
RUF100_3.py:28:39: RUF100 [*] Unused `noqa` directive (unused: `E501`)
|
||||
|
|
||||
27 | print(a) # comment with unicode µ # noqa: E501
|
||||
28 | print(a) # comment with unicode µ # noqa: E501, F821
|
||||
| ^^^^^^^^^^^^^^^^^^ RUF100
|
||||
|
|
||||
= help: Remove unused `noqa` directive
|
||||
|
||||
ℹ Safe fix
|
||||
25 25 | print(a) # noqa: E501, F821 comment
|
||||
26 26 |
|
||||
27 27 | print(a) # comment with unicode µ # noqa: E501
|
||||
28 |-print(a) # comment with unicode µ # noqa: E501, F821
|
||||
28 |+print(a) # comment with unicode µ # noqa: F821
|
||||
|
||||
|
||||
|
||||
@@ -911,6 +911,180 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the function has an implicit return.
|
||||
pub fn implicit_return(function: &ast::StmtFunctionDef) -> bool {
|
||||
/// Returns `true` if the body may break via a `break` statement.
|
||||
fn sometimes_breaks(stmts: &[Stmt]) -> bool {
|
||||
for stmt in stmts {
|
||||
match stmt {
|
||||
Stmt::For(ast::StmtFor { body, orelse, .. }) => {
|
||||
if returns(body) {
|
||||
return false;
|
||||
}
|
||||
if sometimes_breaks(orelse) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::While(ast::StmtWhile { body, orelse, .. }) => {
|
||||
if returns(body) {
|
||||
return false;
|
||||
}
|
||||
if sometimes_breaks(orelse) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::If(ast::StmtIf {
|
||||
body,
|
||||
elif_else_clauses,
|
||||
..
|
||||
}) => {
|
||||
if std::iter::once(body)
|
||||
.chain(elif_else_clauses.iter().map(|clause| &clause.body))
|
||||
.any(|body| sometimes_breaks(body))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::Match(ast::StmtMatch { cases, .. }) => {
|
||||
if cases.iter().any(|case| sometimes_breaks(&case.body)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::Try(ast::StmtTry {
|
||||
body,
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
..
|
||||
}) => {
|
||||
if sometimes_breaks(body)
|
||||
|| handlers.iter().any(|handler| {
|
||||
let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler {
|
||||
body,
|
||||
..
|
||||
}) = handler;
|
||||
sometimes_breaks(body)
|
||||
})
|
||||
|| sometimes_breaks(orelse)
|
||||
|| sometimes_breaks(finalbody)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::With(ast::StmtWith { body, .. }) => {
|
||||
if sometimes_breaks(body) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::Break(_) => return true,
|
||||
Stmt::Return(_) => return false,
|
||||
Stmt::Raise(_) => return false,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Returns `true` if the body may break via a `break` statement.
|
||||
fn always_breaks(stmts: &[Stmt]) -> bool {
|
||||
for stmt in stmts {
|
||||
match stmt {
|
||||
Stmt::Break(_) => return true,
|
||||
Stmt::Return(_) => return false,
|
||||
Stmt::Raise(_) => return false,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Returns `true` if the body contains a branch that ends without an explicit `return` or
|
||||
/// `raise` statement.
|
||||
fn returns(stmts: &[Stmt]) -> bool {
|
||||
for stmt in stmts.iter().rev() {
|
||||
match stmt {
|
||||
Stmt::For(ast::StmtFor { body, orelse, .. }) => {
|
||||
if always_breaks(body) {
|
||||
return false;
|
||||
}
|
||||
if returns(body) {
|
||||
return true;
|
||||
}
|
||||
if returns(orelse) && !sometimes_breaks(body) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::While(ast::StmtWhile { body, orelse, .. }) => {
|
||||
if always_breaks(body) {
|
||||
return false;
|
||||
}
|
||||
if returns(body) {
|
||||
return true;
|
||||
}
|
||||
if returns(orelse) && !sometimes_breaks(body) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::If(ast::StmtIf {
|
||||
body,
|
||||
elif_else_clauses,
|
||||
..
|
||||
}) => {
|
||||
if elif_else_clauses.iter().any(|clause| clause.test.is_none())
|
||||
&& std::iter::once(body)
|
||||
.chain(elif_else_clauses.iter().map(|clause| &clause.body))
|
||||
.all(|body| returns(body))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::Match(ast::StmtMatch { cases, .. }) => {
|
||||
// Note: we assume the `match` is exhaustive.
|
||||
if cases.iter().all(|case| returns(&case.body)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::Try(ast::StmtTry {
|
||||
body,
|
||||
handlers,
|
||||
orelse,
|
||||
finalbody,
|
||||
..
|
||||
}) => {
|
||||
// If the `finally` block returns, the `try` block must also return.
|
||||
if returns(finalbody) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the `body` or the `else` block returns, the `try` block must also return.
|
||||
if (returns(body) || returns(orelse))
|
||||
&& handlers.iter().all(|handler| {
|
||||
let ExceptHandler::ExceptHandler(ast::ExceptHandlerExceptHandler {
|
||||
body,
|
||||
..
|
||||
}) = handler;
|
||||
returns(body)
|
||||
})
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::With(ast::StmtWith { body, .. }) => {
|
||||
if returns(body) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::Return(_) => return true,
|
||||
Stmt::Raise(_) => return true,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
!returns(&function.body)
|
||||
}
|
||||
|
||||
/// A [`StatementVisitor`] that collects all `raise` statements in a function or method.
|
||||
#[derive(Default)]
|
||||
pub struct RaiseStatementVisitor<'a> {
|
||||
|
||||
@@ -3729,204 +3729,6 @@ impl Ranged for crate::nodes::ParameterWithDefault {
|
||||
}
|
||||
}
|
||||
|
||||
/// An expression that may be parenthesized.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ParenthesizedExpr {
|
||||
/// The range of the expression, including any parentheses.
|
||||
pub range: TextRange,
|
||||
/// The underlying expression.
|
||||
pub expr: Expr,
|
||||
}
|
||||
impl ParenthesizedExpr {
|
||||
/// Returns `true` if the expression is may be parenthesized.
|
||||
pub fn is_parenthesized(&self) -> bool {
|
||||
self.range != self.expr.range()
|
||||
}
|
||||
}
|
||||
impl Ranged for ParenthesizedExpr {
|
||||
fn range(&self) -> TextRange {
|
||||
self.range
|
||||
}
|
||||
}
|
||||
impl From<Expr> for ParenthesizedExpr {
|
||||
fn from(expr: Expr) -> Self {
|
||||
ParenthesizedExpr {
|
||||
range: expr.range(),
|
||||
expr,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<ParenthesizedExpr> for Expr {
|
||||
fn from(parenthesized_expr: ParenthesizedExpr) -> Self {
|
||||
parenthesized_expr.expr
|
||||
}
|
||||
}
|
||||
impl From<ExprIpyEscapeCommand> for ParenthesizedExpr {
|
||||
fn from(payload: ExprIpyEscapeCommand) -> Self {
|
||||
Expr::IpyEscapeCommand(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprBoolOp> for ParenthesizedExpr {
|
||||
fn from(payload: ExprBoolOp) -> Self {
|
||||
Expr::BoolOp(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprNamedExpr> for ParenthesizedExpr {
|
||||
fn from(payload: ExprNamedExpr) -> Self {
|
||||
Expr::NamedExpr(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprBinOp> for ParenthesizedExpr {
|
||||
fn from(payload: ExprBinOp) -> Self {
|
||||
Expr::BinOp(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprUnaryOp> for ParenthesizedExpr {
|
||||
fn from(payload: ExprUnaryOp) -> Self {
|
||||
Expr::UnaryOp(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprLambda> for ParenthesizedExpr {
|
||||
fn from(payload: ExprLambda) -> Self {
|
||||
Expr::Lambda(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprIfExp> for ParenthesizedExpr {
|
||||
fn from(payload: ExprIfExp) -> Self {
|
||||
Expr::IfExp(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprDict> for ParenthesizedExpr {
|
||||
fn from(payload: ExprDict) -> Self {
|
||||
Expr::Dict(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprSet> for ParenthesizedExpr {
|
||||
fn from(payload: ExprSet) -> Self {
|
||||
Expr::Set(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprListComp> for ParenthesizedExpr {
|
||||
fn from(payload: ExprListComp) -> Self {
|
||||
Expr::ListComp(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprSetComp> for ParenthesizedExpr {
|
||||
fn from(payload: ExprSetComp) -> Self {
|
||||
Expr::SetComp(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprDictComp> for ParenthesizedExpr {
|
||||
fn from(payload: ExprDictComp) -> Self {
|
||||
Expr::DictComp(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprGeneratorExp> for ParenthesizedExpr {
|
||||
fn from(payload: ExprGeneratorExp) -> Self {
|
||||
Expr::GeneratorExp(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprAwait> for ParenthesizedExpr {
|
||||
fn from(payload: ExprAwait) -> Self {
|
||||
Expr::Await(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprYield> for ParenthesizedExpr {
|
||||
fn from(payload: ExprYield) -> Self {
|
||||
Expr::Yield(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprYieldFrom> for ParenthesizedExpr {
|
||||
fn from(payload: ExprYieldFrom) -> Self {
|
||||
Expr::YieldFrom(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprCompare> for ParenthesizedExpr {
|
||||
fn from(payload: ExprCompare) -> Self {
|
||||
Expr::Compare(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprCall> for ParenthesizedExpr {
|
||||
fn from(payload: ExprCall) -> Self {
|
||||
Expr::Call(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprFormattedValue> for ParenthesizedExpr {
|
||||
fn from(payload: ExprFormattedValue) -> Self {
|
||||
Expr::FormattedValue(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprFString> for ParenthesizedExpr {
|
||||
fn from(payload: ExprFString) -> Self {
|
||||
Expr::FString(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprStringLiteral> for ParenthesizedExpr {
|
||||
fn from(payload: ExprStringLiteral) -> Self {
|
||||
Expr::StringLiteral(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprBytesLiteral> for ParenthesizedExpr {
|
||||
fn from(payload: ExprBytesLiteral) -> Self {
|
||||
Expr::BytesLiteral(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprNumberLiteral> for ParenthesizedExpr {
|
||||
fn from(payload: ExprNumberLiteral) -> Self {
|
||||
Expr::NumberLiteral(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprBooleanLiteral> for ParenthesizedExpr {
|
||||
fn from(payload: ExprBooleanLiteral) -> Self {
|
||||
Expr::BooleanLiteral(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprNoneLiteral> for ParenthesizedExpr {
|
||||
fn from(payload: ExprNoneLiteral) -> Self {
|
||||
Expr::NoneLiteral(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprEllipsisLiteral> for ParenthesizedExpr {
|
||||
fn from(payload: ExprEllipsisLiteral) -> Self {
|
||||
Expr::EllipsisLiteral(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprAttribute> for ParenthesizedExpr {
|
||||
fn from(payload: ExprAttribute) -> Self {
|
||||
Expr::Attribute(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprSubscript> for ParenthesizedExpr {
|
||||
fn from(payload: ExprSubscript) -> Self {
|
||||
Expr::Subscript(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprStarred> for ParenthesizedExpr {
|
||||
fn from(payload: ExprStarred) -> Self {
|
||||
Expr::Starred(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprName> for ParenthesizedExpr {
|
||||
fn from(payload: ExprName) -> Self {
|
||||
Expr::Name(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprList> for ParenthesizedExpr {
|
||||
fn from(payload: ExprList) -> Self {
|
||||
Expr::List(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprTuple> for ParenthesizedExpr {
|
||||
fn from(payload: ExprTuple) -> Self {
|
||||
Expr::Tuple(payload).into()
|
||||
}
|
||||
}
|
||||
impl From<ExprSlice> for ParenthesizedExpr {
|
||||
fn from(payload: ExprSlice) -> Self {
|
||||
Expr::Slice(payload).into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
mod size_assertions {
|
||||
use static_assertions::assert_eq_size;
|
||||
|
||||
@@ -139,7 +139,6 @@ pub trait PreorderVisitor<'a> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
fn visit_pattern_keyword(&mut self, pattern_keyword: &'a PatternKeyword) {
|
||||
walk_pattern_keyword(self, pattern_keyword);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user