Compare commits
67 Commits
zanie/fix-
...
zanie/shar
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f474920d87 | ||
|
|
a556319e7f | ||
|
|
a29aa78702 | ||
|
|
7a16680791 | ||
|
|
88c0106421 | ||
|
|
f60aa85471 | ||
|
|
d942a777d7 | ||
|
|
8a529925b3 | ||
|
|
dc6b4ad2b4 | ||
|
|
21ea290d6a | ||
|
|
5da0f9111e | ||
|
|
cb6d74c27b | ||
|
|
73049df3ed | ||
|
|
bf0e5788ef | ||
|
|
4113d65836 | ||
|
|
4c2c9bf7e0 | ||
|
|
172ac2c9a2 | ||
|
|
cac9754455 | ||
|
|
134def0119 | ||
|
|
1fabaca5de | ||
|
|
523f542dbd | ||
|
|
ee7575eb5a | ||
|
|
84f7391cc5 | ||
|
|
7da4e28a98 | ||
|
|
5718df638f | ||
|
|
4bb4cd3b37 | ||
|
|
620426de7a | ||
|
|
84ec66a22c | ||
|
|
e58ffa9a7a | ||
|
|
aa6846c78c | ||
|
|
3d03e75a9d | ||
|
|
b6e75e58c9 | ||
|
|
8061894af6 | ||
|
|
e261eb7461 | ||
|
|
bd06cbe0c5 | ||
|
|
ddffadb4b0 | ||
|
|
8255e4ed6c | ||
|
|
60ca6885b1 | ||
|
|
889117ea87 | ||
|
|
c03a693ebc | ||
|
|
6f9c317aa5 | ||
|
|
66179af4f1 | ||
|
|
1e184e69f3 | ||
|
|
f08a5f67eb | ||
|
|
cd564c4200 | ||
|
|
c1fdb9c46d | ||
|
|
48b256bd94 | ||
|
|
3944c42d4c | ||
|
|
cb06b7956c | ||
|
|
4454fbf7e5 | ||
|
|
b243840e4b | ||
|
|
23bbe7336a | ||
|
|
a71c4dfabb | ||
|
|
81275d12e9 | ||
|
|
40cad44f4a | ||
|
|
c38617fa27 | ||
|
|
1835d7bb45 | ||
|
|
f670f9b22c | ||
|
|
7a072cc2ea | ||
|
|
8c4b5d3c90 | ||
|
|
ec9d5cddd6 | ||
|
|
0f759af3cf | ||
|
|
644011fb14 | ||
|
|
a1ee6d28ce | ||
|
|
826868da5b | ||
|
|
5986ff748a | ||
|
|
739a8aa10e |
18
.github/workflows/ci.yaml
vendored
18
.github/workflows/ci.yaml
vendored
@@ -77,6 +77,8 @@ jobs:
|
||||
rustup component add clippy
|
||||
rustup target add wasm32-unknown-unknown
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: "ci"
|
||||
- name: "Clippy"
|
||||
run: cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||
- name: "Clippy (wasm)"
|
||||
@@ -97,6 +99,8 @@ jobs:
|
||||
with:
|
||||
tool: cargo-insta
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: "ci"
|
||||
- name: "Run tests (Ubuntu)"
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
run: cargo insta test --all --all-features --unreferenced reject
|
||||
@@ -146,6 +150,8 @@ jobs:
|
||||
cache-dependency-path: playground/package-lock.json
|
||||
- uses: jetli/wasm-pack-action@v0.4.0
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: "ci"
|
||||
- name: "Run wasm-pack"
|
||||
run: |
|
||||
cd crates/ruff_wasm
|
||||
@@ -159,6 +165,8 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup component add rustfmt
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: "ci"
|
||||
- run: ./scripts/add_rule.py --name DoTheThing --prefix PL --code C0999 --linter pylint
|
||||
- run: cargo check
|
||||
- run: cargo fmt --all --check
|
||||
@@ -227,6 +235,8 @@ jobs:
|
||||
# Only pinned to make caching work, update freely
|
||||
run: rustup toolchain install nightly-2023-06-08
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: "ci"
|
||||
- name: "Install cargo-udeps"
|
||||
uses: taiki-e/install-action@cargo-udeps
|
||||
- name: "Run cargo-udeps"
|
||||
@@ -242,6 +252,8 @@ jobs:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: "ci"
|
||||
- name: "Prep README.md"
|
||||
run: python scripts/transform_readme.py --target pypi
|
||||
- name: "Build wheels"
|
||||
@@ -267,6 +279,8 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: "ci"
|
||||
- name: "Install pre-commit"
|
||||
run: pip install pre-commit
|
||||
- name: "Cache pre-commit"
|
||||
@@ -300,6 +314,8 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: "ci"
|
||||
- name: "Install Insiders dependencies"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
run: pip install -r docs/requirements-insiders.txt
|
||||
@@ -352,6 +368,8 @@ jobs:
|
||||
tool: cargo-codspeed
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: "ci"
|
||||
|
||||
- name: "Build benchmarks"
|
||||
run: cargo codspeed build --features codspeed -p ruff_benchmark
|
||||
|
||||
2
.github/workflows/docs.yaml
vendored
2
.github/workflows/docs.yaml
vendored
@@ -47,7 +47,7 @@ jobs:
|
||||
run: mkdocs build --strict -f mkdocs.generated.yml
|
||||
- name: "Deploy to Cloudflare Pages"
|
||||
if: ${{ env.CF_API_TOKEN_EXISTS == 'true' }}
|
||||
uses: cloudflare/wrangler-action@v3.2.0
|
||||
uses: cloudflare/wrangler-action@v3.3.1
|
||||
with:
|
||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||
accountId: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
|
||||
2
.github/workflows/playground.yaml
vendored
2
.github/workflows/playground.yaml
vendored
@@ -40,7 +40,7 @@ jobs:
|
||||
working-directory: playground
|
||||
- name: "Deploy to Cloudflare Pages"
|
||||
if: ${{ env.CF_API_TOKEN_EXISTS == 'true' }}
|
||||
uses: cloudflare/wrangler-action@v3.2.0
|
||||
uses: cloudflare/wrangler-action@v3.3.1
|
||||
with:
|
||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||
accountId: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
|
||||
@@ -23,6 +23,7 @@ repos:
|
||||
- id: mdformat
|
||||
additional_dependencies:
|
||||
- mdformat-mkdocs
|
||||
- mdformat-admon
|
||||
|
||||
- repo: https://github.com/igorshubovych/markdownlint-cli
|
||||
rev: v0.33.0
|
||||
|
||||
@@ -1,5 +1,35 @@
|
||||
# Breaking Changes
|
||||
|
||||
## 0.1.0
|
||||
|
||||
### The deprecated `format` setting has been removed
|
||||
|
||||
Ruff previously used the `format` setting, `--format` CLI option, and `RUFF_FORMAT` environment variable to
|
||||
configure the output format of the CLI. This usage was deprecated in `v0.0.291` — the `format` setting is now used
|
||||
to control Ruff's code formatting. As of this release:
|
||||
|
||||
- The `format` setting cannot be used to configure the output format, use `output-format` instead
|
||||
- The `RUFF_FORMAT` environment variable is ignored, use `RUFF_OUTPUT_FORMAT` instead
|
||||
- The `--format` option has been removed from `ruff check`, use `--output-format` instead
|
||||
|
||||
### Unsafe fixes are not applied by default ([#7769](https://github.com/astral-sh/ruff/pull/7769))
|
||||
|
||||
Ruff labels fixes as "safe" and "unsafe". The meaning and intent of your code will be retained when applying safe
|
||||
fixes, but the meaning could be changed when applying unsafe fixes. Previously, unsafe fixes were always displayed
|
||||
and applied when fixing was enabled. Now, unsafe fixes are hidden by default and not applied. The `--unsafe-fixes`
|
||||
flag or `unsafe-fixes` configuration option can be used to enable unsafe fixes.
|
||||
|
||||
See the [docs](https://docs.astral.sh/ruff/configuration/#fix-safety) for details.
|
||||
|
||||
### Remove formatter-conflicting rules from the default rule set ([#7900](https://github.com/astral-sh/ruff/pull/7900))
|
||||
|
||||
Previously, Ruff enabled all implemented rules in Pycodestyle (`E`) by default. Ruff now only includes the
|
||||
Pycodestyle prefixes `E4`, `E7`, and `E9` to exclude rules that conflict with automatic formatters. Consequently,
|
||||
the stable rule set no longer includes `line-too-long` (`E501`) and `mixed-spaces-and-tabs` (`E101`). Other
|
||||
excluded Pycodestyle rules include whitespace enforcement in `E1` and `E2`; these rules are currently in preview, and are already omitted by default.
|
||||
|
||||
This change only affects those using Ruff under its default rule set. Users that include `E` in their `select` will experience no change in behavior.
|
||||
|
||||
## 0.0.288
|
||||
|
||||
### Remove support for emoji identifiers ([#7212](https://github.com/astral-sh/ruff/pull/7212))
|
||||
|
||||
115
CHANGELOG.md
Normal file
115
CHANGELOG.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# Changelog
|
||||
|
||||
This is the first release which uses the `CHANGELOG` file. See [GitHub Releases](https://github.com/astral-sh/ruff/releases) for prior changelog entries.
|
||||
|
||||
Read Ruff's new [versioning policy](https://docs.astral.sh/ruff/versioning/).
|
||||
|
||||
## 0.1.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
- Unsafe fixes are no longer displayed or applied without opt-in ([#7769](https://github.com/astral-sh/ruff/pull/7769))
|
||||
- Drop formatting specific rules from the default set ([#7900](https://github.com/astral-sh/ruff/pull/7900))
|
||||
- The deprecated `format` setting has been removed ([#7984](https://github.com/astral-sh/ruff/pull/7984))
|
||||
- The `format` setting cannot be used to configure the output format, use `output-format` instead
|
||||
- The `RUFF_FORMAT` environment variable is ignored, use `RUFF_OUTPUT_FORMAT` instead
|
||||
- The `--format` option has been removed from `ruff check`, use `--output-format` instead
|
||||
|
||||
### Rule changes
|
||||
|
||||
- Extend `reimplemented-starmap` (`FURB140`) to catch calls with a single and starred argument ([#7768](https://github.com/astral-sh/ruff/pull/7768))
|
||||
- Improve cases covered by `RUF015` ([#7848](https://github.com/astral-sh/ruff/pull/7848))
|
||||
- Update `SIM15` to allow `open` followed by `close` ([#7916](https://github.com/astral-sh/ruff/pull/7916))
|
||||
- Respect `msgspec.Struct` default-copy semantics in `RUF012` ([#7786](https://github.com/astral-sh/ruff/pull/7786))
|
||||
- Add `sqlalchemy` methods to \`flake8-boolean-trap\`\` exclusion list ([#7874](https://github.com/astral-sh/ruff/pull/7874))
|
||||
- Add fix for `PLR1714` ([#7910](https://github.com/astral-sh/ruff/pull/7910))
|
||||
- Add fix for `PIE804` ([#7884](https://github.com/astral-sh/ruff/pull/7884))
|
||||
- Add fix for `PLC0208` ([#7887](https://github.com/astral-sh/ruff/pull/7887))
|
||||
- Add fix for `PYI055` ([#7886](https://github.com/astral-sh/ruff/pull/7886))
|
||||
- Update `non-pep695-type-alias` to require `--unsafe-fixes` outside of stub files ([#7836](https://github.com/astral-sh/ruff/pull/7836))
|
||||
- Improve fix message for `UP018` ([#7913](https://github.com/astral-sh/ruff/pull/7913))
|
||||
- Update `PLW3201` to support `Enum` [sunder names](https://docs.python.org/3/library/enum.html#supported-sunder-names) ([#7987](https://github.com/astral-sh/ruff/pull/7987))
|
||||
|
||||
### Preview features
|
||||
|
||||
- Only show warnings for empty preview selectors when enabling rules ([#7842](https://github.com/astral-sh/ruff/pull/7842))
|
||||
- Add `unnecessary-key-check` to simplify `key in dct and dct[key]` to `dct.get(key)` ([#7895](https://github.com/astral-sh/ruff/pull/7895))
|
||||
- Add `assignment-in-assert` to prevent walrus expressions in assert statements ([#7856](https://github.com/astral-sh/ruff/pull/7856))
|
||||
- \[`refurb`\] Add `single-item-membership-test` (`FURB171`) ([#7815](https://github.com/astral-sh/ruff/pull/7815))
|
||||
- \[`pylint`\] Add `and-or-ternary` (`R1706`) ([#7811](https://github.com/astral-sh/ruff/pull/7811))
|
||||
|
||||
_New rules are added in [preview](https://docs.astral.sh/ruff/preview/)._
|
||||
|
||||
### Configuration
|
||||
|
||||
- Add `unsafe-fixes` setting ([#7769](https://github.com/astral-sh/ruff/pull/7769))
|
||||
- Add `extend-safe-fixes` and `extend-unsafe-fixes` for promoting and demoting fixes ([#7841](https://github.com/astral-sh/ruff/pull/7841))
|
||||
|
||||
### CLI
|
||||
|
||||
- Added `--unsafe-fixes` option for opt-in to display and apply unsafe fixes ([#7769](https://github.com/astral-sh/ruff/pull/7769))
|
||||
- Fix use of deprecated `--format` option in warning ([#7837](https://github.com/astral-sh/ruff/pull/7837))
|
||||
- Show changed files when running under `--check` ([#7788](https://github.com/astral-sh/ruff/pull/7788))
|
||||
- Write summary messages to stderr when fixing via stdin instead of omitting them ([#7838](https://github.com/astral-sh/ruff/pull/7838))
|
||||
- Update fix summary message in `check --diff` to include unsafe fix hints ([#7790](https://github.com/astral-sh/ruff/pull/7790))
|
||||
- Add notebook `cell` field to JSON output format ([#7664](https://github.com/astral-sh/ruff/pull/7664))
|
||||
- Rename applicability levels to `Safe`, `Unsafe`, and `Display` ([#7843](https://github.com/astral-sh/ruff/pull/7843))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Fix bug where f-strings were allowed in match pattern literal ([#7857](https://github.com/astral-sh/ruff/pull/7857))
|
||||
- Fix `SIM110` with a yield in the condition ([#7801](https://github.com/astral-sh/ruff/pull/7801))
|
||||
- Preserve trailing comments in `C414` fixes ([#7775](https://github.com/astral-sh/ruff/pull/7775))
|
||||
- Check sequence type before triggering `unnecessary-enumerate` `len` suggestion ([#7781](https://github.com/astral-sh/ruff/pull/7781))
|
||||
- Use correct start location for class/function clause header ([#7802](https://github.com/astral-sh/ruff/pull/7802))
|
||||
- Fix incorrect fixes for `SIM101` ([#7798](https://github.com/astral-sh/ruff/pull/7798))
|
||||
- Format comment before parameter default correctly ([#7870](https://github.com/astral-sh/ruff/pull/7870))
|
||||
- Fix `E251` false positive inside f-strings ([#7894](https://github.com/astral-sh/ruff/pull/7894))
|
||||
- Allow bindings to be created and referenced within annotations ([#7885](https://github.com/astral-sh/ruff/pull/7885))
|
||||
- Show per-cell diffs when analyzing notebooks over `stdin` ([#7789](https://github.com/astral-sh/ruff/pull/7789))
|
||||
- Avoid curly brace escape in f-string format spec ([#7780](https://github.com/astral-sh/ruff/pull/7780))
|
||||
- Fix lexing single-quoted f-string with multi-line format spec ([#7787](https://github.com/astral-sh/ruff/pull/7787))
|
||||
- Consider nursery rules to be in-preview for `ruff rule` ([#7812](https://github.com/astral-sh/ruff/pull/7812))
|
||||
- Report precise location for invalid conversion flag ([#7809](https://github.com/astral-sh/ruff/pull/7809))
|
||||
- Visit pattern match guard as a boolean test ([#7911](https://github.com/astral-sh/ruff/pull/7911))
|
||||
- Respect `--unfixable` in `ISC` rules ([#7917](https://github.com/astral-sh/ruff/pull/7917))
|
||||
- Fix edge case with `PIE804` ([#7922](https://github.com/astral-sh/ruff/pull/7922))
|
||||
- Show custom message in `PTH118` for `Path.joinpath` with starred arguments ([#7852](https://github.com/astral-sh/ruff/pull/7852))
|
||||
- Fix false negative in `outdated-version-block` when using greater than comparisons ([#7920](https://github.com/astral-sh/ruff/pull/7920))
|
||||
- Avoid converting f-strings within Django `gettext` calls ([#7898](https://github.com/astral-sh/ruff/pull/7898))
|
||||
- Fix false positive in `PLR6301` ([#7933](https://github.com/astral-sh/ruff/pull/7933))
|
||||
- Treat type aliases as typing-only expressions e.g. resolves false positive in `TCH004` ([#7968](https://github.com/astral-sh/ruff/pull/7968))
|
||||
- Resolve `cache-dir` relative to project root ([#7962](https://github.com/astral-sh/ruff/pull/7962))
|
||||
- Respect subscripted base classes in type-checking rules e.g. resolves false positive in `TCH003` ([#7954](https://github.com/astral-sh/ruff/pull/7954))
|
||||
- Fix JSON schema limit for `line-length` ([#7883](https://github.com/astral-sh/ruff/pull/7883))
|
||||
- Fix commented-out `coalesce` keyword ([#7876](https://github.com/astral-sh/ruff/pull/7876))
|
||||
|
||||
### Documentation
|
||||
|
||||
- Document `reimplemented-starmap` performance effects ([#7846](https://github.com/astral-sh/ruff/pull/7846))
|
||||
- Default to following the system dark/light mode ([#7888](https://github.com/astral-sh/ruff/pull/7888))
|
||||
- Add documentation for fixes ([#7901](https://github.com/astral-sh/ruff/pull/7901))
|
||||
- Fix typo in docs of `PLR6301` ([#7831](https://github.com/astral-sh/ruff/pull/7831))
|
||||
- Update `UP038` docs to note that it results in slower code ([#7872](https://github.com/astral-sh/ruff/pull/7872))
|
||||
- crlf -> cr-lf ([#7766](https://github.com/astral-sh/ruff/pull/7766))
|
||||
- Add an example of an unsafe fix ([#7924](https://github.com/astral-sh/ruff/pull/7924))
|
||||
- Fix documented examples for `unnecessary-subscript-reversal` ([#7774](https://github.com/astral-sh/ruff/pull/7774))
|
||||
- Correct error in tuple example in ruff formatter docs ([#7822](https://github.com/astral-sh/ruff/pull/7822))
|
||||
- Add versioning policy to documentation ([#7923](https://github.com/astral-sh/ruff/pull/7923))
|
||||
- Fix invalid code in `FURB177` example ([#7832](https://github.com/astral-sh/ruff/pull/7832))
|
||||
|
||||
### Formatter
|
||||
|
||||
- Less scary `ruff format` message ([#7867](https://github.com/astral-sh/ruff/pull/7867))
|
||||
- Remove spaces from import statements ([#7859](https://github.com/astral-sh/ruff/pull/7859))
|
||||
- Formatter quoting for f-strings with triple quotes ([#7826](https://github.com/astral-sh/ruff/pull/7826))
|
||||
- Update `ruff_python_formatter` generate.py comment ([#7850](https://github.com/astral-sh/ruff/pull/7850))
|
||||
- Document one-call chaining deviation ([#7767](https://github.com/astral-sh/ruff/pull/7767))
|
||||
- Allow f-string modifications in line-shrinking cases ([#7818](https://github.com/astral-sh/ruff/pull/7818))
|
||||
- Add trailing comment deviation to README ([#7827](https://github.com/astral-sh/ruff/pull/7827))
|
||||
- Add trailing zero between dot and exponential ([#7956](https://github.com/astral-sh/ruff/pull/7956))
|
||||
- Force parentheses for power operations in unary expressions ([#7955](https://github.com/astral-sh/ruff/pull/7955))
|
||||
|
||||
### Playground
|
||||
|
||||
- Fix playground `Quick Fix` action ([#7824](https://github.com/astral-sh/ruff/pull/7824))
|
||||
@@ -170,7 +170,8 @@ At a high level, the steps involved in adding a new lint rule are as follows:
|
||||
statements, like imports) or `analyze/expression.rs` (if your rule is based on analyzing
|
||||
expressions, like function calls).
|
||||
|
||||
1. Map the violation struct to a rule code in `crates/ruff_linter/src/codes.rs` (e.g., `B011`).
|
||||
1. Map the violation struct to a rule code in `crates/ruff_linter/src/codes.rs` (e.g., `B011`). New rules
|
||||
should be added in `RuleGroup::Preview`.
|
||||
|
||||
1. Add proper [testing](#rule-testing-fixtures-and-snapshots) for your rule.
|
||||
|
||||
|
||||
62
Cargo.lock
generated
62
Cargo.lock
generated
@@ -28,9 +28,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab"
|
||||
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -810,7 +810,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.292"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -1084,9 +1084,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "insta"
|
||||
version = "1.33.0"
|
||||
version = "1.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aa511b2e298cd49b1856746f6bb73e17036bcd66b25f5e92cdcdbec9bd75686"
|
||||
checksum = "5d64600be34b2fcfc267740a243fa7744441bb4947a619ac4e5bb6507f35fbfc"
|
||||
dependencies = [
|
||||
"console",
|
||||
"globset",
|
||||
@@ -1925,14 +1925,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.9.6"
|
||||
version = "1.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff"
|
||||
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata 0.3.9",
|
||||
"regex-syntax 0.7.5",
|
||||
"regex-automata 0.4.3",
|
||||
"regex-syntax 0.8.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1949,10 +1949,16 @@ name = "regex-automata"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9"
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax 0.7.5",
|
||||
"regex-syntax 0.8.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1967,6 +1973,12 @@ version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
|
||||
[[package]]
|
||||
name = "result-like"
|
||||
version = "0.4.6"
|
||||
@@ -2039,7 +2051,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_cli"
|
||||
version = "0.0.292"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
@@ -2143,6 +2155,7 @@ name = "ruff_diagnostics"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"is-macro",
|
||||
"log",
|
||||
"ruff_text_size",
|
||||
"serde",
|
||||
@@ -2175,7 +2188,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_linter"
|
||||
version = "0.0.292"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"annotate-snippets 0.9.1",
|
||||
@@ -2645,9 +2658,9 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.19"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0"
|
||||
checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
@@ -3091,11 +3104,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.37"
|
||||
version = "0.1.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
|
||||
checksum = "ee2ef2af84856a50c1d430afce2fdded0a4ec7eda868db86409b4543df0797f9"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
@@ -3104,9 +3116,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.26"
|
||||
version = "0.1.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
|
||||
checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3115,9 +3127,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.31"
|
||||
version = "0.1.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
|
||||
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
@@ -3247,9 +3259,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode_names2"
|
||||
version = "1.1.0"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38b2c0942619ae1797f999a0ce7efc6c09592ad30e68e16cdbfdcd48a98c3579"
|
||||
checksum = "5d5506ae2c3c1ccbdf468e52fc5ef536c2ccd981f01273a4cb81aa61021f3a5f"
|
||||
dependencies = [
|
||||
"phf",
|
||||
"unicode_names2_generator",
|
||||
@@ -3257,9 +3269,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode_names2_generator"
|
||||
version = "1.1.0"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d0d66ab60be9799a70f8eb227ea43da7dcc47561dd9102cbadacfe0930113f7"
|
||||
checksum = "b6dfc680313e95bc6637fa278cd7a22390c3c2cd7b8b2bd28755bc6c0fc811e7"
|
||||
dependencies = [
|
||||
"getopts",
|
||||
"log",
|
||||
|
||||
@@ -21,7 +21,7 @@ filetime = { version = "0.2.20" }
|
||||
glob = { version = "0.3.1" }
|
||||
globset = { version = "0.4.10" }
|
||||
ignore = { version = "0.4.20" }
|
||||
insta = { version = "1.33.0", feature = ["filters", "glob"] }
|
||||
insta = { version = "1.34.0", feature = ["filters", "glob"] }
|
||||
is-macro = { version = "0.3.0" }
|
||||
itertools = { version = "0.11.0" }
|
||||
libcst = { version = "1.1.0", default-features = false }
|
||||
@@ -31,7 +31,7 @@ once_cell = { version = "1.17.1" }
|
||||
path-absolutize = { version = "3.1.1" }
|
||||
proc-macro2 = { version = "1.0.69" }
|
||||
quote = { version = "1.0.23" }
|
||||
regex = { version = "1.9.6" }
|
||||
regex = { version = "1.10.2" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
schemars = { version = "0.8.15" }
|
||||
serde = { version = "1.0.152", features = ["derive"] }
|
||||
@@ -46,11 +46,11 @@ syn = { version = "2.0.38" }
|
||||
test-case = { version = "3.2.1" }
|
||||
thiserror = { version = "1.0.49" }
|
||||
toml = { version = "0.7.8" }
|
||||
tracing = { version = "0.1.37" }
|
||||
tracing = { version = "0.1.39" }
|
||||
tracing-indicatif = { version = "0.3.4" }
|
||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
||||
unicode-ident = { version = "1.0.12" }
|
||||
unicode_names2 = { version = "1.1.0" }
|
||||
unicode_names2 = { version = "1.2.0" }
|
||||
unicode-width = { version = "0.1.11" }
|
||||
uuid = { version = "1.4.1", features = ["v4", "fast-rng", "macro-diagnostics", "js"] }
|
||||
wsl = { version = "0.1.0" }
|
||||
|
||||
@@ -140,7 +140,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com) hook:
|
||||
```yaml
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.0.292
|
||||
rev: v0.1.0
|
||||
hooks:
|
||||
- id: ruff
|
||||
```
|
||||
@@ -172,8 +172,8 @@ If left unspecified, the default configuration is equivalent to:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# Enable pycodestyle (`E`) and Pyflakes (`F`) codes by default.
|
||||
select = ["E", "F"]
|
||||
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
|
||||
select = ["E4", "E7", "E9", "F"]
|
||||
ignore = []
|
||||
|
||||
# Allow fix for all enabled rules (when `--fix`) is provided.
|
||||
@@ -239,7 +239,7 @@ Rust as a first-party feature.
|
||||
|
||||
By default, Ruff enables Flake8's `E` and `F` rules. Ruff supports all rules from the `F` category,
|
||||
and a [subset](https://docs.astral.sh/ruff/rules/#error-e) of the `E` category, omitting those
|
||||
stylistic rules made obsolete by the use of an autoformatter, like
|
||||
stylistic rules made obsolete by the use of a formatter, like
|
||||
[Black](https://github.com/psf/black).
|
||||
|
||||
If you're just getting started with Ruff, **the default rule set is a great place to start**: it
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.292"
|
||||
version = "0.1.0"
|
||||
description = """
|
||||
Convert Flake8 configuration files to Ruff configuration files.
|
||||
"""
|
||||
|
||||
@@ -13,6 +13,7 @@ use ruff_linter::rules::flake8_quotes::settings::Quote;
|
||||
use ruff_linter::rules::flake8_tidy_imports::settings::Strictness;
|
||||
use ruff_linter::rules::pydocstyle::settings::Convention;
|
||||
use ruff_linter::settings::types::PythonVersion;
|
||||
use ruff_linter::settings::DEFAULT_SELECTORS;
|
||||
use ruff_linter::warn_user;
|
||||
use ruff_workspace::options::{
|
||||
Flake8AnnotationsOptions, Flake8BugbearOptions, Flake8BuiltinsOptions, Flake8ErrMsgOptions,
|
||||
@@ -25,11 +26,6 @@ use super::external_config::ExternalConfig;
|
||||
use super::plugin::Plugin;
|
||||
use super::{parser, plugin};
|
||||
|
||||
const DEFAULT_SELECTORS: &[RuleSelector] = &[
|
||||
RuleSelector::Linter(Linter::Pyflakes),
|
||||
RuleSelector::Linter(Linter::Pycodestyle),
|
||||
];
|
||||
|
||||
pub(crate) fn convert(
|
||||
config: &HashMap<String, HashMap<String, Option<String>>>,
|
||||
external_config: &ExternalConfig,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_cli"
|
||||
version = "0.0.292"
|
||||
version = "0.1.0"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
|
||||
@@ -117,16 +117,6 @@ pub struct CheckCommand {
|
||||
#[arg(long)]
|
||||
ignore_noqa: bool,
|
||||
|
||||
/// Output serialization format for violations. (Deprecated: Use `--output-format` instead).
|
||||
#[arg(
|
||||
long,
|
||||
value_enum,
|
||||
env = "RUFF_FORMAT",
|
||||
conflicts_with = "output_format",
|
||||
hide = true
|
||||
)]
|
||||
pub format: Option<SerializationFormat>,
|
||||
|
||||
/// Output serialization format for violations.
|
||||
#[arg(long, value_enum, env = "RUFF_OUTPUT_FORMAT")]
|
||||
pub output_format: Option<SerializationFormat>,
|
||||
@@ -507,7 +497,7 @@ impl CheckCommand {
|
||||
unsafe_fixes: resolve_bool_arg(self.unsafe_fixes, self.no_unsafe_fixes)
|
||||
.map(UnsafeFixes::from),
|
||||
force_exclude: resolve_bool_arg(self.force_exclude, self.no_force_exclude),
|
||||
output_format: self.output_format.or(self.format),
|
||||
output_format: self.output_format,
|
||||
show_fixes: resolve_bool_arg(self.show_fixes, self.no_show_fixes),
|
||||
},
|
||||
)
|
||||
|
||||
@@ -6,6 +6,7 @@ use std::sync::mpsc::channel;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::CommandFactory;
|
||||
use colored::Colorize;
|
||||
use log::warn;
|
||||
use notify::{recommended_watcher, RecursiveMode, Watcher};
|
||||
|
||||
@@ -104,8 +105,6 @@ pub fn run(
|
||||
}: Args,
|
||||
) -> Result<ExitStatus> {
|
||||
{
|
||||
use colored::Colorize;
|
||||
|
||||
let default_panic_hook = std::panic::take_hook();
|
||||
std::panic::set_hook(Box::new(move |info| {
|
||||
#[allow(clippy::print_stderr)]
|
||||
@@ -166,10 +165,7 @@ pub fn run(
|
||||
}
|
||||
|
||||
fn format(args: FormatCommand, log_level: LogLevel) -> Result<ExitStatus> {
|
||||
warn_user_once!(
|
||||
"`ruff format` is a work-in-progress, subject to change at any time, and intended only for \
|
||||
experimentation."
|
||||
);
|
||||
warn_user_once!("`ruff format` is not yet stable, and subject to change in future versions.");
|
||||
|
||||
let (cli, overrides) = args.partition();
|
||||
|
||||
@@ -181,14 +177,6 @@ fn format(args: FormatCommand, log_level: LogLevel) -> Result<ExitStatus> {
|
||||
}
|
||||
|
||||
pub fn check(args: CheckCommand, log_level: LogLevel) -> Result<ExitStatus> {
|
||||
if args.format.is_some() {
|
||||
if std::env::var("RUFF_FORMAT").is_ok() {
|
||||
warn_user!("The environment variable `RUFF_FORMAT` is deprecated. Use `RUFF_OUTPUT_FORMAT` instead.");
|
||||
} else {
|
||||
warn_user!("The argument `--format=<FORMAT>` is deprecated. Use `--output-format=<FORMAT>` instead.");
|
||||
}
|
||||
}
|
||||
|
||||
let (cli, overrides) = args.partition();
|
||||
|
||||
// Construct the "default" settings. These are used when no `pyproject.toml`
|
||||
|
||||
@@ -128,17 +128,23 @@ impl Printer {
|
||||
let fix_prefix = format!("[{}]", "*".cyan());
|
||||
|
||||
if self.unsafe_fixes.is_enabled() {
|
||||
writeln!(
|
||||
writer,
|
||||
"{fix_prefix} {} fixable with the --fix option.",
|
||||
fixables.applicable
|
||||
)?;
|
||||
if fixables.applicable > 0 {
|
||||
writeln!(
|
||||
writer,
|
||||
"{fix_prefix} {} fixable with the --fix option.",
|
||||
fixables.applicable
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
if fixables.applicable > 0 && fixables.unapplicable > 0 {
|
||||
let es = if fixables.unapplicable == 1 { "" } else { "es" };
|
||||
if fixables.applicable > 0 && fixables.unapplicable_unsafe > 0 {
|
||||
let es = if fixables.unapplicable_unsafe == 1 {
|
||||
""
|
||||
} else {
|
||||
"es"
|
||||
};
|
||||
writeln!(writer,
|
||||
"{fix_prefix} {} fixable with the `--fix` option ({} hidden fix{es} can be enabled with the `--unsafe-fixes` option).",
|
||||
fixables.applicable, fixables.unapplicable
|
||||
fixables.applicable, fixables.unapplicable_unsafe
|
||||
)?;
|
||||
} else if fixables.applicable > 0 {
|
||||
// Only applicable fixes
|
||||
@@ -149,10 +155,14 @@ impl Printer {
|
||||
)?;
|
||||
} else {
|
||||
// Only unapplicable fixes
|
||||
let es = if fixables.unapplicable == 1 { "" } else { "es" };
|
||||
let es = if fixables.unapplicable_unsafe == 1 {
|
||||
""
|
||||
} else {
|
||||
"es"
|
||||
};
|
||||
writeln!(writer,
|
||||
"{} hidden fix{es} can be enabled with the `--unsafe-fixes` option.",
|
||||
fixables.unapplicable
|
||||
"No fixes available ({} hidden fix{es} can be enabled with the `--unsafe-fixes` option).",
|
||||
fixables.unapplicable_unsafe
|
||||
)?;
|
||||
}
|
||||
}
|
||||
@@ -161,7 +171,7 @@ impl Printer {
|
||||
// Check if there are unapplied fixes
|
||||
let unapplied = {
|
||||
if let Some(fixables) = fixables {
|
||||
fixables.unapplicable
|
||||
fixables.unapplicable_unsafe
|
||||
} else {
|
||||
0
|
||||
}
|
||||
@@ -497,30 +507,33 @@ fn print_fix_summary(writer: &mut dyn Write, fixed: &FxHashMap<String, FixTable>
|
||||
#[derive(Debug)]
|
||||
struct FixableStatistics {
|
||||
applicable: u32,
|
||||
unapplicable: u32,
|
||||
unapplicable_unsafe: u32,
|
||||
}
|
||||
|
||||
impl FixableStatistics {
|
||||
fn try_from(diagnostics: &Diagnostics, unsafe_fixes: UnsafeFixes) -> Option<Self> {
|
||||
let mut applicable = 0;
|
||||
let mut unapplicable = 0;
|
||||
let mut unapplicable_unsafe = 0;
|
||||
|
||||
for message in &diagnostics.messages {
|
||||
if let Some(fix) = &message.fix {
|
||||
if fix.applies(unsafe_fixes.required_applicability()) {
|
||||
applicable += 1;
|
||||
} else {
|
||||
unapplicable += 1;
|
||||
// Do not include unapplicable fixes at other levels that do not provide an opt-in
|
||||
if fix.applicability().is_unsafe() {
|
||||
unapplicable_unsafe += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if applicable == 0 && unapplicable == 0 {
|
||||
if applicable == 0 && unapplicable_unsafe == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(Self {
|
||||
applicable,
|
||||
unapplicable,
|
||||
unapplicable_unsafe,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ if condition:
|
||||
print('Hy "Micha"') # Should not change quotes
|
||||
|
||||
----- stderr -----
|
||||
warning: `ruff format` is a work-in-progress, subject to change at any time, and intended only for experimentation.
|
||||
warning: `ruff format` is not yet stable, and subject to change in future versions.
|
||||
"###);
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ if condition:
|
||||
print('Should change quotes')
|
||||
|
||||
----- stderr -----
|
||||
warning: `ruff format` is a work-in-progress, subject to change at any time, and intended only for experimentation.
|
||||
warning: `ruff format` is not yet stable, and subject to change in future versions.
|
||||
"###);
|
||||
Ok(())
|
||||
}
|
||||
@@ -139,12 +139,12 @@ if condition:
|
||||
print('Should change quotes')
|
||||
|
||||
----- stderr -----
|
||||
warning: `ruff format` is a work-in-progress, subject to change at any time, and intended only for experimentation.
|
||||
warning: `ruff format` is not yet stable, and subject to change in future versions.
|
||||
"###);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tests that the legacy `format` option continues to work but emits a warning.
|
||||
/// Since 0.1.0 the legacy format option is no longer supported
|
||||
#[test]
|
||||
fn legacy_format_option() -> Result<()> {
|
||||
let tempdir = TempDir::new()?;
|
||||
@@ -156,52 +156,29 @@ format = "json"
|
||||
"#,
|
||||
)?;
|
||||
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(["check", "--select", "F401", "--no-cache", "--config"])
|
||||
.arg(&ruff_toml)
|
||||
.arg("-")
|
||||
.pass_stdin(r#"
|
||||
import os
|
||||
"#), @r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
[
|
||||
{
|
||||
"code": "F401",
|
||||
"end_location": {
|
||||
"column": 10,
|
||||
"row": 2
|
||||
},
|
||||
"filename": "-",
|
||||
"fix": {
|
||||
"applicability": "safe",
|
||||
"edits": [
|
||||
{
|
||||
"content": "",
|
||||
"end_location": {
|
||||
"column": 1,
|
||||
"row": 3
|
||||
},
|
||||
"location": {
|
||||
"column": 1,
|
||||
"row": 2
|
||||
}
|
||||
}
|
||||
],
|
||||
"message": "Remove unused import: `os`"
|
||||
},
|
||||
"location": {
|
||||
"column": 8,
|
||||
"row": 2
|
||||
},
|
||||
"message": "`os` imported but unused",
|
||||
"noqa_row": 2,
|
||||
"url": "https://docs.astral.sh/ruff/rules/unused-import"
|
||||
}
|
||||
]
|
||||
----- stderr -----
|
||||
warning: The option `format` has been deprecated to avoid ambiguity with Ruff's upcoming formatter. Use `output-format` instead.
|
||||
"###);
|
||||
insta::with_settings!({filters => vec![
|
||||
(&*regex::escape(ruff_toml.to_str().unwrap()), "[RUFF-TOML-PATH]"),
|
||||
]}, {
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(["check", "--select", "F401", "--no-cache", "--config"])
|
||||
.arg(&ruff_toml)
|
||||
.arg("-")
|
||||
.pass_stdin(r#"
|
||||
import os
|
||||
"#), @r###"
|
||||
success: false
|
||||
exit_code: 2
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
ruff failed
|
||||
Cause: Failed to parse `[RUFF-TOML-PATH]`: TOML parse error at line 2, column 10
|
||||
|
|
||||
2 | format = "json"
|
||||
| ^^^^^^
|
||||
invalid type: string "json", expected struct FormatOptions
|
||||
|
||||
"###);
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#![cfg(not(target_family = "wasm"))]
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::fs;
|
||||
#[cfg(unix)]
|
||||
use std::fs::Permissions;
|
||||
@@ -12,13 +11,13 @@ use std::process::Command;
|
||||
use std::str;
|
||||
|
||||
#[cfg(unix)]
|
||||
use anyhow::{Context, Result};
|
||||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
#[cfg(unix)]
|
||||
use clap::Parser;
|
||||
use insta_cmd::{assert_cmd_snapshot, get_cargo_bin};
|
||||
#[cfg(unix)]
|
||||
use path_absolutize::path_dedot;
|
||||
#[cfg(unix)]
|
||||
use tempfile::TempDir;
|
||||
|
||||
#[cfg(unix)]
|
||||
@@ -486,7 +485,7 @@ fn stdin_format_jupyter() {
|
||||
}
|
||||
|
||||
----- stderr -----
|
||||
warning: `ruff format` is a work-in-progress, subject to change at any time, and intended only for experimentation.
|
||||
warning: `ruff format` is not yet stable, and subject to change in future versions.
|
||||
"###);
|
||||
}
|
||||
|
||||
@@ -946,7 +945,7 @@ fn check_hints_hidden_unsafe_fixes_with_no_safe_fixes() {
|
||||
----- stdout -----
|
||||
-:1:14: F601 Dictionary key literal `'a'` repeated
|
||||
Found 1 error.
|
||||
1 hidden fix can be enabled with the `--unsafe-fixes` option.
|
||||
No fixes available (1 hidden fix can be enabled with the `--unsafe-fixes` option).
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
@@ -987,7 +986,7 @@ fn fix_applies_safe_fixes_by_default() {
|
||||
"--output-format",
|
||||
"text",
|
||||
"--isolated",
|
||||
"--no-cache",
|
||||
"--no-cache",
|
||||
"--select",
|
||||
"F601,UP034",
|
||||
"--fix",
|
||||
@@ -1003,7 +1002,7 @@ fn fix_applies_safe_fixes_by_default() {
|
||||
----- stderr -----
|
||||
-:1:14: F601 Dictionary key literal `'a'` repeated
|
||||
Found 2 errors (1 fixed, 1 remaining).
|
||||
1 hidden fix can be enabled with the `--unsafe-fixes` option.
|
||||
No fixes available (1 hidden fix can be enabled with the `--unsafe-fixes` option).
|
||||
"###);
|
||||
}
|
||||
|
||||
@@ -1016,7 +1015,7 @@ fn fix_applies_unsafe_fixes_with_opt_in() {
|
||||
"--output-format",
|
||||
"text",
|
||||
"--isolated",
|
||||
"--no-cache",
|
||||
"--no-cache",
|
||||
"--select",
|
||||
"F601,UP034",
|
||||
"--fix",
|
||||
@@ -1035,6 +1034,59 @@ fn fix_applies_unsafe_fixes_with_opt_in() {
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fix_does_not_apply_display_only_fixes() {
|
||||
assert_cmd_snapshot!(
|
||||
Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args([
|
||||
"-",
|
||||
"--output-format",
|
||||
"text",
|
||||
"--isolated",
|
||||
"--no-cache",
|
||||
"--select",
|
||||
"B006",
|
||||
"--fix",
|
||||
])
|
||||
.pass_stdin("def add_to_list(item, some_list=[]): ..."),
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
def add_to_list(item, some_list=[]): ...
|
||||
----- stderr -----
|
||||
-:1:33: B006 Do not use mutable data structures for argument defaults
|
||||
Found 1 error.
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fix_does_not_apply_display_only_fixes_with_unsafe_fixes_enabled() {
|
||||
assert_cmd_snapshot!(
|
||||
Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args([
|
||||
"-",
|
||||
"--output-format",
|
||||
"text",
|
||||
"--isolated",
|
||||
"--no-cache",
|
||||
"--select",
|
||||
"B006",
|
||||
"--fix",
|
||||
"--unsafe-fixes",
|
||||
])
|
||||
.pass_stdin("def add_to_list(item, some_list=[]): ..."),
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
def add_to_list(item, some_list=[]): ...
|
||||
----- stderr -----
|
||||
-:1:33: B006 Do not use mutable data structures for argument defaults
|
||||
Found 1 error.
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fix_only_unsafe_fixes_available() {
|
||||
assert_cmd_snapshot!(
|
||||
@@ -1044,7 +1096,7 @@ fn fix_only_unsafe_fixes_available() {
|
||||
"--output-format",
|
||||
"text",
|
||||
"--isolated",
|
||||
"--no-cache",
|
||||
"--no-cache",
|
||||
"--select",
|
||||
"F601",
|
||||
"--fix",
|
||||
@@ -1060,7 +1112,7 @@ fn fix_only_unsafe_fixes_available() {
|
||||
----- stderr -----
|
||||
-:1:14: F601 Dictionary key literal `'a'` repeated
|
||||
Found 1 error.
|
||||
1 hidden fix can be enabled with the `--unsafe-fixes` option.
|
||||
No fixes available (1 hidden fix can be enabled with the `--unsafe-fixes` option).
|
||||
"###);
|
||||
}
|
||||
|
||||
@@ -1073,7 +1125,7 @@ fn fix_only_flag_applies_safe_fixes_by_default() {
|
||||
"--output-format",
|
||||
"text",
|
||||
"--isolated",
|
||||
"--no-cache",
|
||||
"--no-cache",
|
||||
"--select",
|
||||
"F601,UP034",
|
||||
"--fix-only",
|
||||
@@ -1100,7 +1152,7 @@ fn fix_only_flag_applies_unsafe_fixes_with_opt_in() {
|
||||
"--output-format",
|
||||
"text",
|
||||
"--isolated",
|
||||
"--no-cache",
|
||||
"--no-cache",
|
||||
"--select",
|
||||
"F601,UP034",
|
||||
"--fix-only",
|
||||
@@ -1183,6 +1235,31 @@ fn diff_shows_unsafe_fixes_with_opt_in() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_does_not_show_display_only_fixes_with_unsafe_fixes_enabled() {
|
||||
assert_cmd_snapshot!(
|
||||
Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args([
|
||||
"-",
|
||||
"--output-format",
|
||||
"text",
|
||||
"--isolated",
|
||||
"--no-cache",
|
||||
"--select",
|
||||
"B006",
|
||||
"--diff",
|
||||
"--unsafe-fixes",
|
||||
])
|
||||
.pass_stdin("def add_to_list(item, some_list=[]): ..."),
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_only_unsafe_fixes_available() {
|
||||
assert_cmd_snapshot!(
|
||||
@@ -1208,3 +1285,122 @@ fn diff_only_unsafe_fixes_available() {
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_extend_unsafe_fixes() -> Result<()> {
|
||||
let tempdir = TempDir::new()?;
|
||||
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||
fs::write(
|
||||
&ruff_toml,
|
||||
r#"
|
||||
[lint]
|
||||
extend-unsafe-fixes = ["UP034"]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(["check", "--config"])
|
||||
.arg(&ruff_toml)
|
||||
.arg("-")
|
||||
.args([
|
||||
"--output-format",
|
||||
"text",
|
||||
"--no-cache",
|
||||
"--select",
|
||||
"F601,UP034",
|
||||
])
|
||||
.pass_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n"),
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:14: F601 Dictionary key literal `'a'` repeated
|
||||
-:2:7: UP034 Avoid extraneous parentheses
|
||||
Found 2 errors.
|
||||
No fixes available (2 hidden fixes can be enabled with the `--unsafe-fixes` option).
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_extend_safe_fixes() -> Result<()> {
|
||||
let tempdir = TempDir::new()?;
|
||||
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||
fs::write(
|
||||
&ruff_toml,
|
||||
r#"
|
||||
[lint]
|
||||
extend-safe-fixes = ["F601"]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(["check", "--config"])
|
||||
.arg(&ruff_toml)
|
||||
.arg("-")
|
||||
.args([
|
||||
"--output-format",
|
||||
"text",
|
||||
"--no-cache",
|
||||
"--select",
|
||||
"F601,UP034",
|
||||
])
|
||||
.pass_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n"),
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:14: F601 [*] Dictionary key literal `'a'` repeated
|
||||
-:2:7: UP034 [*] Avoid extraneous parentheses
|
||||
Found 2 errors.
|
||||
[*] 2 fixable with the `--fix` option.
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_extend_unsafe_fixes_conflict_with_extend_safe_fixes() -> Result<()> {
|
||||
// Adding a rule to both options should result in it being treated as unsafe
|
||||
let tempdir = TempDir::new()?;
|
||||
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||
fs::write(
|
||||
&ruff_toml,
|
||||
r#"
|
||||
[lint]
|
||||
extend-unsafe-fixes = ["UP034"]
|
||||
extend-safe-fixes = ["UP034"]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.args(["check", "--config"])
|
||||
.arg(&ruff_toml)
|
||||
.arg("-")
|
||||
.args([
|
||||
"--output-format",
|
||||
"text",
|
||||
"--no-cache",
|
||||
"--select",
|
||||
"F601,UP034",
|
||||
])
|
||||
.pass_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n"),
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:14: F601 Dictionary key literal `'a'` repeated
|
||||
-:2:7: UP034 Avoid extraneous parentheses
|
||||
Found 2 errors.
|
||||
No fixes available (2 hidden fixes can be enabled with the `--unsafe-fixes` option).
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ exit_code: 1
|
||||
----- stdout -----
|
||||
[
|
||||
{
|
||||
"cell": null,
|
||||
"code": "F401",
|
||||
"end_location": {
|
||||
"column": 10,
|
||||
|
||||
@@ -17,4 +17,5 @@ ruff_text_size = { path = "../ruff_text_size" }
|
||||
|
||||
anyhow = { workspace = true }
|
||||
log = { workspace = true }
|
||||
is-macro = { workspace = true }
|
||||
serde = { workspace = true, optional = true, features = [] }
|
||||
|
||||
@@ -6,7 +6,7 @@ use ruff_text_size::{Ranged, TextSize};
|
||||
use crate::edit::Edit;
|
||||
|
||||
/// Indicates if a fix can be applied.
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, is_macro::Is)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
|
||||
pub enum Applicability {
|
||||
@@ -138,4 +138,11 @@ impl Fix {
|
||||
pub fn applies(&self, applicability: Applicability) -> bool {
|
||||
self.applicability >= applicability
|
||||
}
|
||||
|
||||
/// Create a new [`Fix`] with the given [`Applicability`].
|
||||
#[must_use]
|
||||
pub fn with_applicability(mut self, applicability: Applicability) -> Self {
|
||||
self.applicability = applicability;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ impl<'a> Printer<'a> {
|
||||
|
||||
/// Prints the passed in element as well as all its content,
|
||||
/// starting at the specified indentation level
|
||||
#[tracing::instrument(name = "Printer::print", skip_all)]
|
||||
#[tracing::instrument(level = "debug", name = "Printer::print", skip_all)]
|
||||
pub fn print_with_indent(
|
||||
mut self,
|
||||
document: &'a Document,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_linter"
|
||||
version = "0.0.292"
|
||||
version = "0.1.0"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
@@ -29,7 +29,7 @@ ruff_python_parser = { path = "../ruff_python_parser" }
|
||||
ruff_source_file = { path = "../ruff_source_file", features = ["serde"] }
|
||||
ruff_text_size = { path = "../ruff_text_size" }
|
||||
|
||||
aho-corasick = { version = "1.1.1" }
|
||||
aho-corasick = { version = "1.1.2" }
|
||||
annotate-snippets = { version = "0.9.1", features = ["color"] }
|
||||
anyhow = { workspace = true }
|
||||
bitflags = { workspace = true }
|
||||
@@ -59,7 +59,7 @@ regex = { workspace = true }
|
||||
result-like = { version = "0.4.6" }
|
||||
rustc-hash = { workspace = true }
|
||||
schemars = { workspace = true, optional = true }
|
||||
semver = { version = "1.0.19" }
|
||||
semver = { version = "1.0.20" }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
similar = { workspace = true }
|
||||
|
||||
@@ -8,6 +8,8 @@ Foo.objects.create(**{"_id": some_id}) # PIE804
|
||||
|
||||
Foo.objects.create(**{**bar}) # PIE804
|
||||
|
||||
foo(**{})
|
||||
|
||||
|
||||
foo(**{**data, "foo": "buzz"})
|
||||
foo(**buzz)
|
||||
|
||||
@@ -28,4 +28,12 @@ item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
|
||||
|
||||
def func():
|
||||
# PYI055
|
||||
item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
|
||||
x: type[requests_mock.Mocker] | type[httpretty] | type[str] = requests_mock.Mocker
|
||||
y: Union[type[requests_mock.Mocker], type[httpretty], type[str]] = requests_mock.Mocker
|
||||
|
||||
|
||||
def func():
|
||||
from typing import Union as U
|
||||
|
||||
# PYI055
|
||||
x: Union[type[requests_mock.Mocker], type[httpretty], type[str]] = requests_mock.Mocker
|
||||
|
||||
@@ -21,4 +21,5 @@ item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
|
||||
|
||||
def func():
|
||||
# PYI055
|
||||
item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
|
||||
item: type[requests_mock.Mocker] | type[httpretty] | type[str] = requests_mock.Mocker
|
||||
item2: Union[type[requests_mock.Mocker], type[httpretty], type[str]] = requests_mock.Mocker
|
||||
|
||||
@@ -42,3 +42,7 @@ with contextlib.ExitStack():
|
||||
with contextlib.ExitStack() as exit_stack:
|
||||
exit_stack_ = exit_stack
|
||||
f = exit_stack_.enter_context(open("filename"))
|
||||
|
||||
# OK (quick one-liner to clear file contents)
|
||||
open("filename", "w").close()
|
||||
pathlib.Path("filename").open("w").close()
|
||||
|
||||
19
crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TCH004_15.py
vendored
Normal file
19
crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TCH004_15.py
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .foo import Record
|
||||
|
||||
type RecordOrThings = Record | int | str
|
||||
type RecordCallback[R: Record] = Callable[[R], None]
|
||||
|
||||
|
||||
def process_record[R: Record](record: R) -> None:
|
||||
...
|
||||
|
||||
|
||||
class RecordContainer[R: Record]:
|
||||
def add_record(self, record: R) -> None:
|
||||
...
|
||||
@@ -22,3 +22,10 @@ class C:
|
||||
|
||||
class D(C):
|
||||
x: UUID
|
||||
|
||||
|
||||
import collections
|
||||
|
||||
|
||||
class E(BaseModel[int]):
|
||||
x: collections.Awaitable
|
||||
|
||||
@@ -54,3 +54,8 @@ f"{a=}"
|
||||
f"{a:=1}"
|
||||
f"{foo(a=1)}"
|
||||
f"normal {f"{a=}"} normal"
|
||||
|
||||
# Okay as the `=` is used inside a f-string...
|
||||
print(f"{foo = }")
|
||||
# ...but then it creates false negatives for now
|
||||
print(f"{foo(a = 1)}")
|
||||
|
||||
10
crates/ruff_linter/resources/test/fixtures/pydocstyle/D300.py
vendored
Normal file
10
crates/ruff_linter/resources/test/fixtures/pydocstyle/D300.py
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
def with_backslash():
|
||||
"""Sum\\mary."""
|
||||
|
||||
|
||||
def ends_in_quote():
|
||||
'Sum\\mary."'
|
||||
|
||||
|
||||
def contains_quote():
|
||||
'Sum"\\mary.'
|
||||
73
crates/ruff_linter/resources/test/fixtures/pylint/and_or_ternary.py
vendored
Normal file
73
crates/ruff_linter/resources/test/fixtures/pylint/and_or_ternary.py
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
# OK
|
||||
|
||||
1<2 and 'b' and 'c'
|
||||
|
||||
1<2 or 'a' and 'b'
|
||||
|
||||
1<2 and 'a'
|
||||
|
||||
1<2 or 'a'
|
||||
|
||||
2>1
|
||||
|
||||
1<2 and 'a' or 'b' and 'c'
|
||||
|
||||
1<2 and 'a' or 'b' or 'c'
|
||||
|
||||
1<2 and 'a' or 'b' or 'c' or (lambda x: x+1)
|
||||
|
||||
1<2 and 'a' or 'b' or (lambda x: x+1) or 'c'
|
||||
|
||||
default = 'default'
|
||||
if (not isinstance(default, bool) and isinstance(default, int)) \
|
||||
or (isinstance(default, str) and default):
|
||||
pass
|
||||
|
||||
docid, token = None, None
|
||||
(docid is None and token is None) or (docid is not None and token is not None)
|
||||
|
||||
vendor, os_version = 'darwin', '14'
|
||||
vendor == "debian" and os_version in ["12"] or vendor == "ubuntu" and os_version in []
|
||||
|
||||
# Don't emit if the parent is an `if` statement.
|
||||
if (task_id in task_dict and task_dict[task_id] is not task) \
|
||||
or task_id in used_group_ids:
|
||||
pass
|
||||
|
||||
no_target, is_x64, target = True, False, 'target'
|
||||
if (no_target and not is_x64) or target == 'ARM_APPL_RUST_TARGET':
|
||||
pass
|
||||
|
||||
# Don't emit if the parent is a `bool_op` expression.
|
||||
isinstance(val, str) and ((len(val) == 7 and val[0] == "#") or val in enums.NamedColor)
|
||||
|
||||
# Errors
|
||||
|
||||
1<2 and 'a' or 'b'
|
||||
|
||||
(lambda x: x+1) and 'a' or 'b'
|
||||
|
||||
'a' and (lambda x: x+1) or 'orange'
|
||||
|
||||
val = '#0000FF'
|
||||
(len(val) == 7 and val[0] == "#") or val in {'green'}
|
||||
|
||||
marker = 'marker'
|
||||
isinstance(marker, dict) and 'field' in marker or marker in {}
|
||||
|
||||
def has_oranges(oranges, apples=None) -> bool:
|
||||
return apples and False or oranges
|
||||
|
||||
[x for x in l if a and b or c]
|
||||
|
||||
{x: y for x in l if a and b or c}
|
||||
|
||||
{x for x in l if a and b or c}
|
||||
|
||||
new_list = [
|
||||
x
|
||||
for sublist in all_lists
|
||||
if a and b or c
|
||||
for x in sublist
|
||||
if (isinstance(operator, list) and x in operator) or x != operator
|
||||
]
|
||||
@@ -63,6 +63,15 @@ class Apples:
|
||||
def __html__(self):
|
||||
pass
|
||||
|
||||
# Allow _missing_, used by enum.Enum.
|
||||
@classmethod
|
||||
def _missing_(cls, value):
|
||||
pass
|
||||
|
||||
# Allow anonymous functions.
|
||||
def _(self):
|
||||
pass
|
||||
|
||||
|
||||
def __foo_bar__(): # this is not checked by the [bad-dunder-name] rule
|
||||
...
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
# Errors
|
||||
|
||||
for item in {1}:
|
||||
print(f"I can count to {item}!")
|
||||
|
||||
for item in {"apples", "lemons", "water"}: # flags in-line set literals
|
||||
print(f"I like {item}.")
|
||||
|
||||
for item in {1,}:
|
||||
print(f"I can count to {item}!")
|
||||
|
||||
for item in {
|
||||
"apples", "lemons", "water"
|
||||
}: # flags in-line set literals
|
||||
print(f"I like {item}.")
|
||||
|
||||
numbers_list = [i for i in {1, 2, 3}] # flags sets in list comprehensions
|
||||
|
||||
numbers_set = {i for i in {1, 2, 3}} # flags sets in set comprehensions
|
||||
|
||||
10
crates/ruff_linter/resources/test/fixtures/pylint/literal_membership.py
vendored
Normal file
10
crates/ruff_linter/resources/test/fixtures/pylint/literal_membership.py
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# Errors
|
||||
1 in [1, 2, 3]
|
||||
1 in (1, 2, 3)
|
||||
1 in (
|
||||
1, 2, 3
|
||||
)
|
||||
|
||||
# OK
|
||||
fruits = ["cherry", "grapes"]
|
||||
"cherry" in fruits
|
||||
71
crates/ruff_linter/resources/test/fixtures/pylint/misplaced_bare_raise.py
vendored
Normal file
71
crates/ruff_linter/resources/test/fixtures/pylint/misplaced_bare_raise.py
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
# bare raise is an except block
|
||||
try:
|
||||
pass
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
try:
|
||||
pass
|
||||
except Exception:
|
||||
if True:
|
||||
raise
|
||||
|
||||
# bare raise is in a finally block which is in an except block
|
||||
try:
|
||||
raise Exception
|
||||
except Exception:
|
||||
try:
|
||||
raise Exception
|
||||
finally:
|
||||
raise
|
||||
|
||||
# bare raise is in an __exit__ method
|
||||
class ContextManager:
|
||||
def __enter__(self):
|
||||
return self
|
||||
def __exit__(self, *args):
|
||||
raise
|
||||
|
||||
try:
|
||||
raise # [misplaced-bare-raise]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def f():
|
||||
try:
|
||||
raise # [misplaced-bare-raise]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def g():
|
||||
raise # [misplaced-bare-raise]
|
||||
|
||||
def h():
|
||||
try:
|
||||
if True:
|
||||
def i():
|
||||
raise # [misplaced-bare-raise]
|
||||
except Exception:
|
||||
pass
|
||||
raise # [misplaced-bare-raise]
|
||||
|
||||
raise # [misplaced-bare-raise]
|
||||
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
def i():
|
||||
raise # [misplaced-bare-raise]
|
||||
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
class C:
|
||||
raise # [misplaced-bare-raise]
|
||||
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
pass
|
||||
finally:
|
||||
raise # [misplaced-bare-raise]
|
||||
@@ -1,5 +1,7 @@
|
||||
import abc
|
||||
|
||||
from typing_extensions import override
|
||||
|
||||
|
||||
class Person:
|
||||
def developer_greeting(self, name): # [no-self-use]
|
||||
@@ -60,3 +62,24 @@ class Prop:
|
||||
@property
|
||||
def count(self):
|
||||
return 24
|
||||
|
||||
|
||||
class A:
|
||||
def foo(self):
|
||||
...
|
||||
|
||||
|
||||
class B(A):
|
||||
@override
|
||||
def foo(self):
|
||||
...
|
||||
|
||||
def foobar(self):
|
||||
super()
|
||||
|
||||
def bar(self):
|
||||
super().foo()
|
||||
|
||||
def baz(self):
|
||||
if super().foo():
|
||||
...
|
||||
|
||||
54
crates/ruff_linter/resources/test/fixtures/pylint/too_many_boolean_expressions.py
vendored
Normal file
54
crates/ruff_linter/resources/test/fixtures/pylint/too_many_boolean_expressions.py
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
if a:
|
||||
...
|
||||
elif (a and b):
|
||||
...
|
||||
elif (a and b) and c:
|
||||
...
|
||||
elif (a and b) and c and d:
|
||||
...
|
||||
elif (a and b) and c and d and e:
|
||||
...
|
||||
elif (a and b) and c and d and e and f:
|
||||
...
|
||||
elif (a and b) and c and d and e and f and g:
|
||||
...
|
||||
elif (a and b) and c and d and e and f and g and h:
|
||||
...
|
||||
elif (a and b) and c and d and e and f and g and h and i:
|
||||
...
|
||||
elif (a and b) and c and d and e and f and g and h and i and j:
|
||||
...
|
||||
elif (a and b) and c and d and e and f and g and h and i and j and k:
|
||||
...
|
||||
elif (a and b) and c and d and e and f and g and h and i and j and k and l:
|
||||
...
|
||||
elif (a and b) and c and d and e and f and g and h and i and j and k and l and m:
|
||||
...
|
||||
elif (a and b) and c and d and e and f and g and h and i and j and k and l and m and n:
|
||||
...
|
||||
elif (a and b) and c and d and e and f and g and h and i and j and k and l and m and n and o:
|
||||
...
|
||||
elif (a and b) and c and d and e and f and g and h and i and j and k and l and m and n and o and p:
|
||||
...
|
||||
elif (a and b) and c and d and e and f and g and h and i and j and k and l and m and n and o and p and q:
|
||||
...
|
||||
elif (a and b) and c and d and e and f and g and h and i and j and k and l and m and n and o and p and q and r:
|
||||
...
|
||||
elif (a and b) and c and d and e and f and g and h and i and j and k and l and m and n and o and p and q and r and s:
|
||||
...
|
||||
elif (a and b) and c and d and e and f and g and h and i and j and k and l and m and n and o and p and q and r and s and t:
|
||||
...
|
||||
elif (a and b) and c and d and e and f and g and h and i and j and k and l and m and n and o and p and q and r and s and t and u:
|
||||
...
|
||||
elif (a and b) and c and d and e and f and g and h and i and j and k and l and m and n and o and p and q and r and s and t and u and v:
|
||||
...
|
||||
elif (a and b) and c and d and e and f and g and h and i and j and k and l and m and n and o and p and q and r and s and t and u and v and w:
|
||||
...
|
||||
elif (a and b) and c and d and e and f and g and h and i and j and k and l and m and n and o and p and q and r and s and t and u and v and w and x:
|
||||
...
|
||||
elif (a and b) and c and d and e and f and g and h and i and j and k and l and m and n and o and p and q and r and s and t and u and v and w and x and y:
|
||||
...
|
||||
elif (a and b) and c and d and e and f and g and h and i and j and k and l and m and n and o and p and q and r and s and t and u and v and w and x and y and z:
|
||||
...
|
||||
else:
|
||||
...
|
||||
44
crates/ruff_linter/resources/test/fixtures/pylint/unspecified_encoding.py
vendored
Normal file
44
crates/ruff_linter/resources/test/fixtures/pylint/unspecified_encoding.py
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
import io
|
||||
import sys
|
||||
import tempfile
|
||||
import io as hugo
|
||||
import codecs
|
||||
|
||||
# Errors.
|
||||
open("test.txt")
|
||||
io.TextIOWrapper(io.FileIO("test.txt"))
|
||||
hugo.TextIOWrapper(hugo.FileIO("test.txt"))
|
||||
tempfile.NamedTemporaryFile("w")
|
||||
tempfile.TemporaryFile("w")
|
||||
codecs.open("test.txt")
|
||||
tempfile.SpooledTemporaryFile(0, "w")
|
||||
|
||||
# Non-errors.
|
||||
open("test.txt", encoding="utf-8")
|
||||
open("test.bin", "wb")
|
||||
open("test.bin", mode="wb")
|
||||
open("test.txt", "r", -1, "utf-8")
|
||||
open("test.txt", mode=sys.argv[1])
|
||||
|
||||
def func(*args, **kwargs):
|
||||
open(*args)
|
||||
open("text.txt", **kwargs)
|
||||
|
||||
io.TextIOWrapper(io.FileIO("test.txt"), encoding="utf-8")
|
||||
io.TextIOWrapper(io.FileIO("test.txt"), "utf-8")
|
||||
tempfile.TemporaryFile("w", encoding="utf-8")
|
||||
tempfile.TemporaryFile("w", -1, "utf-8")
|
||||
tempfile.TemporaryFile("wb")
|
||||
tempfile.TemporaryFile()
|
||||
tempfile.NamedTemporaryFile("w", encoding="utf-8")
|
||||
tempfile.NamedTemporaryFile("w", -1, "utf-8")
|
||||
tempfile.NamedTemporaryFile("wb")
|
||||
tempfile.NamedTemporaryFile()
|
||||
codecs.open("test.txt", encoding="utf-8")
|
||||
codecs.open("test.bin", "wb")
|
||||
codecs.open("test.bin", mode="wb")
|
||||
codecs.open("test.txt", "r", -1, "utf-8")
|
||||
tempfile.SpooledTemporaryFile(0, "w", encoding="utf-8")
|
||||
tempfile.SpooledTemporaryFile(0, "w", -1, "utf-8")
|
||||
tempfile.SpooledTemporaryFile(0, "wb")
|
||||
tempfile.SpooledTemporaryFile(0, )
|
||||
@@ -197,8 +197,8 @@ if sys.version_info < (3,10000000):
|
||||
if sys.version_info <= (3,10000000):
|
||||
print("py3")
|
||||
|
||||
if sys.version_info > (3, 12):
|
||||
print('py3')
|
||||
|
||||
if sys.version_info >= (3, 12):
|
||||
print('py3')
|
||||
if sys.version_info > (3,12):
|
||||
print("py3")
|
||||
|
||||
if sys.version_info >= (3,12):
|
||||
print("py3")
|
||||
|
||||
20
crates/ruff_linter/resources/test/fixtures/ruff/RUF019.py
vendored
Normal file
20
crates/ruff_linter/resources/test/fixtures/ruff/RUF019.py
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
d = {}
|
||||
# RUF019
|
||||
if "k" in d and d["k"]:
|
||||
pass
|
||||
|
||||
k = "k"
|
||||
if k in d and d[k]:
|
||||
pass
|
||||
|
||||
if (k) in d and d[k]:
|
||||
pass
|
||||
|
||||
if k in d and d[(k)]:
|
||||
pass
|
||||
|
||||
# OK
|
||||
v = "k" in d and d["k"]
|
||||
|
||||
if f() in d and d[f()]:
|
||||
pass
|
||||
@@ -32,15 +32,10 @@ pub(crate) fn bindings(checker: &mut Checker) {
|
||||
},
|
||||
binding.range(),
|
||||
);
|
||||
if checker.patch(Rule::UnusedVariable) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
pyflakes::fixes::remove_exception_handler_assignment(
|
||||
binding,
|
||||
checker.locator,
|
||||
)
|
||||
diagnostic.try_set_fix(|| {
|
||||
pyflakes::fixes::remove_exception_handler_assignment(binding, checker.locator)
|
||||
.map(Fix::safe_edit)
|
||||
});
|
||||
}
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,7 +306,7 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
|
||||
|
||||
if scope.kind.is_function() {
|
||||
if checker.enabled(Rule::NoSelfUse) {
|
||||
pylint::rules::no_self_use(checker, scope, &mut diagnostics);
|
||||
pylint::rules::no_self_use(checker, scope_id, scope, &mut diagnostics);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -786,6 +786,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::SubprocessRunWithoutCheck) {
|
||||
pylint::rules::subprocess_run_without_check(checker, call);
|
||||
}
|
||||
if checker.enabled(Rule::UnspecifiedEncoding) {
|
||||
pylint::rules::unspecified_encoding(checker, call);
|
||||
}
|
||||
if checker.any_enabled(&[
|
||||
Rule::PytestRaisesWithoutException,
|
||||
Rule::PytestRaisesTooBroad,
|
||||
@@ -1194,6 +1197,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::ComparisonWithItself) {
|
||||
pylint::rules::comparison_with_itself(checker, left, ops, comparators);
|
||||
}
|
||||
if checker.enabled(Rule::LiteralMembership) {
|
||||
pylint::rules::literal_membership(checker, compare);
|
||||
}
|
||||
if checker.enabled(Rule::ComparisonOfConstant) {
|
||||
pylint::rules::comparison_of_constant(checker, left, ops, comparators);
|
||||
}
|
||||
@@ -1412,6 +1418,12 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::RepeatedEqualityComparison) {
|
||||
pylint::rules::repeated_equality_comparison(checker, bool_op);
|
||||
}
|
||||
if checker.enabled(Rule::AndOrTernary) {
|
||||
pylint::rules::and_or_ternary(checker, bool_op);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryKeyCheck) {
|
||||
ruff::rules::unnecessary_key_check(checker, expr);
|
||||
}
|
||||
}
|
||||
Expr::NamedExpr(..) => {
|
||||
if checker.enabled(Rule::AssignmentInAssert) {
|
||||
|
||||
@@ -964,7 +964,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
}
|
||||
Stmt::Raise(ast::StmtRaise { exc, .. }) => {
|
||||
Stmt::Raise(raise @ ast::StmtRaise { exc, .. }) => {
|
||||
if checker.enabled(Rule::RaiseNotImplemented) {
|
||||
if let Some(expr) = exc {
|
||||
pyflakes::rules::raise_not_implemented(checker, expr);
|
||||
@@ -1004,6 +1004,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
flake8_raise::rules::unnecessary_paren_on_raise_exception(checker, expr);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::MisplacedBareRaise) {
|
||||
pylint::rules::misplaced_bare_raise(checker, raise);
|
||||
}
|
||||
}
|
||||
Stmt::AugAssign(ast::StmtAugAssign { target, .. }) => {
|
||||
if checker.enabled(Rule::GlobalStatement) {
|
||||
@@ -1067,6 +1070,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::CheckAndRemoveFromSet) {
|
||||
refurb::rules::check_and_remove_from_set(checker, if_);
|
||||
}
|
||||
if checker.enabled(Rule::TooManyBooleanExpressions) {
|
||||
pylint::rules::too_many_boolean_expressions(checker, if_);
|
||||
}
|
||||
if checker.source_type.is_stub() {
|
||||
if checker.any_enabled(&[
|
||||
Rule::UnrecognizedVersionInfoCheck,
|
||||
|
||||
@@ -143,11 +143,6 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Checker<'a> {
|
||||
/// Return `true` if a patch should be generated for a given [`Rule`].
|
||||
pub(crate) fn patch(&self, code: Rule) -> bool {
|
||||
self.settings.rules.should_fix(code)
|
||||
}
|
||||
|
||||
/// Return `true` if a [`Rule`] is disabled by a `noqa` directive.
|
||||
pub(crate) fn rule_is_ignored(&self, code: Rule, offset: TextSize) -> bool {
|
||||
// TODO(charlie): `noqa` directives are mostly enforced in `check_lines.rs`.
|
||||
@@ -529,6 +524,7 @@ where
|
||||
);
|
||||
self.semantic.push_definition(definition);
|
||||
self.semantic.push_scope(ScopeKind::Function(function_def));
|
||||
self.semantic.flags -= SemanticModelFlags::EXCEPTION_HANDLER;
|
||||
|
||||
self.deferred.functions.push(self.semantic.snapshot());
|
||||
|
||||
@@ -567,6 +563,7 @@ where
|
||||
);
|
||||
self.semantic.push_definition(definition);
|
||||
self.semantic.push_scope(ScopeKind::Class(class_def));
|
||||
self.semantic.flags -= SemanticModelFlags::EXCEPTION_HANDLER;
|
||||
|
||||
// Extract any global bindings from the class body.
|
||||
if let Some(globals) = Globals::from_body(body) {
|
||||
@@ -585,7 +582,9 @@ where
|
||||
if let Some(type_params) = type_params {
|
||||
self.visit_type_params(type_params);
|
||||
}
|
||||
self.visit_expr(value);
|
||||
// The value in a `type` alias has annotation semantics, in that it's never
|
||||
// evaluated at runtime.
|
||||
self.visit_annotation(value);
|
||||
self.semantic.pop_scope();
|
||||
self.visit_expr(name);
|
||||
}
|
||||
@@ -1367,7 +1366,7 @@ where
|
||||
fn visit_match_case(&mut self, match_case: &'b MatchCase) {
|
||||
self.visit_pattern(&match_case.pattern);
|
||||
if let Some(expr) = &match_case.guard {
|
||||
self.visit_expr(expr);
|
||||
self.visit_boolean_test(expr);
|
||||
}
|
||||
|
||||
self.semantic.push_branch();
|
||||
@@ -1771,7 +1770,7 @@ impl<'a> Checker<'a> {
|
||||
bound: Some(bound), ..
|
||||
}) = type_param
|
||||
{
|
||||
self.visit_expr(bound);
|
||||
self.visit_annotation(bound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use ruff_python_parser::TokenKind;
|
||||
use ruff_source_file::Locator;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::registry::{AsRule, Rule};
|
||||
use crate::registry::AsRule;
|
||||
use crate::rules::pycodestyle::rules::logical_lines::{
|
||||
extraneous_whitespace, indentation, missing_whitespace, missing_whitespace_after_keyword,
|
||||
missing_whitespace_around_operator, space_after_comma, space_around_operator,
|
||||
@@ -38,17 +38,6 @@ pub(crate) fn check_logical_lines(
|
||||
) -> Vec<Diagnostic> {
|
||||
let mut context = LogicalLinesContext::new(settings);
|
||||
|
||||
let should_fix_missing_whitespace = settings.rules.should_fix(Rule::MissingWhitespace);
|
||||
let should_fix_whitespace_before_parameters =
|
||||
settings.rules.should_fix(Rule::WhitespaceBeforeParameters);
|
||||
let should_fix_whitespace_after_open_bracket =
|
||||
settings.rules.should_fix(Rule::WhitespaceAfterOpenBracket);
|
||||
let should_fix_whitespace_before_close_bracket = settings
|
||||
.rules
|
||||
.should_fix(Rule::WhitespaceBeforeCloseBracket);
|
||||
let should_fix_whitespace_before_punctuation =
|
||||
settings.rules.should_fix(Rule::WhitespaceBeforePunctuation);
|
||||
|
||||
let mut prev_line = None;
|
||||
let mut prev_indent_level = None;
|
||||
let indent_char = stylist.indentation().as_char();
|
||||
@@ -58,7 +47,7 @@ pub(crate) fn check_logical_lines(
|
||||
space_around_operator(&line, &mut context);
|
||||
whitespace_around_named_parameter_equals(&line, &mut context);
|
||||
missing_whitespace_around_operator(&line, &mut context);
|
||||
missing_whitespace(&line, should_fix_missing_whitespace, &mut context);
|
||||
missing_whitespace(&line, &mut context);
|
||||
}
|
||||
if line.flags().contains(TokenFlags::PUNCTUATION) {
|
||||
space_after_comma(&line, &mut context);
|
||||
@@ -68,13 +57,7 @@ pub(crate) fn check_logical_lines(
|
||||
.flags()
|
||||
.intersects(TokenFlags::OPERATOR | TokenFlags::BRACKET | TokenFlags::PUNCTUATION)
|
||||
{
|
||||
extraneous_whitespace(
|
||||
&line,
|
||||
&mut context,
|
||||
should_fix_whitespace_after_open_bracket,
|
||||
should_fix_whitespace_before_close_bracket,
|
||||
should_fix_whitespace_before_punctuation,
|
||||
);
|
||||
extraneous_whitespace(&line, &mut context);
|
||||
}
|
||||
|
||||
if line.flags().contains(TokenFlags::KEYWORD) {
|
||||
@@ -87,11 +70,7 @@ pub(crate) fn check_logical_lines(
|
||||
}
|
||||
|
||||
if line.flags().contains(TokenFlags::BRACKET) {
|
||||
whitespace_before_parameters(
|
||||
&line,
|
||||
should_fix_whitespace_before_parameters,
|
||||
&mut context,
|
||||
);
|
||||
whitespace_before_parameters(&line, &mut context);
|
||||
}
|
||||
|
||||
// Extract the indentation level.
|
||||
|
||||
@@ -109,10 +109,8 @@ pub(crate) fn check_noqa(
|
||||
if line.matches.is_empty() {
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(UnusedNOQA { codes: None }, directive.range());
|
||||
if settings.rules.should_fix(diagnostic.kind.rule()) {
|
||||
diagnostic
|
||||
.set_fix(Fix::safe_edit(delete_noqa(directive.range(), locator)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(delete_noqa(directive.range(), locator)));
|
||||
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
@@ -173,18 +171,14 @@ pub(crate) fn check_noqa(
|
||||
},
|
||||
directive.range(),
|
||||
);
|
||||
if settings.rules.should_fix(diagnostic.kind.rule()) {
|
||||
if valid_codes.is_empty() {
|
||||
diagnostic.set_fix(Fix::safe_edit(delete_noqa(
|
||||
directive.range(),
|
||||
locator,
|
||||
)));
|
||||
} else {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
format!("# noqa: {}", valid_codes.join(", ")),
|
||||
directive.range(),
|
||||
)));
|
||||
}
|
||||
if valid_codes.is_empty() {
|
||||
diagnostic
|
||||
.set_fix(Fix::safe_edit(delete_noqa(directive.range(), locator)));
|
||||
} else {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
format!("# noqa: {}", valid_codes.join(", ")),
|
||||
directive.range(),
|
||||
)));
|
||||
}
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -71,11 +71,7 @@ pub(crate) fn check_physical_lines(
|
||||
}
|
||||
|
||||
if enforce_no_newline_at_end_of_file {
|
||||
if let Some(diagnostic) = no_newline_at_end_of_file(
|
||||
locator,
|
||||
stylist,
|
||||
settings.rules.should_fix(Rule::MissingNewlineAtEndOfFile),
|
||||
) {
|
||||
if let Some(diagnostic) = no_newline_at_end_of_file(locator, stylist) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ pub(crate) fn check_tokens(
|
||||
}
|
||||
|
||||
if settings.rules.enabled(Rule::UTF8EncodingDeclaration) {
|
||||
pyupgrade::rules::unnecessary_coding_comment(&mut diagnostics, locator, indexer, settings);
|
||||
pyupgrade::rules::unnecessary_coding_comment(&mut diagnostics, locator, indexer);
|
||||
}
|
||||
|
||||
if settings.rules.enabled(Rule::InvalidEscapeSequence) {
|
||||
@@ -83,7 +83,6 @@ pub(crate) fn check_tokens(
|
||||
indexer,
|
||||
tok,
|
||||
*range,
|
||||
settings.rules.should_fix(Rule::InvalidEscapeSequence),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -109,13 +108,7 @@ pub(crate) fn check_tokens(
|
||||
Rule::MultipleStatementsOnOneLineSemicolon,
|
||||
Rule::UselessSemicolon,
|
||||
]) {
|
||||
pycodestyle::rules::compound_statements(
|
||||
&mut diagnostics,
|
||||
tokens,
|
||||
locator,
|
||||
indexer,
|
||||
settings,
|
||||
);
|
||||
pycodestyle::rules::compound_statements(&mut diagnostics, tokens, locator, indexer);
|
||||
}
|
||||
|
||||
if settings.rules.enabled(Rule::AvoidableEscapedQuote) && settings.flake8_quotes.avoid_escape {
|
||||
@@ -137,7 +130,7 @@ pub(crate) fn check_tokens(
|
||||
flake8_implicit_str_concat::rules::implicit(
|
||||
&mut diagnostics,
|
||||
tokens,
|
||||
&settings.flake8_implicit_str_concat,
|
||||
settings,
|
||||
locator,
|
||||
indexer,
|
||||
);
|
||||
@@ -148,11 +141,11 @@ pub(crate) fn check_tokens(
|
||||
Rule::TrailingCommaOnBareTuple,
|
||||
Rule::ProhibitedTrailingComma,
|
||||
]) {
|
||||
flake8_commas::rules::trailing_commas(&mut diagnostics, tokens, locator, settings);
|
||||
flake8_commas::rules::trailing_commas(&mut diagnostics, tokens, locator);
|
||||
}
|
||||
|
||||
if settings.rules.enabled(Rule::ExtraneousParentheses) {
|
||||
pyupgrade::rules::extraneous_parentheses(&mut diagnostics, tokens, locator, settings);
|
||||
pyupgrade::rules::extraneous_parentheses(&mut diagnostics, tokens, locator);
|
||||
}
|
||||
|
||||
if is_stub && settings.rules.enabled(Rule::TypeCommentInStub) {
|
||||
@@ -166,7 +159,7 @@ pub(crate) fn check_tokens(
|
||||
Rule::ShebangNotFirstLine,
|
||||
Rule::ShebangMissingPython,
|
||||
]) {
|
||||
flake8_executable::rules::from_tokens(tokens, path, locator, settings, &mut diagnostics);
|
||||
flake8_executable::rules::from_tokens(tokens, path, locator, &mut diagnostics);
|
||||
}
|
||||
|
||||
if settings.rules.any_enabled(&[
|
||||
@@ -191,7 +184,7 @@ pub(crate) fn check_tokens(
|
||||
TodoComment::from_comment(comment, *comment_range, i)
|
||||
})
|
||||
.collect();
|
||||
flake8_todos::rules::todos(&mut diagnostics, &todo_comments, locator, indexer, settings);
|
||||
flake8_todos::rules::todos(&mut diagnostics, &todo_comments, locator, indexer);
|
||||
flake8_fixme::rules::todos(&mut diagnostics, &todo_comments);
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ use itertools::Itertools;
|
||||
use log::error;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_diagnostics::{Applicability, Diagnostic};
|
||||
use ruff_python_ast::imports::ImportMap;
|
||||
use ruff_python_ast::PySourceType;
|
||||
use ruff_python_codegen::Stylist;
|
||||
@@ -260,6 +260,36 @@ pub fn check_path(
|
||||
}
|
||||
}
|
||||
|
||||
// Remove fixes for any rules marked as unfixable.
|
||||
for diagnostic in &mut diagnostics {
|
||||
if !settings.rules.should_fix(diagnostic.kind.rule()) {
|
||||
diagnostic.fix = None;
|
||||
}
|
||||
}
|
||||
|
||||
// Update fix applicability to account for overrides
|
||||
if !settings.extend_safe_fixes.is_empty() || !settings.extend_unsafe_fixes.is_empty() {
|
||||
for diagnostic in &mut diagnostics {
|
||||
if let Some(fix) = diagnostic.fix.take() {
|
||||
// Enforce demotions over promotions so if someone puts a rule in both we are conservative
|
||||
if fix.applicability().is_safe()
|
||||
&& settings
|
||||
.extend_unsafe_fixes
|
||||
.contains(diagnostic.kind.rule())
|
||||
{
|
||||
diagnostic.set_fix(fix.with_applicability(Applicability::Unsafe));
|
||||
} else if fix.applicability().is_unsafe()
|
||||
&& settings.extend_safe_fixes.contains(diagnostic.kind.rule())
|
||||
{
|
||||
diagnostic.set_fix(fix.with_applicability(Applicability::Safe));
|
||||
} else {
|
||||
// Retain the existing fix (will be dropped from `.take()` otherwise)
|
||||
diagnostic.set_fix(fix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LinterResult::new((diagnostics, imports), error)
|
||||
}
|
||||
|
||||
|
||||
@@ -177,18 +177,15 @@ impl Display for DisplayParseError<'_> {
|
||||
f,
|
||||
"cell {cell}{colon}",
|
||||
cell = jupyter_index
|
||||
.cell(source_location.row.get())
|
||||
.unwrap_or_default(),
|
||||
.cell(source_location.row)
|
||||
.unwrap_or(OneIndexed::MIN),
|
||||
colon = ":".cyan(),
|
||||
)?;
|
||||
|
||||
SourceLocation {
|
||||
row: OneIndexed::new(
|
||||
jupyter_index
|
||||
.cell_row(source_location.row.get())
|
||||
.unwrap_or(1) as usize,
|
||||
)
|
||||
.unwrap(),
|
||||
row: jupyter_index
|
||||
.cell_row(source_location.row)
|
||||
.unwrap_or(OneIndexed::MIN),
|
||||
column: source_location.column,
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -35,6 +35,7 @@ impl<'a> Diff<'a> {
|
||||
|
||||
impl Display for Diff<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
// TODO(dhruvmanila): Add support for Notebook cells once it's user-facing
|
||||
let mut output = String::with_capacity(self.source_code.source_text().len());
|
||||
let mut last_end = TextSize::default();
|
||||
|
||||
|
||||
@@ -125,18 +125,18 @@ impl Display for DisplayGroupedMessage<'_> {
|
||||
f,
|
||||
"cell {cell}{sep}",
|
||||
cell = jupyter_index
|
||||
.cell(start_location.row.get())
|
||||
.unwrap_or_default(),
|
||||
.cell(start_location.row)
|
||||
.unwrap_or(OneIndexed::MIN),
|
||||
sep = ":".cyan()
|
||||
)?;
|
||||
(
|
||||
jupyter_index
|
||||
.cell_row(start_location.row.get())
|
||||
.unwrap_or(1) as usize,
|
||||
start_location.column.get(),
|
||||
.cell_row(start_location.row)
|
||||
.unwrap_or(OneIndexed::MIN),
|
||||
start_location.column,
|
||||
)
|
||||
} else {
|
||||
(start_location.row.get(), start_location.column.get())
|
||||
(start_location.row, start_location.column)
|
||||
};
|
||||
|
||||
writeln!(
|
||||
|
||||
@@ -5,7 +5,8 @@ use serde::{Serialize, Serializer};
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use ruff_diagnostics::Edit;
|
||||
use ruff_source_file::SourceCode;
|
||||
use ruff_notebook::NotebookIndex;
|
||||
use ruff_source_file::{OneIndexed, SourceCode, SourceLocation};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::message::{Emitter, EmitterContext, Message};
|
||||
@@ -19,9 +20,9 @@ impl Emitter for JsonEmitter {
|
||||
&mut self,
|
||||
writer: &mut dyn Write,
|
||||
messages: &[Message],
|
||||
_context: &EmitterContext,
|
||||
context: &EmitterContext,
|
||||
) -> anyhow::Result<()> {
|
||||
serde_json::to_writer_pretty(writer, &ExpandedMessages { messages })?;
|
||||
serde_json::to_writer_pretty(writer, &ExpandedMessages { messages, context })?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -29,6 +30,7 @@ impl Emitter for JsonEmitter {
|
||||
|
||||
struct ExpandedMessages<'a> {
|
||||
messages: &'a [Message],
|
||||
context: &'a EmitterContext<'a>,
|
||||
}
|
||||
|
||||
impl Serialize for ExpandedMessages<'_> {
|
||||
@@ -39,7 +41,7 @@ impl Serialize for ExpandedMessages<'_> {
|
||||
let mut s = serializer.serialize_seq(Some(self.messages.len()))?;
|
||||
|
||||
for message in self.messages {
|
||||
let value = message_to_json_value(message);
|
||||
let value = message_to_json_value(message, self.context);
|
||||
s.serialize_element(&value)?;
|
||||
}
|
||||
|
||||
@@ -47,26 +49,40 @@ impl Serialize for ExpandedMessages<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn message_to_json_value(message: &Message) -> Value {
|
||||
pub(crate) fn message_to_json_value(message: &Message, context: &EmitterContext) -> Value {
|
||||
let source_code = message.file.to_source_code();
|
||||
let notebook_index = context.notebook_index(message.filename());
|
||||
|
||||
let fix = message.fix.as_ref().map(|fix| {
|
||||
json!({
|
||||
"applicability": fix.applicability(),
|
||||
"message": message.kind.suggestion.as_deref(),
|
||||
"edits": &ExpandedEdits { edits: fix.edits(), source_code: &source_code },
|
||||
"edits": &ExpandedEdits { edits: fix.edits(), source_code: &source_code, notebook_index },
|
||||
})
|
||||
});
|
||||
|
||||
let start_location = source_code.source_location(message.start());
|
||||
let end_location = source_code.source_location(message.end());
|
||||
let noqa_location = source_code.source_location(message.noqa_offset);
|
||||
let mut start_location = source_code.source_location(message.start());
|
||||
let mut end_location = source_code.source_location(message.end());
|
||||
let mut noqa_location = source_code.source_location(message.noqa_offset);
|
||||
let mut notebook_cell_index = None;
|
||||
|
||||
if let Some(notebook_index) = notebook_index {
|
||||
notebook_cell_index = Some(
|
||||
notebook_index
|
||||
.cell(start_location.row)
|
||||
.unwrap_or(OneIndexed::MIN),
|
||||
);
|
||||
start_location = notebook_index.translate_location(&start_location);
|
||||
end_location = notebook_index.translate_location(&end_location);
|
||||
noqa_location = notebook_index.translate_location(&noqa_location);
|
||||
}
|
||||
|
||||
json!({
|
||||
"code": message.kind.rule().noqa_code().to_string(),
|
||||
"url": message.kind.rule().url(),
|
||||
"message": message.kind.body,
|
||||
"fix": fix,
|
||||
"cell": notebook_cell_index,
|
||||
"location": start_location,
|
||||
"end_location": end_location,
|
||||
"filename": message.filename(),
|
||||
@@ -77,6 +93,7 @@ pub(crate) fn message_to_json_value(message: &Message) -> Value {
|
||||
struct ExpandedEdits<'a> {
|
||||
edits: &'a [Edit],
|
||||
source_code: &'a SourceCode<'a, 'a>,
|
||||
notebook_index: Option<&'a NotebookIndex>,
|
||||
}
|
||||
|
||||
impl Serialize for ExpandedEdits<'_> {
|
||||
@@ -87,10 +104,57 @@ impl Serialize for ExpandedEdits<'_> {
|
||||
let mut s = serializer.serialize_seq(Some(self.edits.len()))?;
|
||||
|
||||
for edit in self.edits {
|
||||
let mut location = self.source_code.source_location(edit.start());
|
||||
let mut end_location = self.source_code.source_location(edit.end());
|
||||
|
||||
if let Some(notebook_index) = self.notebook_index {
|
||||
// There exists a newline between each cell's source code in the
|
||||
// concatenated source code in Ruff. This newline doesn't actually
|
||||
// exists in the JSON source field.
|
||||
//
|
||||
// Now, certain edits may try to remove this newline, which means
|
||||
// the edit will spill over to the first character of the next cell.
|
||||
// If it does, we need to translate the end location to the last
|
||||
// character of the previous cell.
|
||||
match (
|
||||
notebook_index.cell(location.row),
|
||||
notebook_index.cell(end_location.row),
|
||||
) {
|
||||
(Some(start_cell), Some(end_cell)) if start_cell != end_cell => {
|
||||
debug_assert_eq!(end_location.column.get(), 1);
|
||||
|
||||
let prev_row = end_location.row.saturating_sub(1);
|
||||
end_location = SourceLocation {
|
||||
row: notebook_index.cell_row(prev_row).unwrap_or(OneIndexed::MIN),
|
||||
column: self
|
||||
.source_code
|
||||
.source_location(self.source_code.line_end_exclusive(prev_row))
|
||||
.column,
|
||||
};
|
||||
}
|
||||
(Some(_), None) => {
|
||||
debug_assert_eq!(end_location.column.get(), 1);
|
||||
|
||||
let prev_row = end_location.row.saturating_sub(1);
|
||||
end_location = SourceLocation {
|
||||
row: notebook_index.cell_row(prev_row).unwrap_or(OneIndexed::MIN),
|
||||
column: self
|
||||
.source_code
|
||||
.source_location(self.source_code.line_end_exclusive(prev_row))
|
||||
.column,
|
||||
};
|
||||
}
|
||||
_ => {
|
||||
end_location = notebook_index.translate_location(&end_location);
|
||||
}
|
||||
}
|
||||
location = notebook_index.translate_location(&location);
|
||||
}
|
||||
|
||||
let value = json!({
|
||||
"content": edit.content().unwrap_or_default(),
|
||||
"location": self.source_code.source_location(edit.start()),
|
||||
"end_location": self.source_code.source_location(edit.end())
|
||||
"location": location,
|
||||
"end_location": end_location
|
||||
});
|
||||
|
||||
s.serialize_element(&value)?;
|
||||
@@ -104,7 +168,10 @@ impl Serialize for ExpandedEdits<'_> {
|
||||
mod tests {
|
||||
use insta::assert_snapshot;
|
||||
|
||||
use crate::message::tests::{capture_emitter_output, create_messages};
|
||||
use crate::message::tests::{
|
||||
capture_emitter_notebook_output, capture_emitter_output, create_messages,
|
||||
create_notebook_messages,
|
||||
};
|
||||
use crate::message::JsonEmitter;
|
||||
|
||||
#[test]
|
||||
@@ -114,4 +181,13 @@ mod tests {
|
||||
|
||||
assert_snapshot!(content);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn notebook_output() {
|
||||
let mut emitter = JsonEmitter;
|
||||
let (messages, notebook_indexes) = create_notebook_messages();
|
||||
let content = capture_emitter_notebook_output(&mut emitter, &messages, ¬ebook_indexes);
|
||||
|
||||
assert_snapshot!(content);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,11 +11,11 @@ impl Emitter for JsonLinesEmitter {
|
||||
&mut self,
|
||||
writer: &mut dyn Write,
|
||||
messages: &[Message],
|
||||
_context: &EmitterContext,
|
||||
context: &EmitterContext,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut w = writer;
|
||||
for message in messages {
|
||||
serde_json::to_writer(&mut w, &message_to_json_value(message))?;
|
||||
serde_json::to_writer(&mut w, &message_to_json_value(message, context))?;
|
||||
w.write_all(b"\n")?;
|
||||
}
|
||||
Ok(())
|
||||
@@ -27,7 +27,10 @@ mod tests {
|
||||
use insta::assert_snapshot;
|
||||
|
||||
use crate::message::json_lines::JsonLinesEmitter;
|
||||
use crate::message::tests::{capture_emitter_output, create_messages};
|
||||
use crate::message::tests::{
|
||||
capture_emitter_notebook_output, capture_emitter_output, create_messages,
|
||||
create_notebook_messages,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn output() {
|
||||
@@ -36,4 +39,13 @@ mod tests {
|
||||
|
||||
assert_snapshot!(content);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn notebook_output() {
|
||||
let mut emitter = JsonLinesEmitter;
|
||||
let (messages, notebook_indexes) = create_notebook_messages();
|
||||
let content = capture_emitter_notebook_output(&mut emitter, &messages, ¬ebook_indexes);
|
||||
|
||||
assert_snapshot!(content);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +150,8 @@ mod tests {
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, DiagnosticKind, Edit, Fix};
|
||||
use ruff_source_file::SourceFileBuilder;
|
||||
use ruff_notebook::NotebookIndex;
|
||||
use ruff_source_file::{OneIndexed, SourceFileBuilder};
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
||||
use crate::message::{Emitter, EmitterContext, Message};
|
||||
@@ -221,6 +222,113 @@ def fibonacci(n):
|
||||
]
|
||||
}
|
||||
|
||||
pub(super) fn create_notebook_messages() -> (Vec<Message>, FxHashMap<String, NotebookIndex>) {
|
||||
let notebook = r"# cell 1
|
||||
import os
|
||||
# cell 2
|
||||
import math
|
||||
|
||||
print('hello world')
|
||||
# cell 3
|
||||
def foo():
|
||||
print()
|
||||
x = 1
|
||||
";
|
||||
|
||||
let unused_import_os = Diagnostic::new(
|
||||
DiagnosticKind {
|
||||
name: "UnusedImport".to_string(),
|
||||
body: "`os` imported but unused".to_string(),
|
||||
suggestion: Some("Remove unused import: `os`".to_string()),
|
||||
},
|
||||
TextRange::new(TextSize::from(16), TextSize::from(18)),
|
||||
)
|
||||
.with_fix(Fix::safe_edit(Edit::range_deletion(TextRange::new(
|
||||
TextSize::from(9),
|
||||
TextSize::from(19),
|
||||
))));
|
||||
|
||||
let unused_import_math = Diagnostic::new(
|
||||
DiagnosticKind {
|
||||
name: "UnusedImport".to_string(),
|
||||
body: "`math` imported but unused".to_string(),
|
||||
suggestion: Some("Remove unused import: `math`".to_string()),
|
||||
},
|
||||
TextRange::new(TextSize::from(35), TextSize::from(39)),
|
||||
)
|
||||
.with_fix(Fix::safe_edit(Edit::range_deletion(TextRange::new(
|
||||
TextSize::from(28),
|
||||
TextSize::from(40),
|
||||
))));
|
||||
|
||||
let unused_variable = Diagnostic::new(
|
||||
DiagnosticKind {
|
||||
name: "UnusedVariable".to_string(),
|
||||
body: "Local variable `x` is assigned to but never used".to_string(),
|
||||
suggestion: Some("Remove assignment to unused variable `x`".to_string()),
|
||||
},
|
||||
TextRange::new(TextSize::from(98), TextSize::from(99)),
|
||||
)
|
||||
.with_fix(Fix::unsafe_edit(Edit::deletion(
|
||||
TextSize::from(94),
|
||||
TextSize::from(104),
|
||||
)));
|
||||
|
||||
let notebook_source = SourceFileBuilder::new("notebook.ipynb", notebook).finish();
|
||||
|
||||
let mut notebook_indexes = FxHashMap::default();
|
||||
notebook_indexes.insert(
|
||||
"notebook.ipynb".to_string(),
|
||||
NotebookIndex::new(
|
||||
vec![
|
||||
OneIndexed::from_zero_indexed(0),
|
||||
OneIndexed::from_zero_indexed(0),
|
||||
OneIndexed::from_zero_indexed(1),
|
||||
OneIndexed::from_zero_indexed(1),
|
||||
OneIndexed::from_zero_indexed(1),
|
||||
OneIndexed::from_zero_indexed(1),
|
||||
OneIndexed::from_zero_indexed(2),
|
||||
OneIndexed::from_zero_indexed(2),
|
||||
OneIndexed::from_zero_indexed(2),
|
||||
OneIndexed::from_zero_indexed(2),
|
||||
],
|
||||
vec![
|
||||
OneIndexed::from_zero_indexed(0),
|
||||
OneIndexed::from_zero_indexed(1),
|
||||
OneIndexed::from_zero_indexed(0),
|
||||
OneIndexed::from_zero_indexed(1),
|
||||
OneIndexed::from_zero_indexed(2),
|
||||
OneIndexed::from_zero_indexed(3),
|
||||
OneIndexed::from_zero_indexed(0),
|
||||
OneIndexed::from_zero_indexed(1),
|
||||
OneIndexed::from_zero_indexed(2),
|
||||
OneIndexed::from_zero_indexed(3),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
let unused_import_os_start = unused_import_os.start();
|
||||
let unused_import_math_start = unused_import_math.start();
|
||||
let unused_variable_start = unused_variable.start();
|
||||
|
||||
(
|
||||
vec![
|
||||
Message::from_diagnostic(
|
||||
unused_import_os,
|
||||
notebook_source.clone(),
|
||||
unused_import_os_start,
|
||||
),
|
||||
Message::from_diagnostic(
|
||||
unused_import_math,
|
||||
notebook_source.clone(),
|
||||
unused_import_math_start,
|
||||
),
|
||||
Message::from_diagnostic(unused_variable, notebook_source, unused_variable_start),
|
||||
],
|
||||
notebook_indexes,
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn capture_emitter_output(
|
||||
emitter: &mut dyn Emitter,
|
||||
messages: &[Message],
|
||||
@@ -232,4 +340,16 @@ def fibonacci(n):
|
||||
|
||||
String::from_utf8(output).expect("Output to be valid UTF-8")
|
||||
}
|
||||
|
||||
pub(super) fn capture_emitter_notebook_output(
|
||||
emitter: &mut dyn Emitter,
|
||||
messages: &[Message],
|
||||
notebook_indexes: &FxHashMap<String, NotebookIndex>,
|
||||
) -> String {
|
||||
let context = EmitterContext::new(notebook_indexes);
|
||||
let mut output: Vec<u8> = Vec::new();
|
||||
emitter.emit(&mut output, messages, &context).unwrap();
|
||||
|
||||
String::from_utf8(output).expect("Output to be valid UTF-8")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/message/json.rs
|
||||
expression: content
|
||||
---
|
||||
[
|
||||
{
|
||||
"cell": 1,
|
||||
"code": "F401",
|
||||
"end_location": {
|
||||
"column": 10,
|
||||
"row": 2
|
||||
},
|
||||
"filename": "notebook.ipynb",
|
||||
"fix": {
|
||||
"applicability": "safe",
|
||||
"edits": [
|
||||
{
|
||||
"content": "",
|
||||
"end_location": {
|
||||
"column": 10,
|
||||
"row": 2
|
||||
},
|
||||
"location": {
|
||||
"column": 1,
|
||||
"row": 2
|
||||
}
|
||||
}
|
||||
],
|
||||
"message": "Remove unused import: `os`"
|
||||
},
|
||||
"location": {
|
||||
"column": 8,
|
||||
"row": 2
|
||||
},
|
||||
"message": "`os` imported but unused",
|
||||
"noqa_row": 2,
|
||||
"url": "https://docs.astral.sh/ruff/rules/unused-import"
|
||||
},
|
||||
{
|
||||
"cell": 2,
|
||||
"code": "F401",
|
||||
"end_location": {
|
||||
"column": 12,
|
||||
"row": 2
|
||||
},
|
||||
"filename": "notebook.ipynb",
|
||||
"fix": {
|
||||
"applicability": "safe",
|
||||
"edits": [
|
||||
{
|
||||
"content": "",
|
||||
"end_location": {
|
||||
"column": 1,
|
||||
"row": 3
|
||||
},
|
||||
"location": {
|
||||
"column": 1,
|
||||
"row": 2
|
||||
}
|
||||
}
|
||||
],
|
||||
"message": "Remove unused import: `math`"
|
||||
},
|
||||
"location": {
|
||||
"column": 8,
|
||||
"row": 2
|
||||
},
|
||||
"message": "`math` imported but unused",
|
||||
"noqa_row": 2,
|
||||
"url": "https://docs.astral.sh/ruff/rules/unused-import"
|
||||
},
|
||||
{
|
||||
"cell": 3,
|
||||
"code": "F841",
|
||||
"end_location": {
|
||||
"column": 6,
|
||||
"row": 4
|
||||
},
|
||||
"filename": "notebook.ipynb",
|
||||
"fix": {
|
||||
"applicability": "unsafe",
|
||||
"edits": [
|
||||
{
|
||||
"content": "",
|
||||
"end_location": {
|
||||
"column": 10,
|
||||
"row": 4
|
||||
},
|
||||
"location": {
|
||||
"column": 1,
|
||||
"row": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"message": "Remove assignment to unused variable `x`"
|
||||
},
|
||||
"location": {
|
||||
"column": 5,
|
||||
"row": 4
|
||||
},
|
||||
"message": "Local variable `x` is assigned to but never used",
|
||||
"noqa_row": 4,
|
||||
"url": "https://docs.astral.sh/ruff/rules/unused-variable"
|
||||
}
|
||||
]
|
||||
@@ -4,6 +4,7 @@ expression: content
|
||||
---
|
||||
[
|
||||
{
|
||||
"cell": null,
|
||||
"code": "F401",
|
||||
"end_location": {
|
||||
"column": 10,
|
||||
@@ -36,6 +37,7 @@ expression: content
|
||||
"url": "https://docs.astral.sh/ruff/rules/unused-import"
|
||||
},
|
||||
{
|
||||
"cell": null,
|
||||
"code": "F841",
|
||||
"end_location": {
|
||||
"column": 6,
|
||||
@@ -68,6 +70,7 @@ expression: content
|
||||
"url": "https://docs.astral.sh/ruff/rules/unused-variable"
|
||||
},
|
||||
{
|
||||
"cell": null,
|
||||
"code": "F821",
|
||||
"end_location": {
|
||||
"column": 5,
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/message/json_lines.rs
|
||||
expression: content
|
||||
---
|
||||
{"cell":1,"code":"F401","end_location":{"column":10,"row":2},"filename":"notebook.ipynb","fix":{"applicability":"safe","edits":[{"content":"","end_location":{"column":10,"row":2},"location":{"column":1,"row":2}}],"message":"Remove unused import: `os`"},"location":{"column":8,"row":2},"message":"`os` imported but unused","noqa_row":2,"url":"https://docs.astral.sh/ruff/rules/unused-import"}
|
||||
{"cell":2,"code":"F401","end_location":{"column":12,"row":2},"filename":"notebook.ipynb","fix":{"applicability":"safe","edits":[{"content":"","end_location":{"column":1,"row":3},"location":{"column":1,"row":2}}],"message":"Remove unused import: `math`"},"location":{"column":8,"row":2},"message":"`math` imported but unused","noqa_row":2,"url":"https://docs.astral.sh/ruff/rules/unused-import"}
|
||||
{"cell":3,"code":"F841","end_location":{"column":6,"row":4},"filename":"notebook.ipynb","fix":{"applicability":"unsafe","edits":[{"content":"","end_location":{"column":10,"row":4},"location":{"column":1,"row":4}}],"message":"Remove assignment to unused variable `x`"},"location":{"column":5,"row":4},"message":"Local variable `x` is assigned to but never used","noqa_row":4,"url":"https://docs.astral.sh/ruff/rules/unused-variable"}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
source: crates/ruff_linter/src/message/json_lines.rs
|
||||
expression: content
|
||||
---
|
||||
{"code":"F401","end_location":{"column":10,"row":1},"filename":"fib.py","fix":{"applicability":"unsafe","edits":[{"content":"","end_location":{"column":1,"row":2},"location":{"column":1,"row":1}}],"message":"Remove unused import: `os`"},"location":{"column":8,"row":1},"message":"`os` imported but unused","noqa_row":1,"url":"https://docs.astral.sh/ruff/rules/unused-import"}
|
||||
{"code":"F841","end_location":{"column":6,"row":6},"filename":"fib.py","fix":{"applicability":"unsafe","edits":[{"content":"","end_location":{"column":10,"row":6},"location":{"column":5,"row":6}}],"message":"Remove assignment to unused variable `x`"},"location":{"column":5,"row":6},"message":"Local variable `x` is assigned to but never used","noqa_row":6,"url":"https://docs.astral.sh/ruff/rules/unused-variable"}
|
||||
{"code":"F821","end_location":{"column":5,"row":1},"filename":"undef.py","fix":null,"location":{"column":4,"row":1},"message":"Undefined name `a`","noqa_row":1,"url":"https://docs.astral.sh/ruff/rules/undefined-name"}
|
||||
{"cell":null,"code":"F401","end_location":{"column":10,"row":1},"filename":"fib.py","fix":{"applicability":"unsafe","edits":[{"content":"","end_location":{"column":1,"row":2},"location":{"column":1,"row":1}}],"message":"Remove unused import: `os`"},"location":{"column":8,"row":1},"message":"`os` imported but unused","noqa_row":1,"url":"https://docs.astral.sh/ruff/rules/unused-import"}
|
||||
{"cell":null,"code":"F841","end_location":{"column":6,"row":6},"filename":"fib.py","fix":{"applicability":"unsafe","edits":[{"content":"","end_location":{"column":10,"row":6},"location":{"column":5,"row":6}}],"message":"Remove assignment to unused variable `x`"},"location":{"column":5,"row":6},"message":"Local variable `x` is assigned to but never used","noqa_row":6,"url":"https://docs.astral.sh/ruff/rules/unused-variable"}
|
||||
{"cell":null,"code":"F821","end_location":{"column":5,"row":1},"filename":"undef.py","fix":null,"location":{"column":4,"row":1},"message":"Undefined name `a`","noqa_row":1,"url":"https://docs.astral.sh/ruff/rules/undefined-name"}
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/message/text.rs
|
||||
expression: content
|
||||
---
|
||||
notebook.ipynb:cell 1:2:8: F401 [*] `os` imported but unused
|
||||
|
|
||||
1 | # cell 1
|
||||
2 | import os
|
||||
| ^^ F401
|
||||
|
|
||||
= help: Remove unused import: `os`
|
||||
|
||||
notebook.ipynb:cell 2:2:8: F401 [*] `math` imported but unused
|
||||
|
|
||||
1 | # cell 2
|
||||
2 | import math
|
||||
| ^^^^ F401
|
||||
3 |
|
||||
4 | print('hello world')
|
||||
|
|
||||
= help: Remove unused import: `math`
|
||||
|
||||
notebook.ipynb:cell 3:4:5: F841 [*] Local variable `x` is assigned to but never used
|
||||
|
|
||||
2 | def foo():
|
||||
3 | print()
|
||||
4 | x = 1
|
||||
| ^ F841
|
||||
|
|
||||
= help: Remove assignment to unused variable `x`
|
||||
|
||||
|
||||
@@ -87,18 +87,15 @@ impl Emitter for TextEmitter {
|
||||
writer,
|
||||
"cell {cell}{sep}",
|
||||
cell = notebook_index
|
||||
.cell(start_location.row.get())
|
||||
.unwrap_or_default(),
|
||||
.cell(start_location.row)
|
||||
.unwrap_or(OneIndexed::MIN),
|
||||
sep = ":".cyan(),
|
||||
)?;
|
||||
|
||||
SourceLocation {
|
||||
row: OneIndexed::new(
|
||||
notebook_index
|
||||
.cell_row(start_location.row.get())
|
||||
.unwrap_or(1) as usize,
|
||||
)
|
||||
.unwrap(),
|
||||
row: notebook_index
|
||||
.cell_row(start_location.row)
|
||||
.unwrap_or(OneIndexed::MIN),
|
||||
column: start_location.column,
|
||||
}
|
||||
} else {
|
||||
@@ -203,9 +200,9 @@ impl Display for MessageCodeFrame<'_> {
|
||||
// If we're working with a Jupyter Notebook, skip the lines which are
|
||||
// outside of the cell containing the diagnostic.
|
||||
if let Some(index) = self.notebook_index {
|
||||
let content_start_cell = index.cell(content_start_index.get()).unwrap_or_default();
|
||||
let content_start_cell = index.cell(content_start_index).unwrap_or(OneIndexed::MIN);
|
||||
while start_index < content_start_index {
|
||||
if index.cell(start_index.get()).unwrap_or_default() == content_start_cell {
|
||||
if index.cell(start_index).unwrap_or(OneIndexed::MIN) == content_start_cell {
|
||||
break;
|
||||
}
|
||||
start_index = start_index.saturating_add(1);
|
||||
@@ -228,9 +225,9 @@ impl Display for MessageCodeFrame<'_> {
|
||||
// If we're working with a Jupyter Notebook, skip the lines which are
|
||||
// outside of the cell containing the diagnostic.
|
||||
if let Some(index) = self.notebook_index {
|
||||
let content_end_cell = index.cell(content_end_index.get()).unwrap_or_default();
|
||||
let content_end_cell = index.cell(content_end_index).unwrap_or(OneIndexed::MIN);
|
||||
while end_index > content_end_index {
|
||||
if index.cell(end_index.get()).unwrap_or_default() == content_end_cell {
|
||||
if index.cell(end_index).unwrap_or(OneIndexed::MIN) == content_end_cell {
|
||||
break;
|
||||
}
|
||||
end_index = end_index.saturating_sub(1);
|
||||
@@ -270,8 +267,9 @@ impl Display for MessageCodeFrame<'_> {
|
||||
|| start_index.get(),
|
||||
|notebook_index| {
|
||||
notebook_index
|
||||
.cell_row(start_index.get())
|
||||
.unwrap_or_default() as usize
|
||||
.cell_row(start_index)
|
||||
.unwrap_or(OneIndexed::MIN)
|
||||
.get()
|
||||
},
|
||||
),
|
||||
annotations: vec![SourceAnnotation {
|
||||
@@ -353,7 +351,10 @@ struct SourceCode<'a> {
|
||||
mod tests {
|
||||
use insta::assert_snapshot;
|
||||
|
||||
use crate::message::tests::{capture_emitter_output, create_messages};
|
||||
use crate::message::tests::{
|
||||
capture_emitter_notebook_output, capture_emitter_output, create_messages,
|
||||
create_notebook_messages,
|
||||
};
|
||||
use crate::message::TextEmitter;
|
||||
use crate::settings::types::UnsafeFixes;
|
||||
|
||||
@@ -385,4 +386,16 @@ mod tests {
|
||||
|
||||
assert_snapshot!(content);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn notebook_output() {
|
||||
let mut emitter = TextEmitter::default()
|
||||
.with_show_fix_status(true)
|
||||
.with_show_source(true)
|
||||
.with_unsafe_fixes(UnsafeFixes::Enabled);
|
||||
let (messages, notebook_indexes) = create_notebook_messages();
|
||||
let content = capture_emitter_notebook_output(&mut emitter, &messages, ¬ebook_indexes);
|
||||
|
||||
assert_snapshot!(content);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_source_file::Locator;
|
||||
|
||||
use crate::registry::Rule;
|
||||
use crate::settings::LinterSettings;
|
||||
|
||||
use super::super::detection::comment_contains_code;
|
||||
@@ -67,11 +66,9 @@ pub(crate) fn commented_out_code(
|
||||
if is_standalone_comment(line) && comment_contains_code(line, &settings.task_tags[..]) {
|
||||
let mut diagnostic = Diagnostic::new(CommentedOutCode, *range);
|
||||
|
||||
if settings.rules.should_fix(Rule::CommentedOutCode) {
|
||||
diagnostic.set_fix(Fix::display_edit(Edit::range_deletion(
|
||||
locator.full_lines_range(*range),
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::display_edit(Edit::range_deletion(
|
||||
locator.full_lines_range(*range),
|
||||
)));
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use ruff_python_stdlib::typing::simple_magic_return_type;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::{AsRule, Rule};
|
||||
use crate::registry::Rule;
|
||||
use crate::rules::ruff::typing::type_hint_resolves_to_any;
|
||||
|
||||
/// ## What it does
|
||||
@@ -702,12 +702,10 @@ pub(crate) fn definition(
|
||||
},
|
||||
function.identifier(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
|
||||
" -> None".to_string(),
|
||||
function.parameters.range().end(),
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
|
||||
" -> None".to_string(),
|
||||
function.parameters.range().end(),
|
||||
)));
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
@@ -719,13 +717,11 @@ pub(crate) fn definition(
|
||||
},
|
||||
function.identifier(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if let Some(return_type) = simple_magic_return_type(name) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
|
||||
format!(" -> {return_type}"),
|
||||
function.parameters.range().end(),
|
||||
)));
|
||||
}
|
||||
if let Some(return_type) = simple_magic_return_type(name) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
|
||||
format!(" -> {return_type}"),
|
||||
function.parameters.range().end(),
|
||||
)));
|
||||
}
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::helpers::is_const_false;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `assert False`.
|
||||
@@ -75,11 +74,9 @@ pub(crate) fn assert_false(checker: &mut Checker, stmt: &Stmt, test: &Expr, msg:
|
||||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(AssertFalse, test.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().stmt(&assertion_error(msg)),
|
||||
stmt.range(),
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
checker.generator().stmt(&assertion_error(msg)),
|
||||
stmt.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use ruff_python_ast::call_path::CallPath;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::edits::pad;
|
||||
use crate::registry::{AsRule, Rule};
|
||||
use crate::registry::Rule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for `try-except` blocks with duplicate exception handlers.
|
||||
@@ -146,24 +146,22 @@ fn duplicate_handler_exceptions<'a>(
|
||||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
// Single exceptions don't require parentheses, but since we're _removing_
|
||||
// parentheses, insert whitespace as needed.
|
||||
if let [elt] = unique_elts.as_slice() {
|
||||
pad(
|
||||
checker.generator().expr(elt),
|
||||
expr.range(),
|
||||
checker.locator(),
|
||||
)
|
||||
} else {
|
||||
// Multiple exceptions must always be parenthesized. This is done
|
||||
// manually as the generator never parenthesizes lone tuples.
|
||||
format!("({})", checker.generator().expr(&type_pattern(unique_elts)))
|
||||
},
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
// Single exceptions don't require parentheses, but since we're _removing_
|
||||
// parentheses, insert whitespace as needed.
|
||||
if let [elt] = unique_elts.as_slice() {
|
||||
pad(
|
||||
checker.generator().expr(elt),
|
||||
expr.range(),
|
||||
checker.locator(),
|
||||
)
|
||||
} else {
|
||||
// Multiple exceptions must always be parenthesized. This is done
|
||||
// manually as the generator never parenthesizes lone tuples.
|
||||
format!("({})", checker.generator().expr(&type_pattern(unique_elts)))
|
||||
},
|
||||
expr.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ use ruff_python_stdlib::identifiers::{is_identifier, is_mangled_private};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `getattr` that take a constant attribute value as an
|
||||
@@ -85,26 +84,24 @@ pub(crate) fn getattr_with_constant(
|
||||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(GetAttrWithConstant, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
pad(
|
||||
if matches!(
|
||||
obj,
|
||||
Expr::Name(_) | Expr::Attribute(_) | Expr::Subscript(_) | Expr::Call(_)
|
||||
) && !checker.locator().contains_line_break(obj.range())
|
||||
{
|
||||
format!("{}.{}", checker.locator().slice(obj), value)
|
||||
} else {
|
||||
// Defensively parenthesize any other expressions. For example, attribute accesses
|
||||
// on `int` literals must be parenthesized, e.g., `getattr(1, "real")` becomes
|
||||
// `(1).real`. The same is true for named expressions and others.
|
||||
format!("({}).{}", checker.locator().slice(obj), value)
|
||||
},
|
||||
expr.range(),
|
||||
checker.locator(),
|
||||
),
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
pad(
|
||||
if matches!(
|
||||
obj,
|
||||
Expr::Name(_) | Expr::Attribute(_) | Expr::Subscript(_) | Expr::Call(_)
|
||||
) && !checker.locator().contains_line_break(obj.range())
|
||||
{
|
||||
format!("{}.{}", checker.locator().slice(obj), value)
|
||||
} else {
|
||||
// Defensively parenthesize any other expressions. For example, attribute accesses
|
||||
// on `int` literals must be parenthesized, e.g., `getattr(1, "real")` becomes
|
||||
// `(1).real`. The same is true for named expressions and others.
|
||||
format!("({}).{}", checker.locator().slice(obj), value)
|
||||
},
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
checker.locator(),
|
||||
),
|
||||
expr.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ use ruff_source_file::Locator;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of mutable objects as function argument defaults.
|
||||
@@ -110,18 +109,16 @@ pub(crate) fn mutable_argument_default(checker: &mut Checker, function_def: &ast
|
||||
let mut diagnostic = Diagnostic::new(MutableArgumentDefault, default.range());
|
||||
|
||||
// If the function body is on the same line as the function def, do not fix
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if let Some(fix) = move_initialization(
|
||||
function_def,
|
||||
parameter,
|
||||
default,
|
||||
checker.locator(),
|
||||
checker.stylist(),
|
||||
checker.indexer(),
|
||||
checker.generator(),
|
||||
) {
|
||||
diagnostic.set_fix(fix);
|
||||
}
|
||||
if let Some(fix) = move_initialization(
|
||||
function_def,
|
||||
parameter,
|
||||
default,
|
||||
checker.locator(),
|
||||
checker.stylist(),
|
||||
checker.indexer(),
|
||||
checker.generator(),
|
||||
) {
|
||||
diagnostic.set_fix(fix);
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::edits::pad;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for single-element tuples in exception handlers (e.g.,
|
||||
@@ -77,23 +76,21 @@ pub(crate) fn redundant_tuple_in_exception_handler(
|
||||
},
|
||||
type_.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
// If there's no space between the `except` and the tuple, we need to insert a space,
|
||||
// as in:
|
||||
// ```python
|
||||
// except(ValueError,):
|
||||
// ```
|
||||
// Otherwise, the output will be invalid syntax, since we're removing a set of
|
||||
// parentheses.
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
pad(
|
||||
checker.generator().expr(elt),
|
||||
type_.range(),
|
||||
checker.locator(),
|
||||
),
|
||||
// If there's no space between the `except` and the tuple, we need to insert a space,
|
||||
// as in:
|
||||
// ```python
|
||||
// except(ValueError,):
|
||||
// ```
|
||||
// Otherwise, the output will be invalid syntax, since we're removing a set of
|
||||
// parentheses.
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
pad(
|
||||
checker.generator().expr(elt),
|
||||
type_.range(),
|
||||
)));
|
||||
}
|
||||
checker.locator(),
|
||||
),
|
||||
type_.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ use ruff_python_codegen::Generator;
|
||||
use ruff_python_stdlib::identifiers::{is_identifier, is_mangled_private};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `setattr` that take a constant attribute value as an
|
||||
@@ -108,12 +107,10 @@ pub(crate) fn setattr_with_constant(
|
||||
{
|
||||
if expr == child.as_ref() {
|
||||
let mut diagnostic = Diagnostic::new(SetAttrWithConstant, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
assignment(obj, name, value, checker.generator()),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
assignment(obj, name, value, checker.generator()),
|
||||
expr.range(),
|
||||
)));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `hasattr` to test if an object is callable (e.g.,
|
||||
@@ -80,14 +79,12 @@ pub(crate) fn unreliable_callable_check(
|
||||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(UnreliableCallableCheck, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if id == "hasattr" {
|
||||
if checker.semantic().is_builtin("callable") {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
format!("callable({})", checker.locator().slice(obj)),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
if id == "hasattr" {
|
||||
if checker.semantic().is_builtin("callable") {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
format!("callable({})", checker.locator().slice(obj)),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
|
||||
@@ -8,7 +8,6 @@ use ruff_python_ast::{helpers, visitor};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, result_like::BoolLike)]
|
||||
enum Certainty {
|
||||
@@ -156,23 +155,21 @@ pub(crate) fn unused_loop_control_variable(checker: &mut Checker, stmt_for: &ast
|
||||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if let Some(rename) = rename {
|
||||
if certainty.into() {
|
||||
// Avoid fixing if the variable, or any future bindings to the variable, are
|
||||
// used _after_ the loop.
|
||||
let scope = checker.semantic().current_scope();
|
||||
if scope
|
||||
.get_all(name)
|
||||
.map(|binding_id| checker.semantic().binding(binding_id))
|
||||
.filter(|binding| binding.start() >= expr.start())
|
||||
.all(|binding| !binding.is_used())
|
||||
{
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
rename,
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
if let Some(rename) = rename {
|
||||
if certainty.into() {
|
||||
// Avoid fixing if the variable, or any future bindings to the variable, are
|
||||
// used _after_ the loop.
|
||||
let scope = checker.semantic().current_scope();
|
||||
if scope
|
||||
.get_all(name)
|
||||
.map(|binding_id| checker.semantic().binding(binding_id))
|
||||
.filter(|binding| binding.start() >= expr.start())
|
||||
.all(|binding| !binding.is_used())
|
||||
{
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
rename,
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,6 @@ use ruff_python_parser::Tok;
|
||||
use ruff_source_file::Locator;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::registry::Rule;
|
||||
use crate::settings::LinterSettings;
|
||||
|
||||
/// Simplified token type.
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
enum TokenType {
|
||||
@@ -225,7 +222,6 @@ pub(crate) fn trailing_commas(
|
||||
diagnostics: &mut Vec<Diagnostic>,
|
||||
tokens: &[LexResult],
|
||||
locator: &Locator,
|
||||
settings: &LinterSettings,
|
||||
) {
|
||||
let tokens = tokens
|
||||
.iter()
|
||||
@@ -324,9 +320,7 @@ pub(crate) fn trailing_commas(
|
||||
if comma_prohibited {
|
||||
let comma = prev.spanned.unwrap();
|
||||
let mut diagnostic = Diagnostic::new(ProhibitedTrailingComma, comma.1);
|
||||
if settings.rules.should_fix(Rule::ProhibitedTrailingComma) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(diagnostic.range())));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(diagnostic.range())));
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -359,17 +353,15 @@ pub(crate) fn trailing_commas(
|
||||
MissingTrailingComma,
|
||||
TextRange::empty(missing_comma.1.end()),
|
||||
);
|
||||
if settings.rules.should_fix(Rule::MissingTrailingComma) {
|
||||
// Create a replacement that includes the final bracket (or other token),
|
||||
// rather than just inserting a comma at the end. This prevents the UP034 fix
|
||||
// removing any brackets in the same linter pass - doing both at the same time could
|
||||
// lead to a syntax error.
|
||||
let contents = locator.slice(missing_comma.1);
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
format!("{contents},"),
|
||||
missing_comma.1,
|
||||
)));
|
||||
}
|
||||
// Create a replacement that includes the final bracket (or other token),
|
||||
// rather than just inserting a comma at the end. This prevents the UP034 fix
|
||||
// removing any brackets in the same linter pass - doing both at the same time could
|
||||
// lead to a syntax error.
|
||||
let contents = locator.slice(missing_comma.1);
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
format!("{contents},"),
|
||||
missing_comma.1,
|
||||
)));
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use ruff_python_ast::{self as ast, Expr};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
/// ## What it does
|
||||
@@ -82,19 +82,14 @@ pub(crate) fn unnecessary_call_around_sorted(
|
||||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
let edit = fixes::fix_unnecessary_call_around_sorted(
|
||||
expr,
|
||||
checker.locator(),
|
||||
checker.stylist(),
|
||||
)?;
|
||||
if outer.id == "reversed" {
|
||||
Ok(Fix::unsafe_edit(edit))
|
||||
} else {
|
||||
Ok(Fix::safe_edit(edit))
|
||||
}
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
let edit =
|
||||
fixes::fix_unnecessary_call_around_sorted(expr, checker.locator(), checker.stylist())?;
|
||||
if outer.id == "reversed" {
|
||||
Ok(Fix::unsafe_edit(edit))
|
||||
} else {
|
||||
Ok(Fix::safe_edit(edit))
|
||||
}
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use ruff_python_ast::{Expr, Keyword};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
use crate::rules::flake8_comprehensions::settings::Settings;
|
||||
|
||||
@@ -86,10 +86,8 @@ pub(crate) fn unnecessary_collection_call(
|
||||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_collection_call(expr, checker).map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_collection_call(expr, checker).map(Fix::unsafe_edit)
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use ruff_python_ast::{self as ast, Comprehension, Expr};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
/// ## What it does
|
||||
@@ -64,12 +64,10 @@ fn add_diagnostic(checker: &mut Checker, expr: &Expr) {
|
||||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_comprehension(expr, checker.locator(), checker.stylist())
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_comprehension(expr, checker.locator(), checker.stylist())
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ use ruff_python_ast::helpers::any_over_expr;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
/// ## What it does
|
||||
@@ -89,11 +89,9 @@ pub(crate) fn unnecessary_comprehension_any_all(
|
||||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryComprehensionAnyAll, arg.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_comprehension_any_all(expr, checker.locator(), checker.stylist())
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_comprehension_any_all(expr, checker.locator(), checker.stylist())
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ use ruff_python_ast::{self as ast, Arguments, Expr, Keyword};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
/// ## What it does
|
||||
@@ -130,16 +130,14 @@ pub(crate) fn unnecessary_double_cast_or_process(
|
||||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_double_cast_or_process(
|
||||
expr,
|
||||
checker.locator(),
|
||||
checker.stylist(),
|
||||
)
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_double_cast_or_process(
|
||||
expr,
|
||||
checker.locator(),
|
||||
checker.stylist(),
|
||||
)
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use ruff_python_ast::{self as ast, Expr, Keyword};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
use super::helpers;
|
||||
@@ -67,10 +67,7 @@ pub(crate) fn unnecessary_generator_dict(
|
||||
return;
|
||||
}
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryGeneratorDict, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_generator_dict(expr, checker).map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
diagnostic
|
||||
.try_set_fix(|| fixes::fix_unnecessary_generator_dict(expr, checker).map(Fix::unsafe_edit));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
use super::helpers;
|
||||
@@ -60,12 +60,10 @@ pub(crate) fn unnecessary_generator_list(
|
||||
}
|
||||
if let Expr::GeneratorExp(_) = argument {
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryGeneratorList, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_generator_list(expr, checker.locator(), checker.stylist())
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_generator_list(expr, checker.locator(), checker.stylist())
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
use super::helpers;
|
||||
@@ -60,11 +60,9 @@ pub(crate) fn unnecessary_generator_set(
|
||||
}
|
||||
if let Expr::GeneratorExp(_) = argument {
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryGeneratorSet, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_generator_set(expr, checker).map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_generator_set(expr, checker).map(Fix::unsafe_edit)
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
use super::helpers;
|
||||
@@ -56,11 +56,9 @@ pub(crate) fn unnecessary_list_call(
|
||||
return;
|
||||
}
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryListCall, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_list_call(expr, checker.locator(), checker.stylist())
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_list_call(expr, checker.locator(), checker.stylist())
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use ruff_python_ast::{self as ast, Expr, Keyword};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
use super::helpers;
|
||||
@@ -65,10 +65,8 @@ pub(crate) fn unnecessary_list_comprehension_dict(
|
||||
return;
|
||||
}
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryListComprehensionDict, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_list_comprehension_dict(expr, checker).map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_list_comprehension_dict(expr, checker).map(Fix::unsafe_edit)
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
use super::helpers;
|
||||
@@ -58,11 +58,9 @@ pub(crate) fn unnecessary_list_comprehension_set(
|
||||
}
|
||||
if argument.is_list_comp_expr() {
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryListComprehensionSet, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_list_comprehension_set(expr, checker).map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_list_comprehension_set(expr, checker).map(Fix::unsafe_edit)
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use ruff_python_ast::{self as ast, Expr, Keyword};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
use super::helpers;
|
||||
@@ -80,10 +80,7 @@ pub(crate) fn unnecessary_literal_dict(
|
||||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_literal_dict(expr, checker).map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
diagnostic
|
||||
.try_set_fix(|| fixes::fix_unnecessary_literal_dict(expr, checker).map(Fix::unsafe_edit));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
use super::helpers;
|
||||
@@ -75,10 +75,7 @@ pub(crate) fn unnecessary_literal_set(
|
||||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_literal_set(expr, checker).map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
diagnostic
|
||||
.try_set_fix(|| fixes::fix_unnecessary_literal_set(expr, checker).map(Fix::unsafe_edit));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
use super::helpers;
|
||||
@@ -91,15 +91,9 @@ pub(crate) fn unnecessary_literal_within_dict_call(
|
||||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_literal_within_dict_call(
|
||||
expr,
|
||||
checker.locator(),
|
||||
checker.stylist(),
|
||||
)
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_literal_within_dict_call(expr, checker.locator(), checker.stylist())
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use ruff_python_ast::{Expr, Keyword};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
use super::helpers;
|
||||
@@ -93,15 +93,9 @@ pub(crate) fn unnecessary_literal_within_list_call(
|
||||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_literal_within_list_call(
|
||||
expr,
|
||||
checker.locator(),
|
||||
checker.stylist(),
|
||||
)
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_literal_within_list_call(expr, checker.locator(), checker.stylist())
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
use super::helpers;
|
||||
@@ -95,15 +95,9 @@ pub(crate) fn unnecessary_literal_within_tuple_call(
|
||||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_literal_within_tuple_call(
|
||||
expr,
|
||||
checker.locator(),
|
||||
checker.stylist(),
|
||||
)
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_literal_within_tuple_call(expr, checker.locator(), checker.stylist())
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use ruff_python_ast::{self as ast, Arguments, Expr, ExprContext, Parameters, Stm
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::rules::flake8_comprehensions::fixes;
|
||||
|
||||
use super::helpers;
|
||||
@@ -221,18 +221,16 @@ pub(crate) fn unnecessary_map(
|
||||
};
|
||||
|
||||
let mut diagnostic = Diagnostic::new(UnnecessaryMap { object_type }, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_map(
|
||||
expr,
|
||||
parent,
|
||||
object_type,
|
||||
checker.locator(),
|
||||
checker.stylist(),
|
||||
)
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
fixes::fix_unnecessary_map(
|
||||
expr,
|
||||
parent,
|
||||
object_type,
|
||||
checker.locator(),
|
||||
checker.stylist(),
|
||||
)
|
||||
.map(Fix::unsafe_edit)
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ use ruff_python_ast::whitespace;
|
||||
use ruff_python_codegen::{Generator, Stylist};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::{AsRule, Rule};
|
||||
use crate::registry::Rule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for the use of string literals in exception constructors.
|
||||
@@ -190,30 +190,6 @@ pub(crate) fn string_in_exception(checker: &mut Checker, stmt: &Stmt, exc: &Expr
|
||||
if string.len() >= checker.settings.flake8_errmsg.max_string_length {
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(RawStringInException, first.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if let Some(indentation) =
|
||||
whitespace::indentation(checker.locator(), stmt)
|
||||
{
|
||||
if checker.semantic().is_available("msg") {
|
||||
diagnostic.set_fix(generate_fix(
|
||||
stmt,
|
||||
first,
|
||||
indentation,
|
||||
checker.stylist(),
|
||||
checker.generator(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check for f-strings.
|
||||
Expr::FString(_) => {
|
||||
if checker.enabled(Rule::FStringInException) {
|
||||
let mut diagnostic = Diagnostic::new(FStringInException, first.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if let Some(indentation) =
|
||||
whitespace::indentation(checker.locator(), stmt)
|
||||
{
|
||||
@@ -227,6 +203,25 @@ pub(crate) fn string_in_exception(checker: &mut Checker, stmt: &Stmt, exc: &Expr
|
||||
));
|
||||
}
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check for f-strings.
|
||||
Expr::FString(_) => {
|
||||
if checker.enabled(Rule::FStringInException) {
|
||||
let mut diagnostic = Diagnostic::new(FStringInException, first.range());
|
||||
if let Some(indentation) = whitespace::indentation(checker.locator(), stmt)
|
||||
{
|
||||
if checker.semantic().is_available("msg") {
|
||||
diagnostic.set_fix(generate_fix(
|
||||
stmt,
|
||||
first,
|
||||
indentation,
|
||||
checker.stylist(),
|
||||
checker.generator(),
|
||||
));
|
||||
}
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
@@ -240,19 +235,17 @@ pub(crate) fn string_in_exception(checker: &mut Checker, stmt: &Stmt, exc: &Expr
|
||||
if attr == "format" && value.is_constant_expr() {
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(DotFormatInException, first.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if let Some(indentation) =
|
||||
whitespace::indentation(checker.locator(), stmt)
|
||||
{
|
||||
if checker.semantic().is_available("msg") {
|
||||
diagnostic.set_fix(generate_fix(
|
||||
stmt,
|
||||
first,
|
||||
indentation,
|
||||
checker.stylist(),
|
||||
checker.generator(),
|
||||
));
|
||||
}
|
||||
if let Some(indentation) =
|
||||
whitespace::indentation(checker.locator(), stmt)
|
||||
{
|
||||
if checker.semantic().is_available("msg") {
|
||||
diagnostic.set_fix(generate_fix(
|
||||
stmt,
|
||||
first,
|
||||
indentation,
|
||||
checker.stylist(),
|
||||
checker.generator(),
|
||||
));
|
||||
}
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
|
||||
@@ -12,7 +12,6 @@ pub(crate) use shebang_not_executable::*;
|
||||
pub(crate) use shebang_not_first_line::*;
|
||||
|
||||
use crate::comments::shebang::ShebangDirective;
|
||||
use crate::settings::LinterSettings;
|
||||
|
||||
mod shebang_leading_whitespace;
|
||||
mod shebang_missing_executable_file;
|
||||
@@ -24,7 +23,6 @@ pub(crate) fn from_tokens(
|
||||
tokens: &[LexResult],
|
||||
path: &Path,
|
||||
locator: &Locator,
|
||||
settings: &LinterSettings,
|
||||
diagnostics: &mut Vec<Diagnostic>,
|
||||
) {
|
||||
let mut has_any_shebang = false;
|
||||
@@ -41,7 +39,7 @@ pub(crate) fn from_tokens(
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
if let Some(diagnostic) = shebang_leading_whitespace(*range, locator, settings) {
|
||||
if let Some(diagnostic) = shebang_leading_whitespace(*range, locator) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,6 @@ use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_trivia::is_python_whitespace;
|
||||
use ruff_source_file::Locator;
|
||||
|
||||
use crate::registry::AsRule;
|
||||
use crate::settings::LinterSettings;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for whitespace before a shebang directive.
|
||||
///
|
||||
@@ -50,7 +47,6 @@ impl AlwaysFixableViolation for ShebangLeadingWhitespace {
|
||||
pub(crate) fn shebang_leading_whitespace(
|
||||
range: TextRange,
|
||||
locator: &Locator,
|
||||
settings: &LinterSettings,
|
||||
) -> Option<Diagnostic> {
|
||||
// If the shebang is at the beginning of the file, abort.
|
||||
if range.start() == TextSize::from(0) {
|
||||
@@ -68,8 +64,6 @@ pub(crate) fn shebang_leading_whitespace(
|
||||
|
||||
let prefix = TextRange::up_to(range.start());
|
||||
let mut diagnostic = Diagnostic::new(ShebangLeadingWhitespace, prefix);
|
||||
if settings.rules.should_fix(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(prefix)));
|
||||
}
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(prefix)));
|
||||
Some(diagnostic)
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
use itertools::Itertools;
|
||||
use ruff_python_parser::lexer::LexResult;
|
||||
use ruff_python_parser::Tok;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::str::{leading_quote, trailing_quote};
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_python_parser::lexer::LexResult;
|
||||
use ruff_python_parser::Tok;
|
||||
use ruff_source_file::Locator;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::rules::flake8_implicit_str_concat::settings::Settings;
|
||||
use crate::settings::LinterSettings;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for implicitly concatenated strings on a single line.
|
||||
@@ -94,7 +94,7 @@ impl Violation for MultiLineImplicitStringConcatenation {
|
||||
pub(crate) fn implicit(
|
||||
diagnostics: &mut Vec<Diagnostic>,
|
||||
tokens: &[LexResult],
|
||||
settings: &Settings,
|
||||
settings: &LinterSettings,
|
||||
locator: &Locator,
|
||||
indexer: &Indexer,
|
||||
) {
|
||||
@@ -102,7 +102,9 @@ pub(crate) fn implicit(
|
||||
.iter()
|
||||
.flatten()
|
||||
.filter(|(tok, _)| {
|
||||
!tok.is_comment() && (settings.allow_multiline || !tok.is_non_logical_newline())
|
||||
!tok.is_comment()
|
||||
&& (settings.flake8_implicit_str_concat.allow_multiline
|
||||
|| !tok.is_non_logical_newline())
|
||||
})
|
||||
.tuple_windows()
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@ use ruff_python_semantic::{Binding, Imported};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
use crate::renamer::Renamer;
|
||||
|
||||
/// ## What it does
|
||||
@@ -79,16 +79,14 @@ pub(crate) fn unconventional_import_alias(
|
||||
},
|
||||
binding.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if !import.is_submodule_import() {
|
||||
if checker.semantic().is_available(expected_alias) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
let scope = &checker.semantic().scopes[binding.scope];
|
||||
let (edit, rest) =
|
||||
Renamer::rename(name, expected_alias, scope, checker.semantic())?;
|
||||
Ok(Fix::unsafe_edits(edit, rest))
|
||||
});
|
||||
}
|
||||
if !import.is_submodule_import() {
|
||||
if checker.semantic().is_available(expected_alias) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
let scope = &checker.semantic().scopes[binding.scope];
|
||||
let (edit, rest) =
|
||||
Renamer::rename(name, expected_alias, scope, checker.semantic())?;
|
||||
Ok(Fix::unsafe_edits(edit, rest))
|
||||
});
|
||||
}
|
||||
}
|
||||
Some(diagnostic)
|
||||
|
||||
@@ -5,7 +5,6 @@ use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::importer::ImportRequest;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for direct instantiation of `logging.Logger`, as opposed to using
|
||||
@@ -61,17 +60,15 @@ pub(crate) fn direct_logger_instantiation(checker: &mut Checker, call: &ast::Exp
|
||||
.is_some_and(|call_path| matches!(call_path.as_slice(), ["logging", "Logger"]))
|
||||
{
|
||||
let mut diagnostic = Diagnostic::new(DirectLoggerInstantiation, call.func.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
||||
&ImportRequest::import("logging", "getLogger"),
|
||||
call.func.start(),
|
||||
checker.semantic(),
|
||||
)?;
|
||||
let reference_edit = Edit::range_replacement(binding, call.func.range());
|
||||
Ok(Fix::unsafe_edits(import_edit, [reference_edit]))
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
||||
&ImportRequest::import("logging", "getLogger"),
|
||||
call.func.start(),
|
||||
checker.semantic(),
|
||||
)?;
|
||||
let reference_edit = Edit::range_replacement(binding, call.func.range());
|
||||
Ok(Fix::unsafe_edits(import_edit, [reference_edit]))
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ use ruff_python_ast::{self as ast, Expr};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for any usage of `__cached__` and `__file__` as an argument to
|
||||
@@ -80,13 +79,11 @@ pub(crate) fn invalid_get_logger_argument(checker: &mut Checker, call: &ast::Exp
|
||||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(InvalidGetLoggerArgument, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if checker.semantic().is_builtin("__name__") {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
"__name__".to_string(),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
if checker.semantic().is_builtin("__name__") {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
"__name__".to_string(),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::importer::ImportRequest;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `logging.WARN`.
|
||||
@@ -55,17 +54,15 @@ pub(crate) fn undocumented_warn(checker: &mut Checker, expr: &Expr) {
|
||||
.is_some_and(|call_path| matches!(call_path.as_slice(), ["logging", "WARN"]))
|
||||
{
|
||||
let mut diagnostic = Diagnostic::new(UndocumentedWarn, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.try_set_fix(|| {
|
||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
||||
&ImportRequest::import("logging", "WARNING"),
|
||||
expr.start(),
|
||||
checker.semantic(),
|
||||
)?;
|
||||
let reference_edit = Edit::range_replacement(binding, expr.range());
|
||||
Ok(Fix::safe_edits(import_edit, [reference_edit]))
|
||||
});
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
||||
&ImportRequest::import("logging", "WARNING"),
|
||||
expr.start(),
|
||||
checker.semantic(),
|
||||
)?;
|
||||
let reference_edit = Edit::range_replacement(binding, expr.range());
|
||||
Ok(Fix::safe_edits(import_edit, [reference_edit]))
|
||||
});
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user