Compare commits

..

6 Commits

Author SHA1 Message Date
Charlie Marsh
4a8b65b429 Remove pydantic.BaseModel from defaults 2023-03-05 18:21:34 -05:00
Charlie Marsh
5afe4bf711 Tweak interfaces to take Context 2023-03-05 18:17:49 -05:00
Charlie Marsh
b307200e42 Merge branch 'main' into sasanjac/2195-Implement-configuration-options-from-flake8-type-checking 2023-03-05 18:05:53 -05:00
Charlie Marsh
13f63072df Treat function type annotations within classes as runtime-required (#3312) 2023-03-03 09:08:30 +01:00
Sasan Jacob Rasti
bcba5f176d Refactor 2023-03-03 09:08:30 +01:00
Sasan Jacob Rasti
223a5b4631 Implement configuration options runtime-evaluated-decorators and runtime-evaluated-baseclasses for flake8-type-checking
see #2195
2023-03-03 09:07:40 +01:00
1260 changed files with 16527 additions and 28431 deletions

View File

@@ -1,6 +1,5 @@
[alias]
dev = "run --package ruff_dev --bin ruff_dev"
benchmark = "bench -p ruff_benchmark --"
[target.'cfg(all())']
rustflags = [

View File

@@ -1,11 +0,0 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "12:00"
timezone: "America/New_York"
commit-message:
prefix: "ci(deps)"

View File

@@ -17,6 +17,20 @@ env:
RUSTUP_MAX_RETRIES: 10
jobs:
cargo-build:
name: "cargo build"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: "Install Rust toolchain"
run: rustup show
- uses: Swatinem/rust-cache@v1
- run: cargo build --all
- run: ./target/debug/ruff_dev generate-all
- run: git diff --quiet README.md || echo "::error file=README.md::This file is outdated. Run 'cargo dev generate-all'."
- run: git diff --quiet ruff.schema.json || echo "::error file=ruff.schema.json::This file is outdated. Run 'cargo dev generate-all'."
- run: git diff --exit-code -- README.md ruff.schema.json docs
cargo-fmt:
name: "cargo fmt"
runs-on: ubuntu-latest
@@ -34,7 +48,7 @@ jobs:
- name: "Install Rust toolchain"
run: |
rustup component add clippy
- uses: Swatinem/rust-cache@v2
- uses: Swatinem/rust-cache@v1
- run: cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo-clippy-wasm:
@@ -46,8 +60,8 @@ jobs:
run: |
rustup component add clippy
rustup target add wasm32-unknown-unknown
- uses: Swatinem/rust-cache@v2
- run: cargo clippy -p ruff_wasm --target wasm32-unknown-unknown --all-features -- -D warnings
- uses: Swatinem/rust-cache@v1
- run: cargo clippy -p ruff --target wasm32-unknown-unknown --all-features -- -D warnings
cargo-test:
strategy:
@@ -59,9 +73,9 @@ jobs:
- uses: actions/checkout@v3
- name: "Install Rust toolchain"
run: rustup show
- uses: Swatinem/rust-cache@v2
- uses: Swatinem/rust-cache@v1
- run: cargo install cargo-insta
- run: pip install black[d]==23.1.0
- run: pip install black[d]==22.12.0
- name: "Run tests (Ubuntu)"
if: ${{ matrix.os == 'ubuntu-latest' }}
run: |
@@ -79,30 +93,6 @@ jobs:
env:
# Setting RUSTDOCFLAGS because `cargo doc --check` isn't yet implemented (https://github.com/rust-lang/cargo/issues/10025).
RUSTDOCFLAGS: "-D warnings"
- uses: actions/upload-artifact@v3
with:
name: ruff
path: target/debug/ruff
cargo-test-wasm:
runs-on: ubuntu-latest
name: "cargo test (wasm)"
steps:
- uses: actions/checkout@v3
- name: "Install Rust toolchain"
run: rustup target add wasm32-unknown-unknown
- uses: actions/setup-node@v3
with:
node-version: 18
cache: "npm"
cache-dependency-path: playground/package-lock.json
- uses: jetli/wasm-pack-action@v0.4.0
- uses: Swatinem/rust-cache@v2
- name: "Run wasm-pack"
run: |
cd crates/ruff_wasm
wasm-pack test --node
scripts:
name: "test scripts"
@@ -111,7 +101,7 @@ jobs:
- uses: actions/checkout@v3
- name: "Install Rust toolchain"
run: rustup show
- uses: Swatinem/rust-cache@v2
- uses: Swatinem/rust-cache@v1
- run: ./scripts/add_rule.py --name DoTheThing --code PLC999 --linter pylint
- run: cargo check
- run: |
@@ -127,39 +117,3 @@ jobs:
- uses: crate-ci/typos@master
with:
files: .
ecosystem:
name: "ecosystem"
runs-on: ubuntu-latest
needs: cargo-test
# Only runs on pull requests, since that is the only we way we can find the base version for comparison.
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: "3.11"
- uses: actions/download-artifact@v3
id: ruff-target
with:
name: ruff
path: target/debug
- uses: dawidd6/action-download-artifact@v2
with:
name: ruff
branch: ${{ github.event.pull_request.base.ref }}
check_artifacts: true
- name: Run ecosystem check
run: |
# Make executable, since artifact download doesn't preserve this
chmod +x ruff ${{ steps.ruff-target.outputs.download-path }}/ruff
scripts/check_ecosystem.py ruff ${{ steps.ruff-target.outputs.download-path }}/ruff | tee ecosystem-result
echo ${{ github.event.number }} > pr-number
- uses: actions/upload-artifact@v3
with:
name: ecosystem-result
path: |
ecosystem-result
pr-number

View File

@@ -15,7 +15,7 @@ jobs:
- uses: actions/setup-python@v4
- name: "Install Rust toolchain"
run: rustup show
- uses: Swatinem/rust-cache@v2
- uses: Swatinem/rust-cache@v1
- name: "Install dependencies"
run: |
pip install -r docs/requirements.txt

View File

@@ -1,31 +0,0 @@
on:
workflow_run:
workflows: [CI]
types: [completed]
permissions:
pull-requests: write
jobs:
comment:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dawidd6/action-download-artifact@v2
id: download-result
with:
name: ecosystem-result
workflow: ci.yaml
run_id: ${{ github.event.workflow_run.id }}
if_no_artifact_found: ignore
- if: steps.download-result.outputs.found_artifact
id: result
run: |
echo "pr-number=$(<pr-number)" >> $GITHUB_OUTPUT
- name: Create comment
if: steps.download-result.outputs.found_artifact
uses: thollander/actions-comment-pull-request@v2
with:
pr_number: ${{ steps.result.outputs.pr-number }}
filePath: ecosystem-result
comment_tag: ecosystem-results

View File

@@ -133,7 +133,7 @@ jobs:
target: ${{ matrix.target }}
manylinux: auto
args: --no-default-features --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
- uses: uraimo/run-on-arch-action@v2
- uses: uraimo/run-on-arch-action@v2.5.0
if: matrix.target != 'ppc64'
name: Install built wheel
with:
@@ -206,7 +206,7 @@ jobs:
target: ${{ matrix.platform.target }}
manylinux: musllinux_1_2
args: --release --out dist -m ./${{ env.CRATE_NAME }}/Cargo.toml
- uses: uraimo/run-on-arch-action@v2
- uses: uraimo/run-on-arch-action@master
name: Install built wheel
with:
arch: ${{ matrix.platform.arch }}

View File

@@ -28,7 +28,7 @@ jobs:
- uses: jetli/wasm-pack-action@v0.4.0
- uses: jetli/wasm-bindgen-action@v0.2.0
- name: "Run wasm-pack"
run: wasm-pack build --target web --out-dir ../../playground/src/pkg crates/ruff_wasm
run: wasm-pack build --target web --out-dir ../../playground/src/pkg crates/ruff
- name: "Install Node dependencies"
run: npm ci
working-directory: playground

View File

@@ -208,7 +208,7 @@ jobs:
target: ${{ matrix.platform.target }}
manylinux: auto
args: --release --out dist
- uses: uraimo/run-on-arch-action@v2
- uses: uraimo/run-on-arch-action@v2.5.0
if: matrix.platform.arch != 'ppc64'
name: Install built wheel
with:
@@ -309,7 +309,7 @@ jobs:
target: ${{ matrix.platform.target }}
manylinux: musllinux_1_2
args: --release --out dist
- uses: uraimo/run-on-arch-action@v2
- uses: uraimo/run-on-arch-action@master
name: Install built wheel
with:
arch: ${{ matrix.platform.arch }}

View File

@@ -1,7 +1,7 @@
fail_fast: true
repos:
- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.12.1
rev: v0.10.1
hooks:
- id: validate-pyproject

View File

@@ -104,7 +104,7 @@ At a high level, the steps involved in adding a new lint rule are as follows:
1. Determine a name for the new rule as per our [rule naming convention](#rule-naming-convention).
1. Create a file for your rule (e.g., `crates/ruff/src/rules/flake8_bugbear/rules/abstract_base_class.rs`).
1. In that file, define a violation struct. You can grep for `#[violation]` to see examples.
1. In that file, define a violation struct. You can grep for `define_violation!` to see examples.
1. Map the violation struct to a rule code in `crates/ruff/src/registry.rs` (e.g., `E402`).
1. Define the logic for triggering the violation in `crates/ruff/src/checkers/ast.rs` (for AST-based
checks), `crates/ruff/src/checkers/tokens.rs` (for token-based checks), `crates/ruff/src/checkers/lines.rs`
@@ -115,7 +115,7 @@ At a high level, the steps involved in adding a new lint rule are as follows:
To define the violation, start by creating a dedicated file for your rule under the appropriate
rule linter (e.g., `crates/ruff/src/rules/flake8_bugbear/rules/abstract_base_class.rs`). That file should
contain a struct defined via `#[violation]`, along with a function that creates the violation
contain a struct defined via `define_violation!`, along with a function that creates the violation
based on any required inputs. (Many of the existing examples live in `crates/ruff/src/violations.rs`,
but we're looking to place new rules in their own files.)

198
Cargo.lock generated
View File

@@ -313,14 +313,13 @@ dependencies = [
[[package]]
name = "clap_complete_command"
version = "0.5.1"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "183495371ea78d4c9ff638bfc6497d46fed2396e4f9c50aebc1278a4a9919a3d"
checksum = "4160b4a4f72ef58bd766bad27c09e6ef1cc9d82a22f6a0f55d152985a4a48e31"
dependencies = [
"clap 4.1.8",
"clap_complete",
"clap_complete_fig",
"clap_complete_nushell",
]
[[package]]
@@ -333,16 +332,6 @@ dependencies = [
"clap_complete",
]
[[package]]
name = "clap_complete_nushell"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7fa41f5e6aa83bd151b70fd0ceaee703d68cd669522795dc812df9edad1252c"
dependencies = [
"clap 4.1.8",
"clap_complete",
]
[[package]]
name = "clap_derive"
version = "4.1.8"
@@ -565,16 +554,6 @@ dependencies = [
"typenum",
]
[[package]]
name = "ctor"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "cxx"
version = "1.0.91"
@@ -619,6 +598,17 @@ dependencies = [
"syn",
]
[[package]]
name = "derivative"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "diff"
version = "0.1.13"
@@ -780,7 +770,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8-to-ruff"
version = "0.0.256"
version = "0.0.254"
dependencies = [
"anyhow",
"clap 4.1.8",
@@ -1458,15 +1448,6 @@ version = "6.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
[[package]]
name = "output_vt100"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66"
dependencies = [
"winapi",
]
[[package]]
name = "parking_lot"
version = "0.12.1"
@@ -1514,12 +1495,6 @@ dependencies = [
"once_cell",
]
[[package]]
name = "pathdiff"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
[[package]]
name = "peg"
version = "0.8.1"
@@ -1547,18 +1522,6 @@ version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa00462b37ead6d11a82c9d568b26682d78e0477dc02d1966c013af80969739"
[[package]]
name = "pep440_rs"
version = "0.2.0"
source = "git+https://github.com/konstin/pep440-rs.git?rev=a8fef4ec47f4c25b070b39cdbe6a0b9847e49941#a8fef4ec47f4c25b070b39cdbe6a0b9847e49941"
dependencies = [
"lazy_static",
"regex",
"serde",
"tracing",
"unicode-width",
]
[[package]]
name = "percent-encoding"
version = "2.2.0"
@@ -1756,18 +1719,6 @@ dependencies = [
"termtree",
]
[[package]]
name = "pretty_assertions"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755"
dependencies = [
"ctor",
"diff",
"output_vt100",
"yansi",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
@@ -1982,17 +1933,22 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.256"
version = "0.0.254"
dependencies = [
"anyhow",
"bisection",
"bitflags",
"cfg-if",
"chrono",
"clap 4.1.8",
"colored",
"console_error_panic_hook",
"console_log",
"criterion",
"derivative",
"dirs",
"fern",
"getrandom",
"glob",
"globset",
"ignore",
@@ -2000,6 +1956,7 @@ dependencies = [
"insta",
"is-macro",
"itertools",
"js-sys",
"libcst",
"log",
"natord",
@@ -2008,16 +1965,11 @@ dependencies = [
"num-traits",
"once_cell",
"path-absolutize",
"pathdiff",
"pep440_rs",
"pretty_assertions",
"regex",
"result-like",
"ruff_cache",
"ruff_diagnostics",
"ruff_macros",
"ruff_python_ast",
"ruff_python_stdlib",
"ruff_python",
"ruff_rustpython",
"rustc-hash",
"rustpython-common",
@@ -2025,6 +1977,7 @@ dependencies = [
"schemars",
"semver",
"serde",
"serde-wasm-bindgen",
"shellexpand",
"smallvec",
"strum",
@@ -2033,18 +1986,8 @@ dependencies = [
"textwrap",
"thiserror",
"toml",
]
[[package]]
name = "ruff_benchmark"
version = "0.0.0"
dependencies = [
"criterion",
"mimalloc",
"ruff",
"tikv-jemallocator",
"ureq",
"url",
"wasm-bindgen",
"wasm-bindgen-test",
]
[[package]]
@@ -2060,7 +2003,7 @@ dependencies = [
[[package]]
name = "ruff_cli"
version = "0.0.256"
version = "0.0.254"
dependencies = [
"annotate-snippets 0.9.1",
"anyhow",
@@ -2087,7 +2030,6 @@ dependencies = [
"regex",
"ruff",
"ruff_cache",
"ruff_diagnostics",
"rustc-hash",
"serde",
"serde_json",
@@ -2109,11 +2051,9 @@ dependencies = [
"itertools",
"libcst",
"once_cell",
"pretty_assertions",
"regex",
"ruff",
"ruff_cli",
"ruff_diagnostics",
"rustpython-common",
"rustpython-parser",
"schemars",
@@ -2123,15 +2063,6 @@ dependencies = [
"textwrap",
]
[[package]]
name = "ruff_diagnostics"
version = "0.0.0"
dependencies = [
"ruff_python_ast",
"rustpython-parser",
"serde",
]
[[package]]
name = "ruff_formatter"
version = "0.0.0"
@@ -2158,25 +2089,12 @@ dependencies = [
]
[[package]]
name = "ruff_python_ast"
name = "ruff_python"
version = "0.0.0"
dependencies = [
"anyhow",
"bitflags",
"is-macro",
"itertools",
"log",
"nohash-hasher",
"num-bigint",
"num-traits",
"once_cell",
"regex",
"ruff_python_stdlib",
"ruff_rustpython",
"rustc-hash",
"rustpython-common",
"rustpython-parser",
"smallvec",
]
[[package]]
@@ -2190,8 +2108,7 @@ dependencies = [
"itertools",
"once_cell",
"ruff_formatter",
"ruff_python_ast",
"ruff_python_stdlib",
"ruff_python",
"ruff_rustpython",
"ruff_testing_macros",
"ruff_text_size",
@@ -2202,15 +2119,6 @@ dependencies = [
"test-case",
]
[[package]]
name = "ruff_python_stdlib"
version = "0.0.0"
dependencies = [
"once_cell",
"regex",
"rustc-hash",
]
[[package]]
name = "ruff_rustpython"
version = "0.0.0"
@@ -2226,6 +2134,7 @@ name = "ruff_testing_macros"
version = "0.0.0"
dependencies = [
"glob",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
@@ -2241,25 +2150,6 @@ dependencies = [
"static_assertions",
]
[[package]]
name = "ruff_wasm"
version = "0.0.0"
dependencies = [
"console_error_panic_hook",
"console_log",
"getrandom",
"js-sys",
"log",
"ruff",
"ruff_python_ast",
"ruff_rustpython",
"rustpython-parser",
"serde",
"serde-wasm-bindgen",
"wasm-bindgen",
"wasm-bindgen-test",
]
[[package]]
name = "rust-stemmers"
version = "1.2.0"
@@ -2305,7 +2195,7 @@ dependencies = [
[[package]]
name = "rustpython-ast"
version = "0.2.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=c15f670f2c30cfae6b41a1874893590148c74bc4#c15f670f2c30cfae6b41a1874893590148c74bc4"
source = "git+https://github.com/RustPython/RustPython.git?rev=1871a1632e310985414211222f5bf8069678892f#1871a1632e310985414211222f5bf8069678892f"
dependencies = [
"num-bigint",
"rustpython-compiler-core",
@@ -2314,7 +2204,7 @@ dependencies = [
[[package]]
name = "rustpython-common"
version = "0.2.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=c15f670f2c30cfae6b41a1874893590148c74bc4#c15f670f2c30cfae6b41a1874893590148c74bc4"
source = "git+https://github.com/RustPython/RustPython.git?rev=1871a1632e310985414211222f5bf8069678892f#1871a1632e310985414211222f5bf8069678892f"
dependencies = [
"ascii",
"bitflags",
@@ -2339,7 +2229,7 @@ dependencies = [
[[package]]
name = "rustpython-compiler-core"
version = "0.2.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=c15f670f2c30cfae6b41a1874893590148c74bc4#c15f670f2c30cfae6b41a1874893590148c74bc4"
source = "git+https://github.com/RustPython/RustPython.git?rev=1871a1632e310985414211222f5bf8069678892f#1871a1632e310985414211222f5bf8069678892f"
dependencies = [
"bitflags",
"bstr 0.2.17",
@@ -2353,7 +2243,7 @@ dependencies = [
[[package]]
name = "rustpython-parser"
version = "0.2.0"
source = "git+https://github.com/RustPython/RustPython.git?rev=c15f670f2c30cfae6b41a1874893590148c74bc4#c15f670f2c30cfae6b41a1874893590148c74bc4"
source = "git+https://github.com/RustPython/RustPython.git?rev=1871a1632e310985414211222f5bf8069678892f#1871a1632e310985414211222f5bf8069678892f"
dependencies = [
"ahash",
"anyhow",
@@ -2465,9 +2355,9 @@ dependencies = [
[[package]]
name = "serde-wasm-bindgen"
version = "0.5.0"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e"
checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf"
dependencies = [
"js-sys",
"serde",
@@ -2878,21 +2768,9 @@ checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
dependencies = [
"cfg-if",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.30"
@@ -3388,12 +3266,6 @@ dependencies = [
"linked-hash-map",
]
[[package]]
name = "yansi"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
[[package]]
name = "yansi-term"
version = "0.1.2"

View File

@@ -3,11 +3,7 @@ members = ["crates/*"]
[workspace.package]
edition = "2021"
rust-version = "1.67"
homepage = "https://beta.ruff.rs/docs/"
documentation = "https://beta.ruff.rs/docs/"
repository = "https://github.com/charliermarsh/ruff"
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
rust-version = "1.67.0"
[workspace.dependencies]
anyhow = { version = "1.0.69" }
@@ -30,11 +26,11 @@ proc-macro2 = { version = "1.0.51" }
quote = { version = "1.0.23" }
regex = { version = "1.7.1" }
rustc-hash = { version = "1.1.0" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "c15f670f2c30cfae6b41a1874893590148c74bc4" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "1871a1632e310985414211222f5bf8069678892f" }
rustpython-parser = { features = [
"lalrpop",
"serde",
], git = "https://github.com/RustPython/RustPython.git", rev = "c15f670f2c30cfae6b41a1874893590148c74bc4" }
], git = "https://github.com/RustPython/RustPython.git", rev = "1871a1632e310985414211222f5bf8069678892f" }
schemars = { version = "0.8.12" }
serde = { version = "1.0.152", features = ["derive"] }
serde_json = { version = "1.0.93" }
@@ -63,9 +59,3 @@ opt-level = 3
# https://github.com/bytecodealliance/wasm-tools/blob/b5c3d98e40590512a3b12470ef358d5c7b983b15/crates/wasmparser/src/limits.rs#L29
[profile.dev.package.rustpython-parser]
opt-level = 1
# Use the `--profile release-debug` flag to show symbols in release mode.
# e.g. `cargo build --profile release-debug`
[profile.release-debug]
inherits = "release"
debug = 1

View File

@@ -137,7 +137,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com) hook:
```yaml
- repo: https://github.com/charliermarsh/ruff-pre-commit
# Ruff version.
rev: 'v0.0.256'
rev: 'v0.0.254'
hooks:
- id: ruff
```
@@ -306,9 +306,6 @@ Ruff is used in a number of major open-source projects, including:
- [meson-python](https://github.com/mesonbuild/meson-python)
- [ZenML](https://github.com/zenml-io/zenml)
- [delta-rs](https://github.com/delta-io/delta-rs)
- [Starlite](https://github.com/starlite-api/starlite)
- [telemetry-airflow (Mozilla)](https://github.com/mozilla/telemetry-airflow)
- [Stable Baselines3](https://github.com/DLR-RM/stable-baselines3)
## License

View File

@@ -1,18 +1,17 @@
[package]
name = "flake8-to-ruff"
version = "0.0.256"
version = "0.0.254"
edition = { workspace = true }
rust-version = { workspace = true }
[dependencies]
ruff = { path = "../ruff", default-features = false }
anyhow = { workspace = true }
clap = { workspace = true }
colored = { workspace = true }
configparser = { version = "3.0.2" }
once_cell = { workspace = true }
regex = { workspace = true }
ruff = { path = "../ruff", default-features = false }
rustc-hash = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }

View File

@@ -46,15 +46,8 @@ fn main() -> Result<()> {
.map(|tool| ExternalConfig {
black: tool.black.as_ref(),
isort: tool.isort.as_ref(),
..Default::default()
})
.unwrap_or_default();
let external_config = ExternalConfig {
project: pyproject
.as_ref()
.and_then(|pyproject| pyproject.project.as_ref()),
..external_config
};
// Create Ruff's pyproject.toml section.
let pyproject = flake8_to_ruff::convert(&config, &external_config, args.plugin)?;

View File

@@ -1,32 +1,34 @@
[package]
name = "ruff"
version = "0.0.256"
authors.workspace = true
edition.workspace = true
rust-version.workspace = true
documentation.workspace = true
homepage.workspace = true
repository.workspace = true
version = "0.0.254"
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
edition = { workspace = true }
rust-version = { workspace = true }
documentation = "https://github.com/charliermarsh/ruff"
homepage = "https://github.com/charliermarsh/ruff"
repository = "https://github.com/charliermarsh/ruff"
readme = "README.md"
license = "MIT"
[lib]
name = "ruff"
crate-type = ["cdylib", "rlib"]
doctest = false
[dependencies]
ruff_cache = { path = "../ruff_cache" }
ruff_diagnostics = { path = "../ruff_diagnostics", features = ["serde"] }
ruff_macros = { path = "../ruff_macros" }
ruff_python_ast = { path = "../ruff_python_ast" }
ruff_python_stdlib = { path = "../ruff_python_stdlib" }
ruff_python = { path = "../ruff_python" }
ruff_rustpython = { path = "../ruff_rustpython" }
ruff_cache = { path = "../ruff_cache" }
anyhow = { workspace = true }
bisection = { version = "0.1.0" }
bitflags = { workspace = true }
cfg-if = { version = "1.0.0" }
chrono = { workspace = true }
clap = { workspace = true, features = ["derive", "string"], optional = true }
clap = { workspace = true, features = ["derive", "env", "string"] }
colored = { workspace = true }
derivative = { version = "2.2.0" }
dirs = { version = "4.0.0" }
fern = { version = "0.6.1" }
glob = { workspace = true }
@@ -46,10 +48,6 @@ path-absolutize = { workspace = true, features = [
"once_cell_cache",
"use_unix_paths_on_wasm",
] }
pathdiff = { version = "0.2.1" }
pep440_rs = { git = "https://github.com/konstin/pep440-rs.git", features = [
"serde",
], rev = "a8fef4ec47f4c25b070b39cdbe6a0b9847e49941" }
regex = { workspace = true }
result-like = { version = "0.4.6" }
rustc-hash = { workspace = true }
@@ -66,11 +64,22 @@ textwrap = { workspace = true }
thiserror = { version = "1.0.38" }
toml = { workspace = true }
# https://docs.rs/getrandom/0.2.7/getrandom/#webassembly-support
[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies]
getrandom = { version = "0.2.8", features = ["js"] }
console_error_panic_hook = { version = "0.1.7" }
console_log = { version = "0.2.1" }
serde-wasm-bindgen = { version = "0.4.5" }
js-sys = { version = "0.3.61" }
wasm-bindgen = { version = "0.2.84" }
[dev-dependencies]
criterion = { version = "0.4.0" }
insta = { workspace = true, features = ["yaml", "redactions"] }
pretty_assertions = "1.3.0"
test-case = { workspace = true }
wasm-bindgen-test = { version = "0.3.34" }
[target.'cfg(not(target_family = "wasm"))'.dev-dependencies]
criterion = { version = "0.4.0" }
[features]
default = []

View File

@@ -1,47 +0,0 @@
"""
Should emit:
B030:
- line 12, column 8
- line 17, column 9
- line 22, column 21
- line 27, column 37
"""
try:
pass
except 1: # error
pass
try:
pass
except (1, ValueError): # error
pass
try:
pass
except (ValueError, (RuntimeError, (KeyError, TypeError))): # error
pass
try:
pass
except (ValueError, *(RuntimeError, (KeyError, TypeError))): # error
pass
try:
pass
except (ValueError, *(RuntimeError, TypeError)): # ok
pass
try:
pass
except (ValueError, *[RuntimeError, *(TypeError,)]): # ok
pass
def what_to_catch():
return ...
try:
pass
except what_to_catch(): # ok
pass

View File

@@ -8,9 +8,3 @@ any({x.id for x in bar})
# PIE 802
any([x.id for x in bar])
all([x.id for x in bar])
any( # first comment
[x.id for x in bar], # second comment
) # third comment
all( # first comment
[x.id for x in bar], # second comment
) # third comment

View File

@@ -6,8 +6,6 @@ obj.endswith("foo") or obj.endswith("bar")
obj.startswith(foo) or obj.startswith(bar)
# error
obj.startswith(foo) or obj.startswith("foo")
# error
obj.endswith(foo) or obj.startswith(foo) or obj.startswith("foo")
# ok
obj.startswith(("foo", "bar"))

View File

@@ -1,10 +1,3 @@
import math
import os
import sys
from math import inf
import numpy as np
def f12(
x,
y: str = os.pathsep, # Error PYI011 Only simple default values allowed for typed arguments
@@ -68,49 +61,3 @@ def f22(
x: complex = -42.5j # Error PYI011 Only simple default values allowed for typed arguments
+ 4.3j,
) -> None: ...
def f23(
x: bool = True, # OK
) -> None: ...
def f24(
x: float = 3.14, # OK
) -> None: ...
def f25(
x: float = -3.14, # OK
) -> None: ...
def f26(
x: complex = -3.14j, # OK
) -> None: ...
def f27(
x: complex = -3 - 3.14j, # OK
) -> None: ...
def f28(
x: float = math.tau, # OK
) -> None: ...
def f29(
x: float = math.inf, # OK
) -> None: ...
def f30(
x: float = -math.inf, # OK
) -> None: ...
def f31(
x: float = inf, # Error PYI011 Only simple default values allowed for typed arguments
) -> None: ...
def f32(
x: float = np.inf, # Error PYI011 Only simple default values allowed for typed arguments
) -> None: ...
def f33(
x: float = math.nan, # OK
) -> None: ...
def f34(
x: float = -math.nan, # Error PYI011 Only simple default values allowed for typed arguments
) -> None: ...
def f35(
x: complex = math.inf # Error PYI011 Only simple default values allowed for typed arguments
+ 1j,
) -> None: ...
def f36(
*, x: str = sys.version, # OK
) -> None: ...
def f37(
*, x: str = "" + "", # Error PYI011 Only simple default values allowed for typed arguments
) -> None: ...

View File

@@ -43,6 +43,3 @@ def f21(
def f22(
x=-42.5j + 4.3j, # Error PYI014
) -> None: ...
def f23(
x=True, # OK
) -> None: ...

View File

@@ -6,6 +6,22 @@ def x():
return a # error
def x():
a = 1
print(a)
a = 2
return a # error
def x():
a = 1
if True:
return a # error
a = 2
print(a)
return a
# Can be refactored false positives
# https://github.com/afonasev/flake8-return/issues/47#issuecomment-1122571066
def get_bar_if_exists(obj):
@@ -149,6 +165,44 @@ def my_func():
return foo
# Refactored incorrect false positives
# See test cases above marked: "Can be refactored false positives"
# https://github.com/afonasev/flake8-return/issues/47#issuecomment-1122571066
def get_bar_if_exists(obj):
if hasattr(obj, "bar"):
return str(obj.bar)
return ""
# https://github.com/afonasev/flake8-return/issues/47#issue-641117366
def x():
formatted = _USER_AGENT_FORMATTER.format(format_string, **values)
# clean up after any blank components
return formatted.replace("()", "").replace(" ", " ").strip()
# https://github.com/afonasev/flake8-return/issues/47#issue-641117366
def user_agent_username(username=None):
if not username:
return ""
username = username.replace(" ", "_") # Avoid spaces or %20.
try:
username.encode("ascii") # just test,
# but not actually use it
except UnicodeEncodeError:
username = quote(username.encode("utf-8"))
else:
# % is legal in the default $wgLegalTitleChars
# This is so that ops know the real pywikibot will not
# allow a useragent in the username to allow through a
# hand-coded percent-encoded value.
if "%" in username:
username = quote(username)
return username
# https://github.com/afonasev/flake8-return/issues/116#issue-1367575481
def no_exception_loop():
success = False
@@ -206,44 +260,3 @@ def nonlocal_assignment():
nonlocal X
X = 1
return X
def decorator() -> Flask:
app = Flask(__name__)
@app.route('/hello')
def hello() -> str:
"""Hello endpoint."""
return 'Hello, World!'
return app
def default():
y = 1
def f(x = y) -> X:
return x
return y
def get_queryset(option_1, option_2):
queryset: Any = None
queryset = queryset.filter(a=1)
if option_1:
queryset = queryset.annotate(b=Value(2))
if option_2:
queryset = queryset.filter(c=3)
return queryset
def get_queryset():
queryset = Model.filter(a=1)
queryset = queryset.filter(c=3)
return queryset
def get_queryset():
queryset = Model.filter(a=1)
return queryset # error

View File

@@ -1,3 +0,0 @@
from typing import Tuple
x: Tuple

View File

@@ -1,10 +1,5 @@
import sys, math
from os import path, uname
from json import detect_encoding
from json import dump
from json import dumps as json_dumps
from json import load
from json import loads as json_loads
from logging.handlers import StreamHandler, FileHandler
# comment 1
@@ -15,10 +10,9 @@ from third_party import lib4
from foo import bar # comment 3
from foo2 import bar2 # comment 4
from foo3 import bar3, baz3 # comment 5
# comment 6
# comment 5
from bar import (
a, # comment 7
b, # comment 8
)
a, # comment 6
b, # comment 7
)

View File

@@ -1,10 +0,0 @@
# ruff: isort: skip_file
import e
import f
# isort: split
import a
import b
import c
import d

View File

@@ -6,14 +6,6 @@ def f():
# isort: on
def f():
# ruff: isort: off
import sys
import os
import collections
# ruff: isort: on
def f():
import sys
import os # isort: skip

View File

@@ -1 +0,0 @@
''' SAM macro definitions '''

View File

@@ -1,19 +0,0 @@
x = "a string"
y = "another string"
z = ""
def errors():
if x is "" or x == "":
print("x is an empty string")
if y is not "" or y != "":
print("y is not an empty string")
if "" != z:
print("z is an empty string")
def ok():
if x and not y:
print("x is not an empty string, but y is an empty string")

View File

@@ -1,12 +0,0 @@
import os
tempVar = os.getenv("TEST", 12) # [invalid-envvar-default]
goodVar = os.getenv("TESTING", None)
dictVarBad = os.getenv("AAA", {"a", 7}) # [invalid-envvar-default]
print(os.getenv("TEST", False)) # [invalid-envvar-default]
os.getenv("AA", "GOOD")
os.getenv("AA", f"GOOD")
os.getenv("AA", "GOOD" + "BAD")
os.getenv("AA", "GOOD" + 1)
os.getenv("AA", "GOOD %s" % "BAD")
os.getenv("B", Z)

View File

@@ -1,15 +0,0 @@
import os
os.getenv(1) # [invalid-envvar-value]
os.getenv("a")
os.getenv("test")
os.getenv(key="testingAgain")
os.getenv(key=11) # [invalid-envvar-value]
os.getenv(["hello"]) # [invalid-envvar-value]
os.getenv(key="foo", default="bar")
os.getenv(key=f"foo", default="bar")
os.getenv(key="foo" + "bar", default=1)
os.getenv(key=1 + "bar", default=1) # [invalid-envvar-value]
AA = "aa"
os.getenv(AA)

View File

@@ -12,17 +12,11 @@ Ipsum
""".encode(
"utf-8"
)
(
"Lorem "
"Ipsum".encode()
)
(
"Lorem " # Comment
"Ipsum".encode() # Comment
)
(
"Lorem " "Ipsum".encode()
)
# b"""
# Lorem
#
# Ipsum
# """
# `encode` on variables should not be processed.
string = "hello there"

View File

@@ -1,10 +0,0 @@
import socket
from kombu import Connection, exceptions
try:
conn = Connection(settings.CELERY_BROKER_URL)
conn.ensure_connection(max_retries=2)
conn._close()
except (socket.error, exceptions.OperationalError):
return HttpResponseServerError("cache: cannot connect to broker.")

View File

@@ -41,7 +41,7 @@ if True:
Good,
)
from typing import Callable, Match, Pattern, List, OrderedDict, AbstractSet, ContextManager
from typing import Callable, Match, Pattern, List
if True: from collections import (
Mapping, Counter)

View File

@@ -5,5 +5,3 @@ isinstance(1, int) # OK
issubclass("yes", int) # OK
isinstance(1, int | float) # OK
issubclass("yes", int | str) # OK
isinstance(1, ()) # OK
isinstance(1, (int, *(str, bytes))) # OK

View File

@@ -1,13 +0,0 @@
# noqa
# noqa # comment
print() # noqa
print() # noqa # comment
print(a) # noqa
print(a) # noqa # comment
# noqa: E501, F821
# noqa: E501, F821 # comment
print() # noqa: E501, F821
print() # noqa: E501, F821 # comment
print(a) # noqa: E501, F821
print(a) # noqa: E501, F821 # comment

View File

@@ -4,7 +4,7 @@ use rustc_hash::FxHashMap;
use rustpython_parser::ast::ExcepthandlerKind::ExceptHandler;
use rustpython_parser::ast::{Stmt, StmtKind};
use crate::types::RefEquality;
use crate::ast::types::RefEquality;
/// Return the common ancestor of `left` and `right` below `stop`, or `None`.
fn common_ancestor<'a>(

View File

@@ -1,14 +1,13 @@
//! An equivalent object hierarchy to the [`Expr`] hierarchy, but with the
//! ability to compare expressions for equality (via [`Eq`] and [`Hash`]).
use num_bigint::BigInt;
use rustpython_parser::ast::{
Alias, Arg, Arguments, Boolop, Cmpop, Comprehension, Constant, Excepthandler,
ExcepthandlerKind, Expr, ExprContext, ExprKind, Keyword, MatchCase, Operator, Pattern,
PatternKind, Stmt, StmtKind, Unaryop, Withitem,
};
use num_bigint::BigInt;
#[derive(Debug, PartialEq, Eq, Hash)]
pub enum ComparableExprContext {
Load,

View File

@@ -5,11 +5,11 @@ use rustc_hash::FxHashMap;
use rustpython_parser::ast::{Expr, Stmt};
use smallvec::smallvec;
use ruff_python_stdlib::path::is_python_stub_file;
use ruff_python_stdlib::typing::TYPING_EXTENSIONS;
use ruff_python::typing::TYPING_EXTENSIONS;
use crate::helpers::{collect_call_path, from_relative_import, Exceptions};
use crate::types::{Binding, BindingKind, CallPath, ExecutionContext, RefEquality, Scope};
use crate::ast::helpers::{collect_call_path, from_relative_import, Exceptions};
use crate::ast::types::{Binding, BindingKind, CallPath, ExecutionContext, RefEquality, Scope};
use crate::resolver::is_interface_definition_path;
use crate::visibility::{module_visibility, Modifier, VisibleScope};
#[allow(clippy::struct_excessive_bools)]
@@ -82,7 +82,7 @@ impl<'a> Context<'a> {
in_type_checking_block: false,
seen_import_boundary: false,
futures_allowed: true,
annotations_future_enabled: is_python_stub_file(path),
annotations_future_enabled: is_interface_definition_path(path),
handled_exceptions: Vec::default(),
}
}

View File

@@ -1,8 +1,8 @@
use rustpython_parser::ast::Expr;
use crate::context::Context;
use crate::helpers::{map_callable, to_call_path};
use crate::types::{Scope, ScopeKind};
use crate::ast::context::Context;
use crate::ast::helpers::{map_callable, to_call_path};
use crate::ast::types::{Scope, ScopeKind};
const CLASS_METHODS: [&str; 3] = ["__new__", "__init_subclass__", "__class_getitem__"];
const METACLASS_BASES: [(&str, &str); 2] = [("", "type"), ("abc", "ABCMeta")];

View File

@@ -2,7 +2,7 @@ use std::hash::Hash;
use rustpython_parser::ast::Expr;
use crate::comparable::ComparableExpr;
use crate::ast::comparable::ComparableExpr;
/// Wrapper around `Expr` that implements `Hash` and `PartialEq`.
pub struct HashableExpr<'a>(&'a Expr);
@@ -30,11 +30,11 @@ impl<'a> From<&'a Expr> for HashableExpr<'a> {
}
impl<'a> HashableExpr<'a> {
pub const fn from_expr(expr: &'a Expr) -> Self {
pub(crate) const fn from_expr(expr: &'a Expr) -> Self {
Self(expr)
}
pub const fn as_expr(&self) -> &'a Expr {
pub(crate) const fn as_expr(&self) -> &'a Expr {
self.0
}
}

View File

@@ -13,12 +13,11 @@ use rustpython_parser::ast::{
use rustpython_parser::{lexer, Mode, StringKind, Tok};
use smallvec::{smallvec, SmallVec};
use crate::context::Context;
use crate::newlines::StrExt;
use crate::ast::context::Context;
use crate::ast::types::{Binding, BindingKind, CallPath, Range};
use crate::ast::visitor;
use crate::ast::visitor::Visitor;
use crate::source_code::{Generator, Indexer, Locator, Stylist};
use crate::types::{Binding, BindingKind, CallPath, Range};
use crate::visitor;
use crate::visitor::Visitor;
/// Create an `Expr` with default location from an `ExprKind`.
pub fn create_expr(node: ExprKind) -> Expr {
@@ -653,7 +652,7 @@ pub fn has_comments<T>(located: &Located<T>, locator: &Locator) -> bool {
/// Returns `true` if a [`Range`] includes at least one comment.
pub fn has_comments_in(range: Range, locator: &Locator) -> bool {
for tok in lexer::lex_located(locator.slice(range), Mode::Module, range.location) {
for tok in lexer::lex_located(locator.slice(&range), Mode::Module, range.location) {
match tok {
Ok((_, tok, _)) => {
if matches!(tok, Tok::Comment(..)) {
@@ -742,7 +741,7 @@ pub fn to_module_path(package: &Path, path: &Path) -> Option<Vec<String>> {
.ok()?
.iter()
.map(Path::new)
.map(Path::file_stem)
.map(std::path::Path::file_stem)
.map(|path| path.and_then(|path| path.to_os_string().into_string().ok()))
.collect::<Option<Vec<String>>>()
}
@@ -800,7 +799,7 @@ where
match &stmt.node {
StmtKind::Raise { exc, cause } => {
self.raises
.push((Range::from(stmt), exc.as_deref(), cause.as_deref()));
.push((Range::from_located(stmt), exc.as_deref(), cause.as_deref()));
}
StmtKind::ClassDef { .. }
| StmtKind::FunctionDef { .. }
@@ -855,7 +854,7 @@ pub fn to_relative(absolute: Location, base: Location) -> Location {
/// Return `true` if a [`Located`] has leading content.
pub fn match_leading_content<T>(located: &Located<T>, locator: &Locator) -> bool {
let range = Range::new(Location::new(located.location.row(), 0), located.location);
let prefix = locator.slice(range);
let prefix = locator.slice(&range);
prefix.chars().any(|char| !char.is_whitespace())
}
@@ -865,7 +864,7 @@ pub fn match_trailing_content<T>(located: &Located<T>, locator: &Locator) -> boo
located.end_location.unwrap(),
Location::new(located.end_location.unwrap().row() + 1, 0),
);
let suffix = locator.slice(range);
let suffix = locator.slice(&range);
for char in suffix.chars() {
if char == '#' {
return false;
@@ -883,7 +882,7 @@ pub fn match_trailing_comment<T>(located: &Located<T>, locator: &Locator) -> Opt
located.end_location.unwrap(),
Location::new(located.end_location.unwrap().row() + 1, 0),
);
let suffix = locator.slice(range);
let suffix = locator.slice(&range);
for (i, char) in suffix.chars().enumerate() {
if char == '#' {
return Some(i);
@@ -941,7 +940,7 @@ pub fn identifier_range(stmt: &Stmt, locator: &Locator) -> Range {
| StmtKind::FunctionDef { .. }
| StmtKind::AsyncFunctionDef { .. }
) {
let contents = locator.slice(stmt);
let contents = locator.slice(&Range::from_located(stmt));
for (start, tok, end) in lexer::lex_located(contents, Mode::Module, stmt.location).flatten()
{
if matches!(tok, Tok::Name { .. }) {
@@ -950,7 +949,7 @@ pub fn identifier_range(stmt: &Stmt, locator: &Locator) -> Range {
}
error!("Failed to find identifier for {:?}", stmt);
}
Range::from(stmt)
Range::from_located(stmt)
}
/// Like `identifier_range`, but accepts a `Binding`.
@@ -968,12 +967,12 @@ pub fn binding_range(binding: &Binding, locator: &Locator) -> Range {
}
}
/// Return the ranges of [`Tok::Name`] tokens within a specified node.
pub fn find_names<'a, T>(
located: &'a Located<T>,
// Return the ranges of `Name` tokens within a specified node.
pub fn find_names<'a, T, U>(
located: &'a Located<T, U>,
locator: &'a Locator,
) -> impl Iterator<Item = Range> + 'a {
let contents = locator.slice(located);
let contents = locator.slice(&Range::from_located(located));
lexer::lex_located(contents, Mode::Module, located.location)
.flatten()
.filter(|(_, tok, _)| matches!(tok, Tok::Name { .. }))
@@ -991,7 +990,7 @@ pub fn excepthandler_name_range(handler: &Excepthandler, locator: &Locator) -> O
match (name, type_) {
(Some(_), Some(type_)) => {
let type_end_location = type_.end_location.unwrap();
let contents = locator.slice(Range::new(type_end_location, body[0].location));
let contents = locator.slice(&Range::new(type_end_location, body[0].location));
let range = lexer::lex_located(contents, Mode::Module, type_end_location)
.flatten()
.tuple_windows()
@@ -1015,7 +1014,7 @@ pub fn except_range(handler: &Excepthandler, locator: &Locator) -> Range {
.expect("Expected body to be non-empty")
.location
};
let contents = locator.slice(Range {
let contents = locator.slice(&Range {
location: handler.location,
end_location: end,
});
@@ -1032,7 +1031,7 @@ pub fn except_range(handler: &Excepthandler, locator: &Locator) -> Range {
/// Find f-strings that don't contain any formatted values in a `JoinedStr`.
pub fn find_useless_f_strings(expr: &Expr, locator: &Locator) -> Vec<(Range, Range)> {
let contents = locator.slice(expr);
let contents = locator.slice(&Range::from_located(expr));
lexer::lex_located(contents, Mode::Module, expr.location)
.flatten()
.filter_map(|(location, tok, end_location)| match tok {
@@ -1040,7 +1039,7 @@ pub fn find_useless_f_strings(expr: &Expr, locator: &Locator) -> Vec<(Range, Ran
kind: StringKind::FString | StringKind::RawFString,
..
} => {
let first_char = locator.slice(Range {
let first_char = locator.slice(&Range {
location,
end_location: Location::new(location.row(), location.column() + 1),
});
@@ -1080,7 +1079,7 @@ pub fn else_range(stmt: &Stmt, locator: &Locator) -> Option<Range> {
.expect("Expected body to be non-empty")
.end_location
.unwrap();
let contents = locator.slice(Range {
let contents = locator.slice(&Range {
location: body_end,
end_location: orelse
.first()
@@ -1102,7 +1101,7 @@ pub fn else_range(stmt: &Stmt, locator: &Locator) -> Option<Range> {
/// Return the `Range` of the first `Tok::Colon` token in a `Range`.
pub fn first_colon_range(range: Range, locator: &Locator) -> Option<Range> {
let contents = locator.slice(range);
let contents = locator.slice(&range);
let range = lexer::lex_located(contents, Mode::Module, range.location)
.flatten()
.find(|(_, kind, _)| matches!(kind, Tok::Colon))
@@ -1126,7 +1125,7 @@ pub fn end_of_statement(stmt: &Stmt, locator: &Locator) -> Location {
}
// Otherwise, find the end of the last line that's "part of" the statement.
for (lineno, line) in contents.universal_newlines().enumerate() {
for (lineno, line) in contents.lines().enumerate() {
if line.ends_with('\\') {
continue;
}
@@ -1158,7 +1157,7 @@ pub fn elif_else_range(stmt: &Stmt, locator: &Locator) -> Option<Range> {
[stmt, ..] => stmt.location,
_ => return None,
};
let contents = locator.slice(Range::new(start, end));
let contents = locator.slice(&Range::new(start, end));
let range = lexer::lex_located(contents, Mode::Module, start)
.flatten()
.find(|(_, kind, _)| matches!(kind, Tok::Elif | Tok::Else))
@@ -1213,50 +1212,48 @@ pub struct SimpleCallArgs<'a> {
}
impl<'a> SimpleCallArgs<'a> {
pub fn new(
args: impl IntoIterator<Item = &'a Expr>,
keywords: impl IntoIterator<Item = &'a Keyword>,
) -> Self {
let args = args
.into_iter()
.take_while(|arg| !matches!(arg.node, ExprKind::Starred { .. }))
.collect();
pub fn new(args: &'a [Expr], keywords: &'a [Keyword]) -> Self {
let mut result = SimpleCallArgs::default();
let kwargs = keywords
.into_iter()
.filter_map(|keyword| {
let node = &keyword.node;
node.arg.as_ref().map(|arg| (arg.as_ref(), &node.value))
})
.collect();
for arg in args {
match &arg.node {
ExprKind::Starred { .. } => {
break;
}
_ => {
result.args.push(arg);
}
}
}
SimpleCallArgs { args, kwargs }
}
for keyword in keywords {
if let Some(arg) = &keyword.node.arg {
result.kwargs.insert(arg, &keyword.node.value);
}
}
/// Get the argument with the given name.
/// If the argument is not found by name, return
/// `None`.
pub fn keyword_argument(&self, name: &str) -> Option<&'a Expr> {
self.kwargs.get(name).copied()
result
}
/// Get the argument with the given name or position.
/// If the argument is not found with either name or position, return
/// `None`.
pub fn argument(&self, name: &str, position: usize) -> Option<&'a Expr> {
self.keyword_argument(name)
.or_else(|| self.args.get(position).copied())
pub fn get_argument(&self, name: &'a str, position: Option<usize>) -> Option<&'a Expr> {
if let Some(kwarg) = self.kwargs.get(name) {
return Some(kwarg);
}
if let Some(position) = position {
if position < self.args.len() {
return Some(self.args[position]);
}
}
None
}
/// Return the number of positional and keyword arguments.
/// Get the number of positional and keyword arguments used.
pub fn len(&self) -> usize {
self.args.len() + self.kwargs.len()
}
/// Return `true` if there are no positional or keyword arguments.
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
/// Return `true` if the given `Expr` is a potential logging call. Matches
@@ -1280,11 +1277,11 @@ mod tests {
use rustpython_parser as parser;
use rustpython_parser::ast::Location;
use crate::helpers::{
use crate::ast::helpers::{
elif_else_range, else_range, first_colon_range, identifier_range, match_trailing_content,
};
use crate::ast::types::Range;
use crate::source_code::Locator;
use crate::types::Range;
#[test]
fn trailing_content() -> Result<()> {

View File

@@ -9,7 +9,7 @@ pub enum LoggingLevel {
}
impl LoggingLevel {
pub fn from_attribute(level: &str) -> Option<Self> {
pub fn from_str(level: &str) -> Option<Self> {
match level {
"debug" => Some(LoggingLevel::Debug),
"critical" => Some(LoggingLevel::Critical),

View File

@@ -6,13 +6,9 @@ pub mod function_type;
pub mod hashable;
pub mod helpers;
pub mod logging;
pub mod newlines;
pub mod operations;
pub mod relocate;
pub mod source_code;
pub mod str;
pub mod types;
pub mod typing;
pub mod visibility;
pub mod visitor;
pub mod whitespace;

View File

@@ -3,11 +3,11 @@ use rustc_hash::FxHashMap;
use rustpython_parser::ast::{Cmpop, Constant, Expr, ExprKind, Located, Stmt, StmtKind};
use rustpython_parser::{lexer, Mode, Tok};
use crate::context::Context;
use crate::helpers::any_over_expr;
use crate::types::{BindingKind, Scope};
use crate::visitor;
use crate::visitor::Visitor;
use crate::ast::context::Context;
use crate::ast::helpers::any_over_expr;
use crate::ast::types::{BindingKind, Scope};
use crate::ast::visitor;
use crate::ast::visitor::Visitor;
bitflags! {
#[derive(Default)]
@@ -348,7 +348,7 @@ pub fn locate_cmpops(contents: &str) -> Vec<LocatedCmpop> {
mod tests {
use rustpython_parser::ast::{Cmpop, Location};
use crate::operations::{locate_cmpops, LocatedCmpop};
use crate::ast::operations::{locate_cmpops, LocatedCmpop};
#[test]
fn locates_cmpops() {

View File

@@ -1,6 +1,6 @@
use rustpython_parser::ast::{Expr, ExprKind, Keyword};
use crate::types::Range;
use crate::ast::types::Range;
fn relocate_keyword(keyword: &mut Keyword, location: Range) {
keyword.location = location.location;

View File

@@ -28,17 +28,13 @@ impl Range {
end_location,
}
}
}
impl<T> From<&Located<T>> for Range {
fn from(located: &Located<T>) -> Self {
pub fn from_located<T, U>(located: &Located<T, U>) -> Self {
Range::new(located.location, located.end_location.unwrap())
}
}
impl<T> From<&Box<Located<T>>> for Range {
fn from(located: &Box<Located<T>>) -> Self {
Range::new(located.location, located.end_location.unwrap())
pub fn contains(&self, other: &Range) -> bool {
self.location <= other.location && self.end_location >= other.end_location
}
}

View File

@@ -1,7 +1,7 @@
use ruff_python_stdlib::typing::{PEP_585_BUILTINS_ELIGIBLE, PEP_593_SUBSCRIPTS, SUBSCRIPTS};
use ruff_python::typing::{PEP_585_BUILTINS_ELIGIBLE, PEP_593_SUBSCRIPTS, SUBSCRIPTS};
use rustpython_parser::ast::{Expr, ExprKind};
use crate::types::CallPath;
use crate::ast::types::CallPath;
pub enum Callable {
ForwardRef,

View File

@@ -1,12 +1,14 @@
use std::str::Lines;
use rustpython_parser::ast::{Located, Location};
use crate::ast::types::Range;
use crate::source_code::Locator;
use crate::types::Range;
/// Extract the leading indentation from a line.
pub fn indentation<'a, T>(locator: &'a Locator, located: &'a Located<T>) -> Option<&'a str> {
let range = Range::from(located);
let indentation = locator.slice(Range::new(
let range = Range::from_located(located);
let indentation = locator.slice(&Range::new(
Location::new(range.location.row(), 0),
Location::new(range.location.row(), range.location.column()),
));
@@ -37,3 +39,38 @@ pub fn clean(indentation: &str) -> String {
.map(|char| if char.is_whitespace() { char } else { ' ' })
.collect()
}
/// Like `str#lines`, but includes a trailing newline as an empty line.
pub struct LinesWithTrailingNewline<'a> {
trailing: Option<&'a str>,
underlying: Lines<'a>,
}
impl<'a> LinesWithTrailingNewline<'a> {
pub fn from(input: &'a str) -> LinesWithTrailingNewline<'a> {
LinesWithTrailingNewline {
underlying: input.lines(),
trailing: if input.ends_with('\n') {
Some("")
} else {
None
},
}
}
}
impl<'a> Iterator for LinesWithTrailingNewline<'a> {
type Item = &'a str;
#[inline]
fn next(&mut self) -> Option<&'a str> {
let mut next = self.underlying.next();
if next.is_none() {
if self.trailing.is_some() {
next = self.trailing;
self.trailing = None;
}
}
next
}
}

View File

@@ -6,14 +6,14 @@ use libcst_native::{
use rustpython_parser::ast::{ExcepthandlerKind, Expr, Keyword, Location, Stmt, StmtKind};
use rustpython_parser::{lexer, Mode, Tok};
use ruff_diagnostics::Fix;
use ruff_python_ast::helpers;
use ruff_python_ast::helpers::to_absolute;
use ruff_python_ast::newlines::NewlineWithTrailingNewline;
use ruff_python_ast::source_code::{Indexer, Locator, Stylist};
use crate::ast::helpers;
use crate::ast::helpers::to_absolute;
use crate::ast::types::Range;
use crate::ast::whitespace::LinesWithTrailingNewline;
use crate::cst::helpers::compose_module_path;
use crate::cst::matchers::match_module;
use crate::fix::Fix;
use crate::source_code::{Indexer, Locator, Stylist};
/// Determine if a body contains only a single statement, taking into account
/// deleted.
@@ -100,7 +100,7 @@ fn is_lone_child(child: &Stmt, parent: &Stmt, deleted: &[&Stmt]) -> Result<bool>
/// of a multi-statement line.
fn trailing_semicolon(stmt: &Stmt, locator: &Locator) -> Option<Location> {
let contents = locator.skip(stmt.end_location.unwrap());
for (row, line) in NewlineWithTrailingNewline::from(contents).enumerate() {
for (row, line) in LinesWithTrailingNewline::from(contents).enumerate() {
let trimmed = line.trim();
if trimmed.starts_with(';') {
let column = line
@@ -123,7 +123,7 @@ fn trailing_semicolon(stmt: &Stmt, locator: &Locator) -> Option<Location> {
fn next_stmt_break(semicolon: Location, locator: &Locator) -> Location {
let start_location = Location::new(semicolon.row(), semicolon.column() + 1);
let contents = locator.skip(start_location);
for (row, line) in NewlineWithTrailingNewline::from(contents).enumerate() {
for (row, line) in LinesWithTrailingNewline::from(contents).enumerate() {
let trimmed = line.trim();
// Skip past any continuations.
if trimmed.starts_with('\\') {
@@ -227,7 +227,7 @@ pub fn remove_unused_imports<'a>(
indexer: &Indexer,
stylist: &Stylist,
) -> Result<Fix> {
let module_text = locator.slice(stmt);
let module_text = locator.slice(&Range::from_located(stmt));
let mut tree = match_module(module_text)?;
let Some(Statement::Simple(body)) = tree.body.first_mut() else {
@@ -450,9 +450,8 @@ mod tests {
use rustpython_parser as parser;
use rustpython_parser::ast::Location;
use ruff_python_ast::source_code::Locator;
use crate::autofix::helpers::{next_stmt_break, trailing_semicolon};
use crate::source_code::Locator;
#[test]
fn find_semicolon() -> Result<()> {

View File

@@ -4,12 +4,11 @@ use itertools::Itertools;
use rustc_hash::FxHashMap;
use rustpython_parser::ast::Location;
use ruff_diagnostics::{Diagnostic, Fix};
use ruff_python_ast::source_code::Locator;
use ruff_python_ast::types::Range;
use crate::ast::types::Range;
use crate::fix::Fix;
use crate::linter::FixTable;
use crate::registry::AsRule;
use crate::registry::Diagnostic;
use crate::source_code::Locator;
pub mod helpers;
@@ -55,7 +54,7 @@ fn apply_fixes<'a>(
}
// Add all contents from `last_pos` to `fix.location`.
let slice = locator.slice(Range::new(last_pos, fix.location));
let slice = locator.slice(&Range::new(last_pos, fix.location));
output.push_str(slice);
// Add the patch itself.
@@ -79,7 +78,7 @@ pub(crate) fn apply_fix(fix: &Fix, locator: &Locator) -> String {
let mut output = String::with_capacity(locator.len());
// Add all contents from `last_pos` to `fix.location`.
let slice = locator.slice(Range::new(Location::new(1, 0), fix.location));
let slice = locator.slice(&Range::new(Location::new(1, 0), fix.location));
output.push_str(slice);
// Add the patch itself.
@@ -96,38 +95,43 @@ pub(crate) fn apply_fix(fix: &Fix, locator: &Locator) -> String {
mod tests {
use rustpython_parser::ast::Location;
use ruff_diagnostics::Diagnostic;
use ruff_diagnostics::Fix;
use ruff_python_ast::source_code::Locator;
use crate::autofix::{apply_fix, apply_fixes};
use crate::fix::Fix;
use crate::registry::Diagnostic;
use crate::rules::pycodestyle::rules::NoNewLineAtEndOfFile;
fn create_diagnostics(fixes: impl IntoIterator<Item = Fix>) -> Vec<Diagnostic> {
fixes
.into_iter()
.map(|fix| Diagnostic {
use crate::source_code::Locator;
#[test]
fn empty_file() {
let fixes: Vec<Diagnostic> = vec![];
let locator = Locator::new(r#""#);
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
assert_eq!(contents, "");
assert_eq!(fixed.values().sum::<usize>(), 0);
}
impl From<Fix> for Diagnostic {
fn from(fix: Fix) -> Self {
Diagnostic {
// The choice of rule here is arbitrary.
kind: NoNewLineAtEndOfFile.into(),
location: fix.location,
end_location: fix.end_location,
fix: Some(fix),
parent: None,
})
.collect()
}
#[test]
fn empty_file() {
let locator = Locator::new(r#""#);
let diagnostics = create_diagnostics([]);
let (contents, fixed) = apply_fixes(diagnostics.iter(), &locator);
assert_eq!(contents, "");
assert_eq!(fixed.values().sum::<usize>(), 0);
}
}
}
#[test]
fn apply_one_replacement() {
let fixes: Vec<Diagnostic> = vec![Fix {
content: "Bar".to_string(),
location: Location::new(1, 8),
end_location: Location::new(1, 14),
}
.into()];
let locator = Locator::new(
r#"
class A(object):
@@ -135,12 +139,7 @@ class A(object):
"#
.trim(),
);
let diagnostics = create_diagnostics([Fix {
content: "Bar".to_string(),
location: Location::new(1, 8),
end_location: Location::new(1, 14),
}]);
let (contents, fixed) = apply_fixes(diagnostics.iter(), &locator);
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
assert_eq!(
contents,
r#"
@@ -154,6 +153,12 @@ class A(Bar):
#[test]
fn apply_one_removal() {
let fixes: Vec<Diagnostic> = vec![Fix {
content: String::new(),
location: Location::new(1, 7),
end_location: Location::new(1, 15),
}
.into()];
let locator = Locator::new(
r#"
class A(object):
@@ -161,12 +166,7 @@ class A(object):
"#
.trim(),
);
let diagnostics = create_diagnostics([Fix {
content: String::new(),
location: Location::new(1, 7),
end_location: Location::new(1, 15),
}]);
let (contents, fixed) = apply_fixes(diagnostics.iter(), &locator);
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
assert_eq!(
contents,
r#"
@@ -180,6 +180,20 @@ class A:
#[test]
fn apply_two_removals() {
let fixes: Vec<Diagnostic> = vec![
Fix {
content: String::new(),
location: Location::new(1, 7),
end_location: Location::new(1, 16),
}
.into(),
Fix {
content: String::new(),
location: Location::new(1, 16),
end_location: Location::new(1, 23),
}
.into(),
];
let locator = Locator::new(
r#"
class A(object, object):
@@ -187,19 +201,7 @@ class A(object, object):
"#
.trim(),
);
let diagnostics = create_diagnostics([
Fix {
content: String::new(),
location: Location::new(1, 7),
end_location: Location::new(1, 16),
},
Fix {
content: String::new(),
location: Location::new(1, 16),
end_location: Location::new(1, 23),
},
]);
let (contents, fixed) = apply_fixes(diagnostics.iter(), &locator);
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
assert_eq!(
contents,
@@ -214,6 +216,20 @@ class A:
#[test]
fn ignore_overlapping_fixes() {
let fixes: Vec<Diagnostic> = vec![
Fix {
content: String::new(),
location: Location::new(1, 7),
end_location: Location::new(1, 15),
}
.into(),
Fix {
content: "ignored".to_string(),
location: Location::new(1, 9),
end_location: Location::new(1, 11),
}
.into(),
];
let locator = Locator::new(
r#"
class A(object):
@@ -221,19 +237,7 @@ class A(object):
"#
.trim(),
);
let diagnostics = create_diagnostics([
Fix {
content: String::new(),
location: Location::new(1, 7),
end_location: Location::new(1, 15),
},
Fix {
content: "ignored".to_string(),
location: Location::new(1, 9),
end_location: Location::new(1, 11),
},
]);
let (contents, fixed) = apply_fixes(diagnostics.iter(), &locator);
let (contents, fixed) = apply_fixes(fixes.iter(), &locator);
assert_eq!(
contents,
r#"

View File

@@ -1,11 +1,10 @@
use rustpython_parser::ast::{Expr, Stmt};
use ruff_python_ast::types::Range;
use ruff_python_ast::types::RefEquality;
use ruff_python_ast::visibility::{Visibility, VisibleScope};
use crate::ast::types::RefEquality;
use crate::checkers::ast::AnnotationContext;
use crate::docstrings::definition::Definition;
use crate::visibility::{Visibility, VisibleScope};
use crate::Range;
type Context<'a> = (Vec<usize>, Vec<RefEquality<'a, Stmt>>);

View File

@@ -13,31 +13,23 @@ use rustpython_parser::ast::{
Suite,
};
use ruff_diagnostics::Diagnostic;
use ruff_python_ast::context::Context;
use ruff_python_ast::helpers::{
binding_range, extract_handled_exceptions, to_module_path, Exceptions,
};
use ruff_python_ast::operations::{extract_all_names, AllNamesFlags};
use ruff_python_ast::relocate::relocate_expr;
use ruff_python_ast::source_code::{Indexer, Locator, Stylist};
use ruff_python_ast::types::{
use ruff_python::builtins::{BUILTINS, MAGIC_GLOBALS};
use crate::ast::context::Context;
use crate::ast::helpers::{binding_range, extract_handled_exceptions, to_module_path, Exceptions};
use crate::ast::operations::{extract_all_names, AllNamesFlags};
use crate::ast::relocate::relocate_expr;
use crate::ast::types::{
Binding, BindingKind, ClassDef, ExecutionContext, FunctionDef, Lambda, Node, Range,
RefEquality, Scope, ScopeKind,
};
use ruff_python_ast::typing::{match_annotated_subscript, Callable, SubscriptKind};
use ruff_python_ast::visitor::{walk_excepthandler, walk_pattern, Visitor};
use ruff_python_ast::{
branch_detection, cast, helpers, operations, str, typing, visibility, visitor,
};
use ruff_python_stdlib::builtins::{BUILTINS, MAGIC_GLOBALS};
use ruff_python_stdlib::path::is_python_stub_file;
use crate::ast::typing::{match_annotated_subscript, Callable, SubscriptKind};
use crate::ast::visitor::{walk_excepthandler, walk_pattern, Visitor};
use crate::ast::{branch_detection, cast, helpers, operations, typing, visitor};
use crate::checkers::ast::deferred::Deferred;
use crate::docstrings::definition::{
transition_scope, Definition, DefinitionKind, Docstring, Documentable,
};
use crate::registry::{AsRule, Rule};
use crate::docstrings::definition::{Definition, DefinitionKind, Docstring, Documentable};
use crate::registry::{Diagnostic, Rule};
use crate::resolver::is_interface_definition_path;
use crate::rules::{
flake8_2020, flake8_annotations, flake8_bandit, flake8_blind_except, flake8_boolean_trap,
flake8_bugbear, flake8_builtins, flake8_comprehensions, flake8_datetimez, flake8_debugger,
@@ -49,7 +41,9 @@ use crate::rules::{
};
use crate::settings::types::PythonVersion;
use crate::settings::{flags, Settings};
use crate::{autofix, docstrings, noqa};
use crate::source_code::{Indexer, Locator, Stylist};
use crate::visibility::transition_scope;
use crate::{autofix, docstrings, noqa, visibility};
mod deferred;
@@ -63,7 +57,7 @@ pub struct Checker<'a> {
pub path: &'a Path,
module_path: Option<Vec<String>>,
package: Option<&'a Path>,
is_stub: bool,
is_interface_definition: bool,
autofix: flags::Autofix,
noqa: flags::Noqa,
pub settings: &'a Settings,
@@ -102,7 +96,7 @@ impl<'a> Checker<'a> {
path,
package,
module_path: module_path.clone(),
is_stub: is_python_stub_file(path),
is_interface_definition: is_interface_definition_path(path),
locator,
stylist: style,
indexer,
@@ -199,7 +193,7 @@ where
// Add the binding to the current scope.
let context = self.ctx.execution_context();
let scope = &mut self.ctx.scopes[scope_index];
let usage = Some((scope.id, Range::from(stmt)));
let usage = Some((scope.id, Range::from_located(stmt)));
for (name, range) in names.iter().zip(ranges.iter()) {
let index = self.ctx.bindings.len();
self.ctx.bindings.push(Binding {
@@ -228,7 +222,7 @@ where
if scope_index != GLOBAL_SCOPE_INDEX {
let context = self.ctx.execution_context();
let scope = &mut self.ctx.scopes[scope_index];
let usage = Some((scope.id, Range::from(stmt)));
let usage = Some((scope.id, Range::from_located(stmt)));
for (name, range) in names.iter().zip(ranges.iter()) {
// Add a binding to the current scope.
let index = self.ctx.bindings.len();
@@ -382,7 +376,7 @@ where
}
}
if self.is_stub {
if self.is_interface_definition {
if self.settings.rules.enabled(&Rule::PassStatementStubBody) {
flake8_pyi::rules::pass_statement_stub_body(self, body);
}
@@ -669,7 +663,7 @@ where
runtime_usage: None,
synthetic_usage: None,
typing_usage: None,
range: Range::from(stmt),
range: Range::from_located(stmt),
source: Some(self.ctx.current_stmt().clone()),
context,
},
@@ -760,7 +754,7 @@ where
flake8_bugbear::rules::useless_expression(self, body);
}
if !self.is_stub {
if !self.is_interface_definition {
if self
.settings
.rules
@@ -775,7 +769,7 @@ where
);
}
}
if self.is_stub {
if self.is_interface_definition {
if self.settings.rules.enabled(&Rule::PassStatementStubBody) {
flake8_pyi::rules::pass_statement_stub_body(self, body);
}
@@ -866,10 +860,10 @@ where
.last()
.expect("No current scope found"))]
.id,
Range::from(alias),
Range::from_located(alias),
)),
typing_usage: None,
range: Range::from(alias),
range: Range::from_located(alias),
source: Some(self.ctx.current_stmt().clone()),
context: self.ctx.execution_context(),
},
@@ -880,7 +874,7 @@ where
{
self.diagnostics.push(Diagnostic::new(
pyflakes::rules::LateFutureImport,
Range::from(stmt),
Range::from_located(stmt),
));
}
} else if alias.node.name.contains('.') && alias.node.asname.is_none() {
@@ -901,13 +895,13 @@ where
.last()
.expect("No current scope found"))]
.id,
Range::from(alias),
Range::from_located(alias),
))
} else {
None
},
typing_usage: None,
range: Range::from(alias),
range: Range::from_located(alias),
source: Some(self.ctx.current_stmt().clone()),
context: self.ctx.execution_context(),
},
@@ -939,13 +933,13 @@ where
.last()
.expect("No current scope found"))]
.id,
Range::from(alias),
Range::from_located(alias),
))
} else {
None
},
typing_usage: None,
range: Range::from(alias),
range: Range::from_located(alias),
source: Some(self.ctx.current_stmt().clone()),
context: self.ctx.execution_context(),
},
@@ -1138,8 +1132,8 @@ where
if self.settings.rules.enabled(&Rule::RewriteCElementTree) {
pyupgrade::rules::replace_c_element_tree(self, stmt);
}
if self.settings.rules.enabled(&Rule::DeprecatedImport) {
pyupgrade::rules::deprecated_import(
if self.settings.rules.enabled(&Rule::ImportReplacements) {
pyupgrade::rules::import_replacements(
self,
stmt,
names,
@@ -1212,10 +1206,10 @@ where
.last()
.expect("No current scope found"))]
.id,
Range::from(alias),
Range::from_located(alias),
)),
typing_usage: None,
range: Range::from(alias),
range: Range::from_located(alias),
source: Some(self.ctx.current_stmt().clone()),
context: self.ctx.execution_context(),
},
@@ -1234,7 +1228,7 @@ where
{
self.diagnostics.push(Diagnostic::new(
pyflakes::rules::LateFutureImport,
Range::from(stmt),
Range::from_located(stmt),
));
}
} else if alias.node.name == "*" {
@@ -1251,13 +1245,13 @@ where
.last()
.expect("No current scope found"))]
.id,
Range::from(alias),
Range::from_located(alias),
))
} else {
None
},
typing_usage: None,
range: Range::from(stmt),
range: Range::from_located(stmt),
source: Some(self.ctx.current_stmt().clone()),
context: self.ctx.execution_context(),
},
@@ -1274,7 +1268,7 @@ where
module.as_deref(),
),
},
Range::from(stmt),
Range::from_located(stmt),
));
}
}
@@ -1287,7 +1281,7 @@ where
module.as_deref(),
),
},
Range::from(stmt),
Range::from_located(stmt),
));
}
@@ -1329,13 +1323,13 @@ where
.last()
.expect("No current scope found"))]
.id,
Range::from(alias),
Range::from_located(alias),
))
} else {
None
},
typing_usage: None,
range: Range::from(alias),
range: Range::from_located(alias),
source: Some(self.ctx.current_stmt().clone()),
context: self.ctx.execution_context(),
@@ -1505,7 +1499,7 @@ where
}
if self.settings.rules.enabled(&Rule::OSErrorAlias) {
if let Some(item) = exc {
pyupgrade::rules::os_error_alias_raise(self, item);
pyupgrade::rules::os_error_alias(self, &item);
}
}
if self.settings.rules.enabled(&Rule::RaiseVanillaClass) {
@@ -1757,7 +1751,7 @@ where
flake8_bugbear::rules::redundant_tuple_in_exception_handler(self, handlers);
}
if self.settings.rules.enabled(&Rule::OSErrorAlias) {
pyupgrade::rules::os_error_alias_handlers(self, handlers);
pyupgrade::rules::os_error_alias(self, &handlers);
}
if self.settings.rules.enabled(&Rule::AssertInExcept) {
self.diagnostics.extend(
@@ -1809,7 +1803,7 @@ where
}
}
if self.is_stub {
if self.is_interface_definition {
if self.settings.rules.enabled(&Rule::PrefixTypeParams) {
flake8_pyi::rules::prefix_type_params(self, value, targets);
}
@@ -1957,7 +1951,7 @@ where
runtime_usage: None,
synthetic_usage: None,
typing_usage: None,
range: Range::from(stmt),
range: Range::from_located(stmt),
source: Some(RefEquality(stmt)),
context: self.ctx.execution_context(),
});
@@ -2022,7 +2016,7 @@ where
runtime_usage: None,
synthetic_usage: None,
typing_usage: None,
range: Range::from(*stmt),
range: Range::from_located(stmt),
source: Some(RefEquality(stmt)),
context: self.ctx.execution_context(),
});
@@ -2095,7 +2089,7 @@ where
let baseclasses = &self
.settings
.flake8_type_checking
.runtime_evaluated_base_classes;
.runtime_evaluated_baseclasses;
let decorators = &self
.settings
.flake8_type_checking
@@ -2132,7 +2126,7 @@ where
StmtKind::If { test, body, orelse } => {
self.visit_expr(test);
if flake8_type_checking::helpers::is_type_checking_block(&self.ctx, test) {
if flake8_type_checking::helpers::is_type_checking_block(self, test) {
if self.settings.rules.enabled(&Rule::EmptyTypeCheckingBlock) {
flake8_type_checking::rules::empty_type_checking_block(self, stmt, body);
}
@@ -2165,7 +2159,7 @@ where
runtime_usage: None,
synthetic_usage: None,
typing_usage: None,
range: Range::from(stmt),
range: Range::from_located(stmt),
source: Some(self.ctx.current_stmt().clone()),
context: self.ctx.execution_context(),
},
@@ -2195,7 +2189,7 @@ where
} = &expr.node
{
self.deferred.string_type_definitions.push((
Range::from(expr),
Range::from_located(expr),
value,
(self.ctx.in_annotation, self.ctx.in_type_checking_block),
(self.ctx.scope_stack.clone(), self.ctx.parents.clone()),
@@ -2261,7 +2255,7 @@ where
elts,
check_too_many_expressions,
check_two_starred_expressions,
Range::from(expr),
Range::from_located(expr),
) {
self.diagnostics.push(diagnostic);
}
@@ -2296,9 +2290,10 @@ where
}
ExprContext::Store => {
if self.settings.rules.enabled(&Rule::AmbiguousVariableName) {
if let Some(diagnostic) =
pycodestyle::rules::ambiguous_variable_name(id, Range::from(expr))
{
if let Some(diagnostic) = pycodestyle::rules::ambiguous_variable_name(
id,
Range::from_located(expr),
) {
self.diagnostics.push(diagnostic);
}
}
@@ -2383,7 +2378,7 @@ where
{
if attr == "format" {
// "...".format(...) call
let location = Range::from(expr);
let location = Range::from_located(expr);
match pyflakes::format::FormatSummary::try_from(value.as_ref()) {
Err(e) => {
if self
@@ -2484,7 +2479,7 @@ where
pyupgrade::rules::replace_stdout_stderr(self, expr, func, keywords);
}
if self.settings.rules.enabled(&Rule::OSErrorAlias) {
pyupgrade::rules::os_error_alias_call(self, func);
pyupgrade::rules::os_error_alias(self, &expr);
}
if self.settings.rules.enabled(&Rule::IsinstanceWithTuple)
&& self.settings.target_version >= PythonVersion::Py310
@@ -2779,14 +2774,22 @@ where
func,
args,
keywords,
Range::from(expr),
Range::from_located(expr),
);
}
if self.settings.rules.enabled(&Rule::CallDatetimeToday) {
flake8_datetimez::rules::call_datetime_today(self, func, Range::from(expr));
flake8_datetimez::rules::call_datetime_today(
self,
func,
Range::from_located(expr),
);
}
if self.settings.rules.enabled(&Rule::CallDatetimeUtcnow) {
flake8_datetimez::rules::call_datetime_utcnow(self, func, Range::from(expr));
flake8_datetimez::rules::call_datetime_utcnow(
self,
func,
Range::from_located(expr),
);
}
if self
.settings
@@ -2796,7 +2799,7 @@ where
flake8_datetimez::rules::call_datetime_utcfromtimestamp(
self,
func,
Range::from(expr),
Range::from_located(expr),
);
}
if self
@@ -2809,7 +2812,7 @@ where
func,
args,
keywords,
Range::from(expr),
Range::from_located(expr),
);
}
if self
@@ -2822,7 +2825,7 @@ where
func,
args,
keywords,
Range::from(expr),
Range::from_located(expr),
);
}
if self
@@ -2834,14 +2837,18 @@ where
self,
func,
args,
Range::from(expr),
Range::from_located(expr),
);
}
if self.settings.rules.enabled(&Rule::CallDateToday) {
flake8_datetimez::rules::call_date_today(self, func, Range::from(expr));
flake8_datetimez::rules::call_date_today(self, func, Range::from_located(expr));
}
if self.settings.rules.enabled(&Rule::CallDateFromtimestamp) {
flake8_datetimez::rules::call_date_fromtimestamp(self, func, Range::from(expr));
flake8_datetimez::rules::call_date_fromtimestamp(
self,
func,
Range::from_located(expr),
);
}
// pygrep-hooks
@@ -2866,12 +2873,6 @@ where
if self.settings.rules.enabled(&Rule::BadStrStripCall) {
pylint::rules::bad_str_strip_call(self, func, args);
}
if self.settings.rules.enabled(&Rule::InvalidEnvvarDefault) {
pylint::rules::invalid_envvar_default(self, func, args, keywords);
}
if self.settings.rules.enabled(&Rule::InvalidEnvvarValue) {
pylint::rules::invalid_envvar_value(self, func, args, keywords);
}
// flake8-pytest-style
if self.settings.rules.enabled(&Rule::PatchWithLambda) {
@@ -3076,7 +3077,7 @@ where
.rules
.enabled(&Rule::PercentFormatUnsupportedFormatCharacter)
{
let location = Range::from(expr);
let location = Range::from_located(expr);
match pyflakes::cformat::CFormatSummary::try_from(value.as_str()) {
Err(CFormatError {
typ: CFormatErrorType::UnsupportedFormatChar(c),
@@ -3265,7 +3266,7 @@ where
left,
ops,
comparators,
Range::from(expr),
Range::from_located(expr),
);
}
@@ -3273,7 +3274,7 @@ where
self.diagnostics.extend(pycodestyle::rules::type_comparison(
ops,
comparators,
Range::from(expr),
Range::from_located(expr),
));
}
@@ -3305,10 +3306,6 @@ where
pylint::rules::comparison_of_constant(self, left, ops, comparators);
}
if self.settings.rules.enabled(&Rule::CompareToEmptyString) {
pylint::rules::compare_to_empty_string(self, left, ops, comparators);
}
if self.settings.rules.enabled(&Rule::MagicValueComparison) {
pylint::rules::magic_value_comparison(self, left, comparators);
}
@@ -3321,7 +3318,7 @@ where
flake8_simplify::rules::yoda_conditions(self, expr, left, ops, comparators);
}
if self.is_stub {
if self.is_interface_definition {
if self
.settings
.rules
@@ -3354,7 +3351,7 @@ where
} => {
if self.ctx.in_type_definition && !self.ctx.in_literal {
self.deferred.string_type_definitions.push((
Range::from(expr),
Range::from_located(expr),
value,
(self.ctx.in_annotation, self.ctx.in_type_checking_block),
(self.ctx.scope_stack.clone(), self.ctx.parents.clone()),
@@ -3367,7 +3364,7 @@ where
{
if let Some(diagnostic) = flake8_bandit::rules::hardcoded_bind_all_interfaces(
value,
&Range::from(expr),
&Range::from_located(expr),
) {
self.diagnostics.push(diagnostic);
}
@@ -3441,7 +3438,7 @@ where
pylint::rules::merge_isinstance(self, expr, op, values);
}
if self.settings.rules.enabled(&Rule::SingleStartsEndsWith) {
flake8_pie::rules::single_starts_ends_with(self, expr);
flake8_pie::rules::single_starts_ends_with(self, values, op);
}
if self.settings.rules.enabled(&Rule::DuplicateIsinstanceCall) {
flake8_simplify::rules::duplicate_isinstance_call(self, expr);
@@ -3777,13 +3774,6 @@ where
if self.settings.rules.enabled(&Rule::ExceptWithEmptyTuple) {
flake8_bugbear::rules::except_with_empty_tuple(self, excepthandler);
}
if self
.settings
.rules
.enabled(&Rule::ExceptWithNonExceptionClasses)
{
flake8_bugbear::rules::except_with_non_exception_classes(self, excepthandler);
}
if self.settings.rules.enabled(&Rule::ReraiseNoCause) {
tryceratops::rules::reraise_no_cause(self, body);
}
@@ -3913,7 +3903,7 @@ where
flake8_bugbear::rules::function_call_argument_default(self, arguments);
}
if self.is_stub {
if self.is_interface_definition {
if self
.settings
.rules
@@ -3922,7 +3912,7 @@ where
flake8_pyi::rules::typed_argument_simple_defaults(self, arguments);
}
}
if self.is_stub {
if self.is_interface_definition {
if self.settings.rules.enabled(&Rule::ArgumentSimpleDefaults) {
flake8_pyi::rules::argument_simple_defaults(self, arguments);
}
@@ -3957,7 +3947,7 @@ where
runtime_usage: None,
synthetic_usage: None,
typing_usage: None,
range: Range::from(arg),
range: Range::from_located(arg),
source: Some(self.ctx.current_stmt().clone()),
context: self.ctx.execution_context(),
},
@@ -3965,7 +3955,7 @@ where
if self.settings.rules.enabled(&Rule::AmbiguousVariableName) {
if let Some(diagnostic) =
pycodestyle::rules::ambiguous_variable_name(&arg.node.arg, Range::from(arg))
pycodestyle::rules::ambiguous_variable_name(&arg.node.arg, Range::from_located(arg))
{
self.diagnostics.push(diagnostic);
}
@@ -4000,7 +3990,7 @@ where
runtime_usage: None,
synthetic_usage: None,
typing_usage: None,
range: Range::from(pattern),
range: Range::from_located(pattern),
source: Some(self.ctx.current_stmt().clone()),
context: self.ctx.execution_context(),
},
@@ -4211,7 +4201,7 @@ impl<'a> Checker<'a> {
if let Some(index) = scope.bindings.get(&id.as_str()) {
// Mark the binding as used.
let context = self.ctx.execution_context();
self.ctx.bindings[*index].mark_used(scope_id, Range::from(expr), context);
self.ctx.bindings[*index].mark_used(scope_id, Range::from_located(expr), context);
if self.ctx.bindings[*index].kind.is_annotation()
&& !self.ctx.in_deferred_string_type_definition
@@ -4241,7 +4231,7 @@ impl<'a> Checker<'a> {
if let Some(index) = scope.bindings.get(full_name) {
self.ctx.bindings[*index].mark_used(
scope_id,
Range::from(expr),
Range::from_located(expr),
context,
);
}
@@ -4258,7 +4248,7 @@ impl<'a> Checker<'a> {
if let Some(index) = scope.bindings.get(full_name.as_str()) {
self.ctx.bindings[*index].mark_used(
scope_id,
Range::from(expr),
Range::from_located(expr),
context,
);
}
@@ -4300,7 +4290,7 @@ impl<'a> Checker<'a> {
name: id.to_string(),
sources: from_list,
},
Range::from(expr),
Range::from_located(expr),
));
}
return;
@@ -4331,7 +4321,7 @@ impl<'a> Checker<'a> {
self.diagnostics.push(Diagnostic::new(
pyflakes::rules::UndefinedName { name: id.clone() },
Range::from(expr),
Range::from_located(expr),
));
}
}
@@ -4403,7 +4393,7 @@ impl<'a> Checker<'a> {
runtime_usage: None,
synthetic_usage: None,
typing_usage: None,
range: Range::from(expr),
range: Range::from_located(expr),
source: Some(self.ctx.current_stmt().clone()),
context: self.ctx.execution_context(),
},
@@ -4423,7 +4413,7 @@ impl<'a> Checker<'a> {
runtime_usage: None,
synthetic_usage: None,
typing_usage: None,
range: Range::from(expr),
range: Range::from_located(expr),
source: Some(self.ctx.current_stmt().clone()),
context: self.ctx.execution_context(),
},
@@ -4439,7 +4429,7 @@ impl<'a> Checker<'a> {
runtime_usage: None,
synthetic_usage: None,
typing_usage: None,
range: Range::from(expr),
range: Range::from_located(expr),
source: Some(self.ctx.current_stmt().clone()),
context: self.ctx.execution_context(),
},
@@ -4504,7 +4494,7 @@ impl<'a> Checker<'a> {
runtime_usage: None,
synthetic_usage: None,
typing_usage: None,
range: Range::from(expr),
range: Range::from_located(expr),
source: Some(self.ctx.current_stmt().clone()),
context: self.ctx.execution_context(),
},
@@ -4520,7 +4510,7 @@ impl<'a> Checker<'a> {
runtime_usage: None,
synthetic_usage: None,
typing_usage: None,
range: Range::from(expr),
range: Range::from_located(expr),
source: Some(self.ctx.current_stmt().clone()),
context: self.ctx.execution_context(),
},
@@ -4551,7 +4541,7 @@ impl<'a> Checker<'a> {
pyflakes::rules::UndefinedName {
name: id.to_string(),
},
Range::from(expr),
Range::from_located(expr),
));
}
@@ -4685,37 +4675,31 @@ impl<'a> Checker<'a> {
while let Some((scopes, ..)) = self.deferred.assignments.pop() {
let scope_index = scopes[scopes.len() - 1];
let parent_scope_index = scopes[scopes.len() - 2];
// pyflakes
if self.settings.rules.enabled(&Rule::UnusedVariable) {
pyflakes::rules::unused_variable(self, scope_index);
}
if self.settings.rules.enabled(&Rule::UnusedAnnotation) {
pyflakes::rules::unused_annotation(self, scope_index);
}
if !self.is_stub {
// flake8-unused-arguments
if self.settings.rules.enabled(&Rule::UnusedFunctionArgument)
|| self.settings.rules.enabled(&Rule::UnusedMethodArgument)
|| self
.settings
.rules
.enabled(&Rule::UnusedClassMethodArgument)
|| self
.settings
.rules
.enabled(&Rule::UnusedStaticMethodArgument)
|| self.settings.rules.enabled(&Rule::UnusedLambdaArgument)
{
self.diagnostics
.extend(flake8_unused_arguments::rules::unused_arguments(
self,
&self.ctx.scopes[parent_scope_index],
&self.ctx.scopes[scope_index],
&self.ctx.bindings,
));
}
if self.settings.rules.enabled(&Rule::UnusedFunctionArgument)
|| self.settings.rules.enabled(&Rule::UnusedMethodArgument)
|| self
.settings
.rules
.enabled(&Rule::UnusedClassMethodArgument)
|| self
.settings
.rules
.enabled(&Rule::UnusedStaticMethodArgument)
|| self.settings.rules.enabled(&Rule::UnusedLambdaArgument)
{
self.diagnostics
.extend(flake8_unused_arguments::rules::unused_arguments(
self,
&self.ctx.scopes[parent_scope_index],
&self.ctx.scopes[scope_index],
&self.ctx.bindings,
));
}
}
}
@@ -4743,33 +4727,30 @@ impl<'a> Checker<'a> {
}
fn check_dead_scopes(&mut self) {
let enforce_typing_imports = !self.is_stub
&& (self
.settings
.rules
.enabled(&Rule::GlobalVariableNotAssigned)
|| self
.settings
.rules
.enabled(&Rule::RuntimeImportInTypeCheckingBlock)
|| self
.settings
.rules
.enabled(&Rule::TypingOnlyFirstPartyImport)
|| self
.settings
.rules
.enabled(&Rule::TypingOnlyThirdPartyImport)
|| self
.settings
.rules
.enabled(&Rule::TypingOnlyStandardLibraryImport));
if !(self.settings.rules.enabled(&Rule::UnusedImport)
|| self.settings.rules.enabled(&Rule::ImportStarUsage)
|| self.settings.rules.enabled(&Rule::RedefinedWhileUnused)
|| self.settings.rules.enabled(&Rule::UndefinedExport)
|| enforce_typing_imports)
|| self
.settings
.rules
.enabled(&Rule::GlobalVariableNotAssigned)
|| self
.settings
.rules
.enabled(&Rule::RuntimeImportInTypeCheckingBlock)
|| self
.settings
.rules
.enabled(&Rule::TypingOnlyFirstPartyImport)
|| self
.settings
.rules
.enabled(&Rule::TypingOnlyThirdPartyImport)
|| self
.settings
.rules
.enabled(&Rule::TypingOnlyStandardLibraryImport))
{
return;
}
@@ -4818,10 +4799,26 @@ impl<'a> Checker<'a> {
// Identify any valid runtime imports. If a module is imported at runtime, and
// used at runtime, then by default, we avoid flagging any other
// imports from that model as typing-only.
let runtime_imports: Vec<Vec<&Binding>> = if enforce_typing_imports {
if self.settings.flake8_type_checking.strict {
vec![]
} else {
let runtime_imports: Vec<Vec<&Binding>> = if self.settings.flake8_type_checking.strict {
vec![]
} else {
if self
.settings
.rules
.enabled(&Rule::RuntimeImportInTypeCheckingBlock)
|| self
.settings
.rules
.enabled(&Rule::TypingOnlyFirstPartyImport)
|| self
.settings
.rules
.enabled(&Rule::TypingOnlyThirdPartyImport)
|| self
.settings
.rules
.enabled(&Rule::TypingOnlyStandardLibraryImport)
{
self.ctx
.scopes
.iter()
@@ -4836,9 +4833,9 @@ impl<'a> Checker<'a> {
.collect::<Vec<_>>()
})
.collect::<Vec<_>>()
} else {
vec![]
}
} else {
vec![]
};
let mut diagnostics: Vec<Diagnostic> = vec![];
@@ -4960,7 +4957,23 @@ impl<'a> Checker<'a> {
}
}
if enforce_typing_imports {
if self
.settings
.rules
.enabled(&Rule::RuntimeImportInTypeCheckingBlock)
|| self
.settings
.rules
.enabled(&Rule::TypingOnlyFirstPartyImport)
|| self
.settings
.rules
.enabled(&Rule::TypingOnlyThirdPartyImport)
|| self
.settings
.rules
.enabled(&Rule::TypingOnlyStandardLibraryImport)
{
let runtime_imports: Vec<&Binding> = if self.settings.flake8_type_checking.strict {
vec![]
} else {
@@ -4977,9 +4990,7 @@ impl<'a> Checker<'a> {
if let Some(diagnostic) =
flake8_type_checking::rules::runtime_import_in_type_checking_block(binding)
{
if self.settings.rules.enabled(diagnostic.kind.rule()) {
diagnostics.push(diagnostic);
}
diagnostics.push(diagnostic);
}
if let Some(diagnostic) =
flake8_type_checking::rules::typing_only_runtime_import(
@@ -5276,7 +5287,7 @@ impl<'a> Checker<'a> {
}
overloaded_name = flake8_annotations::helpers::overloaded_name(self, &definition);
}
if self.is_stub {
if self.is_interface_definition {
if self.settings.rules.enabled(&Rule::DocstringInStub) {
flake8_pyi::rules::docstring_in_stubs(self, definition.docstring);
}
@@ -5299,13 +5310,13 @@ impl<'a> Checker<'a> {
// Extract a `Docstring` from a `Definition`.
let expr = definition.docstring.unwrap();
let contents = self.locator.slice(expr);
let indentation = self.locator.slice(Range::new(
let contents = self.locator.slice(&Range::from_located(expr));
let indentation = self.locator.slice(&Range::new(
Location::new(expr.location.row(), 0),
Location::new(expr.location.row(), expr.location.column()),
));
let body = str::raw_contents(contents);
let body = pydocstyle::helpers::raw_contents(contents);
let docstring = Docstring {
kind: definition.kind,
expr,

View File

@@ -1,8 +1,6 @@
use std::path::Path;
use ruff_diagnostics::Diagnostic;
use crate::registry::Rule;
use crate::registry::{Diagnostic, Rule};
use crate::rules::flake8_no_pep420::rules::implicit_namespace_package;
use crate::rules::pep8_naming::rules::invalid_module_name;
use crate::settings::Settings;

View File

@@ -4,15 +4,13 @@ use std::path::Path;
use rustpython_parser::ast::Suite;
use ruff_diagnostics::Diagnostic;
use ruff_python_ast::source_code::{Indexer, Locator, Stylist};
use ruff_python_ast::visitor::Visitor;
use crate::ast::visitor::Visitor;
use crate::directives::IsortDirectives;
use crate::registry::Rule;
use crate::registry::{Diagnostic, Rule};
use crate::rules::isort;
use crate::rules::isort::track::{Block, ImportTracker};
use crate::settings::{flags, Settings};
use crate::source_code::{Indexer, Locator, Stylist};
#[allow(clippy::too_many_arguments)]
pub fn check_imports(

View File

@@ -5,11 +5,8 @@ use itertools::Itertools;
use rustpython_parser::ast::Location;
use rustpython_parser::lexer::LexResult;
use ruff_diagnostics::Diagnostic;
use ruff_python_ast::source_code::{Locator, Stylist};
use ruff_python_ast::types::Range;
use crate::registry::{AsRule, Rule};
use crate::ast::types::Range;
use crate::registry::{Diagnostic, Rule};
use crate::rules::pycodestyle::logical_lines::{iter_logical_lines, TokenFlags};
use crate::rules::pycodestyle::rules::{
extraneous_whitespace, indentation, missing_whitespace, missing_whitespace_after_keyword,
@@ -18,6 +15,7 @@ use crate::rules::pycodestyle::rules::{
whitespace_before_parameters,
};
use crate::settings::{flags, Settings};
use crate::source_code::{Locator, Stylist};
/// Return the amount of indentation, expanding tabs to the next multiple of 8.
fn expand_indent(mut line: &str) -> usize {
@@ -59,7 +57,7 @@ pub fn check_logical_lines(
// Extract the indentation level.
let start_loc = line.mapping[0].1;
let start_line = locator.slice(Range::new(Location::new(start_loc.row(), 0), start_loc));
let start_line = locator.slice(&Range::new(Location::new(start_loc.row(), 0), start_loc));
let indent_level = expand_indent(start_line);
let indent_size = 4;
@@ -227,9 +225,8 @@ mod tests {
use rustpython_parser::lexer::LexResult;
use rustpython_parser::{lexer, Mode};
use ruff_python_ast::source_code::Locator;
use crate::checkers::logical_lines::iter_logical_lines;
use crate::source_code::Locator;
#[test]
fn split_logical_lines() {

View File

@@ -4,14 +4,12 @@ use log::warn;
use nohash_hasher::IntMap;
use rustpython_parser::ast::Location;
use ruff_diagnostics::{Diagnostic, Fix};
use ruff_python_ast::newlines::StrExt;
use ruff_python_ast::types::Range;
use crate::ast::types::Range;
use crate::codes::NoqaCode;
use crate::fix::Fix;
use crate::noqa;
use crate::noqa::{extract_file_exemption, Directive, Exemption};
use crate::registry::{AsRule, Rule};
use crate::registry::{Diagnostic, DiagnosticKind, Rule};
use crate::rule_redirects::get_redirect_target;
use crate::rules::ruff::rules::{UnusedCodes, UnusedNOQA};
use crate::settings::{flags, Settings};
@@ -23,7 +21,7 @@ pub fn check_noqa(
noqa_line_for: &IntMap<usize, usize>,
settings: &Settings,
autofix: flags::Autofix,
) -> Vec<usize> {
) {
let enforce_noqa = settings.rules.enabled(&Rule::UnusedNOQA);
// Whether the file is exempted from all checks.
@@ -39,7 +37,7 @@ pub fn check_noqa(
// Indices of diagnostics that were ignored by a `noqa` directive.
let mut ignored_diagnostics = vec![];
let lines: Vec<&str> = contents.universal_newlines().collect();
let lines: Vec<&str> = contents.lines().collect();
for lineno in commented_lines {
match extract_file_exemption(lines[lineno - 1]) {
Exemption::All => {
@@ -67,7 +65,7 @@ pub fn check_noqa(
// Remove any ignored diagnostics.
for (index, diagnostic) in diagnostics.iter().enumerate() {
if matches!(diagnostic.kind.rule(), Rule::BlanketNOQA) {
if matches!(diagnostic.kind, DiagnosticKind::BlanketNOQA(..)) {
continue;
}
@@ -98,7 +96,7 @@ pub fn check_noqa(
ignored_diagnostics.push(index);
continue;
}
(Directive::Codes(.., codes, _), matches) => {
(Directive::Codes(.., codes), matches) => {
if noqa::includes(diagnostic.kind.rule(), codes) {
matches.push(diagnostic.kind.rule().noqa_code());
ignored_diagnostics.push(index);
@@ -125,7 +123,7 @@ pub fn check_noqa(
ignored_diagnostics.push(index);
continue;
}
(Directive::Codes(.., codes, _), matches) => {
(Directive::Codes(.., codes), matches) => {
if noqa::includes(diagnostic.kind.rule(), codes) {
matches.push(diagnostic.kind.rule().noqa_code());
ignored_diagnostics.push(index);
@@ -141,7 +139,7 @@ pub fn check_noqa(
if enforce_noqa {
for (row, (directive, matches)) in noqa_directives {
match directive {
Directive::All(leading_spaces, start_byte, end_byte, trailing_spaces) => {
Directive::All(spaces, start_byte, end_byte) => {
if matches.is_empty() {
let start = lines[row][..start_byte].chars().count();
let end = start + lines[row][start_byte..end_byte].chars().count();
@@ -151,27 +149,15 @@ pub fn check_noqa(
Range::new(Location::new(row + 1, start), Location::new(row + 1, end)),
);
if autofix.into() && settings.rules.should_fix(diagnostic.kind.rule()) {
if start - leading_spaces == 0 && end == lines[row].chars().count() {
diagnostic.amend(Fix::deletion(
Location::new(row + 1, 0),
Location::new(row + 2, 0),
));
} else if end == lines[row].chars().count() {
diagnostic.amend(Fix::deletion(
Location::new(row + 1, start - leading_spaces),
Location::new(row + 1, end + trailing_spaces),
));
} else {
diagnostic.amend(Fix::deletion(
Location::new(row + 1, start),
Location::new(row + 1, end + trailing_spaces),
));
}
diagnostic.amend(Fix::deletion(
Location::new(row + 1, start - spaces),
Location::new(row + 1, lines[row].chars().count()),
));
}
diagnostics.push(diagnostic);
}
}
Directive::Codes(leading_spaces, start_byte, end_byte, codes, trailing_spaces) => {
Directive::Codes(spaces, start_byte, end_byte, codes) => {
let mut disabled_codes = vec![];
let mut unknown_codes = vec![];
let mut unmatched_codes = vec![];
@@ -231,28 +217,15 @@ pub fn check_noqa(
);
if autofix.into() && settings.rules.should_fix(diagnostic.kind.rule()) {
if valid_codes.is_empty() {
if start - leading_spaces == 0 && end == lines[row].chars().count()
{
diagnostic.amend(Fix::deletion(
Location::new(row + 1, 0),
Location::new(row + 2, 0),
));
} else if end == lines[row].chars().count() {
diagnostic.amend(Fix::deletion(
Location::new(row + 1, start - leading_spaces),
Location::new(row + 1, end + trailing_spaces),
));
} else {
diagnostic.amend(Fix::deletion(
Location::new(row + 1, start),
Location::new(row + 1, end + trailing_spaces),
));
}
diagnostic.amend(Fix::deletion(
Location::new(row + 1, start - spaces),
Location::new(row + 1, lines[row].chars().count()),
));
} else {
diagnostic.amend(Fix::replacement(
format!("# noqa: {}", valid_codes.join(", ")),
Location::new(row + 1, start),
Location::new(row + 1, end),
Location::new(row + 1, lines[row].chars().count()),
));
}
}
@@ -265,5 +238,7 @@ pub fn check_noqa(
}
ignored_diagnostics.sort_unstable();
ignored_diagnostics
for index in ignored_diagnostics.iter().rev() {
diagnostics.swap_remove(*index);
}
}

View File

@@ -2,11 +2,7 @@
use std::path::Path;
use ruff_diagnostics::Diagnostic;
use ruff_python_ast::newlines::StrExt;
use ruff_python_ast::source_code::{Locator, Stylist};
use crate::registry::Rule;
use crate::registry::{Diagnostic, Rule};
use crate::rules::flake8_executable::helpers::{extract_shebang, ShebangDirective};
use crate::rules::flake8_executable::rules::{
shebang_missing, shebang_newline, shebang_not_executable, shebang_python, shebang_whitespace,
@@ -19,11 +15,12 @@ use crate::rules::pygrep_hooks::rules::{blanket_noqa, blanket_type_ignore};
use crate::rules::pylint;
use crate::rules::pyupgrade::rules::unnecessary_coding_comment;
use crate::settings::{flags, Settings};
use crate::source_code::Stylist;
pub fn check_physical_lines(
path: &Path,
locator: &Locator,
stylist: &Stylist,
contents: &str,
commented_lines: &[usize],
doc_lines: &[usize],
settings: &Settings,
@@ -57,7 +54,7 @@ pub fn check_physical_lines(
let mut commented_lines_iter = commented_lines.iter().peekable();
let mut doc_lines_iter = doc_lines.iter().peekable();
for (index, line) in locator.contents().universal_newlines().enumerate() {
for (index, line) in contents.lines().enumerate() {
while commented_lines_iter
.next_if(|lineno| &(index + 1) == *lineno)
.is_some()
@@ -163,8 +160,8 @@ pub fn check_physical_lines(
if enforce_no_newline_at_end_of_file {
if let Some(diagnostic) = no_newline_at_end_of_file(
locator,
stylist,
contents,
autofix.into() && settings.rules.should_fix(&Rule::NoNewLineAtEndOfFile),
) {
diagnostics.push(diagnostic);
@@ -182,14 +179,13 @@ pub fn check_physical_lines(
#[cfg(test)]
mod tests {
use std::path::Path;
use ruff_python_ast::source_code::{Locator, Stylist};
use super::check_physical_lines;
use crate::registry::Rule;
use crate::settings::{flags, Settings};
use super::check_physical_lines;
use crate::source_code::{Locator, Stylist};
#[test]
fn e501_non_ascii_char() {
@@ -200,8 +196,8 @@ mod tests {
let check_with_max_line_length = |line_length: usize| {
check_physical_lines(
Path::new("foo.py"),
&locator,
&stylist,
line,
&[],
&[],
&Settings {

View File

@@ -4,22 +4,21 @@ use rustpython_parser::lexer::LexResult;
use rustpython_parser::Tok;
use crate::lex::docstring_detection::StateMachine;
use crate::registry::{AsRule, Rule};
use crate::registry::{Diagnostic, Rule};
use crate::rules::ruff::rules::Context;
use crate::rules::{
eradicate, flake8_commas, flake8_implicit_str_concat, flake8_pyi, flake8_quotes, pycodestyle,
pyupgrade, ruff,
};
use crate::settings::{flags, Settings};
use ruff_diagnostics::Diagnostic;
use ruff_python_ast::source_code::Locator;
use crate::source_code::Locator;
pub fn check_tokens(
locator: &Locator,
tokens: &[LexResult],
settings: &Settings,
autofix: flags::Autofix,
is_stub: bool,
is_interface_definition: bool,
) -> Vec<Diagnostic> {
let mut diagnostics: Vec<Diagnostic> = vec![];
@@ -165,7 +164,7 @@ pub fn check_tokens(
}
// PYI033
if enforce_type_comment_in_stub && is_stub {
if enforce_type_comment_in_stub && is_interface_definition {
diagnostics.extend(flake8_pyi::rules::type_comment_in_stub(tokens));
}

View File

@@ -1,23 +1,5 @@
use crate::registry::{Linter, Rule};
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub struct NoqaCode(&'static str, &'static str);
impl std::fmt::Display for NoqaCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "{}{}", self.0, self.1)
}
}
impl PartialEq<&str> for NoqaCode {
fn eq(&self, other: &&str) -> bool {
match other.strip_prefix(self.0) {
Some(suffix) => suffix == self.1,
None => false,
}
}
}
#[ruff_macros::map_codes]
pub fn code_to_rule(linter: Linter, code: &str) -> Option<Rule> {
#[allow(clippy::enum_glob_use)]
@@ -161,37 +143,34 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<Rule> {
(Pyflakes, "901") => Rule::RaiseNotImplemented,
// pylint
(Pylint, "C0414") => Rule::UselessImportAlias,
(Pylint, "C1901") => Rule::CompareToEmptyString,
(Pylint, "C3002") => Rule::UnnecessaryDirectLambdaCall,
(Pylint, "E0100") => Rule::YieldInInit,
(Pylint, "E0101") => Rule::ReturnInInit,
(Pylint, "E0117") => Rule::NonlocalWithoutBinding,
(Pylint, "E0118") => Rule::UsedPriorGlobalDeclaration,
(Pylint, "E0604") => Rule::InvalidAllObject,
(Pylint, "E0605") => Rule::InvalidAllFormat,
(Pylint, "W1508") => Rule::InvalidEnvvarDefault,
(Pylint, "E1142") => Rule::AwaitOutsideAsync,
(Pylint, "E1205") => Rule::LoggingTooManyArgs,
(Pylint, "E1206") => Rule::LoggingTooFewArgs,
(Pylint, "E1307") => Rule::BadStringFormatType,
(Pylint, "E1310") => Rule::BadStrStripCall,
(Pylint, "E1507") => Rule::InvalidEnvvarValue,
(Pylint, "E2502") => Rule::BidirectionalUnicode,
(Pylint, "R0133") => Rule::ComparisonOfConstant,
(Pylint, "E1310") => Rule::BadStrStripCall,
(Pylint, "C0414") => Rule::UselessImportAlias,
(Pylint, "C3002") => Rule::UnnecessaryDirectLambdaCall,
(Pylint, "E0117") => Rule::NonlocalWithoutBinding,
(Pylint, "E0118") => Rule::UsedPriorGlobalDeclaration,
(Pylint, "E1142") => Rule::AwaitOutsideAsync,
(Pylint, "R0206") => Rule::PropertyWithParameters,
(Pylint, "R0402") => Rule::ConsiderUsingFromImport,
(Pylint, "R0911") => Rule::TooManyReturnStatements,
(Pylint, "R0912") => Rule::TooManyBranches,
(Pylint, "R0913") => Rule::TooManyArguments,
(Pylint, "R0915") => Rule::TooManyStatements,
(Pylint, "R0133") => Rule::ComparisonOfConstant,
(Pylint, "R1701") => Rule::ConsiderMergingIsinstance,
(Pylint, "R1722") => Rule::ConsiderUsingSysExit,
(Pylint, "R2004") => Rule::MagicValueComparison,
(Pylint, "R5501") => Rule::CollapsibleElseIf,
(Pylint, "R2004") => Rule::MagicValueComparison,
(Pylint, "W0120") => Rule::UselessElseOnLoop,
(Pylint, "W0602") => Rule::GlobalVariableNotAssigned,
(Pylint, "W0603") => Rule::GlobalStatement,
(Pylint, "R0911") => Rule::TooManyReturnStatements,
(Pylint, "R0913") => Rule::TooManyArguments,
(Pylint, "R0912") => Rule::TooManyBranches,
(Pylint, "R0915") => Rule::TooManyStatements,
(Pylint, "W2901") => Rule::RedefinedLoopName,
// flake8-builtins
@@ -227,7 +206,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<Rule> {
(Flake8Bugbear, "026") => Rule::StarArgUnpackingAfterKeywordArg,
(Flake8Bugbear, "027") => Rule::EmptyMethodWithoutAbstractDecorator,
(Flake8Bugbear, "029") => Rule::ExceptWithEmptyTuple,
(Flake8Bugbear, "030") => Rule::ExceptWithNonExceptionClasses,
(Flake8Bugbear, "032") => Rule::UnintentionalTypeAnnotation,
(Flake8Bugbear, "904") => Rule::RaiseWithoutFromInsideExcept,
(Flake8Bugbear, "905") => Rule::ZipWithoutExplicitStrict,
@@ -375,7 +353,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<Rule> {
(Pyupgrade, "032") => Rule::FString,
(Pyupgrade, "033") => Rule::FunctoolsCache,
(Pyupgrade, "034") => Rule::ExtraneousParentheses,
(Pyupgrade, "035") => Rule::DeprecatedImport,
(Pyupgrade, "035") => Rule::ImportReplacements,
(Pyupgrade, "036") => Rule::OutdatedVersionBlock,
(Pyupgrade, "037") => Rule::QuotedAnnotation,
(Pyupgrade, "038") => Rule::IsinstanceWithTuple,

View File

@@ -107,21 +107,15 @@ pub fn extract_isort_directives(lxr: &[LexResult]) -> IsortDirectives {
// omit a space after the colon. The remaining action comments are
// required to include the space, and must appear on their own lines.
let comment_text = comment_text.trim_end();
if matches!(comment_text, "# isort: split" | "# ruff: isort: split") {
if comment_text == "# isort: split" {
splits.push(start.row());
} else if matches!(
comment_text,
"# isort: skip_file"
| "# isort:skip_file"
| "# ruff: isort: skip_file"
| "# ruff: isort:skip_file"
) {
} else if comment_text == "# isort: skip_file" || comment_text == "# isort:skip_file" {
return IsortDirectives {
skip_file: true,
..IsortDirectives::default()
};
} else if off.is_some() {
if comment_text == "# isort: on" || comment_text == "# ruff: isort: on" {
if comment_text == "# isort: on" {
if let Some(start) = off {
for row in start.row() + 1..=end.row() {
exclusions.insert(row);
@@ -132,7 +126,7 @@ pub fn extract_isort_directives(lxr: &[LexResult]) -> IsortDirectives {
} else {
if comment_text.contains("isort: skip") || comment_text.contains("isort:skip") {
exclusions.insert(start.row());
} else if comment_text == "# isort: off" || comment_text == "# ruff: isort: off" {
} else if comment_text == "# isort: off" {
off = Some(start);
}
}

View File

@@ -7,8 +7,8 @@ use rustpython_parser::ast::{Constant, ExprKind, Stmt, StmtKind, Suite};
use rustpython_parser::lexer::LexResult;
use rustpython_parser::Tok;
use ruff_python_ast::visitor;
use ruff_python_ast::visitor::Visitor;
use crate::ast::visitor;
use crate::ast::visitor::Visitor;
/// Extract doc lines (standalone comments) from a token sequence.
pub fn doc_lines_from_tokens(lxr: &[LexResult]) -> DocLines {

View File

@@ -1,6 +1,3 @@
use ruff_python_ast::visibility::{
class_visibility, function_visibility, method_visibility, Modifier, Visibility, VisibleScope,
};
use rustpython_parser::ast::{Expr, Stmt};
#[derive(Debug, Clone)]
@@ -33,36 +30,3 @@ pub enum Documentable {
Class,
Function,
}
pub fn transition_scope(scope: &VisibleScope, stmt: &Stmt, kind: &Documentable) -> VisibleScope {
match kind {
Documentable::Function => VisibleScope {
modifier: Modifier::Function,
visibility: match scope {
VisibleScope {
modifier: Modifier::Module,
visibility: Visibility::Public,
} => function_visibility(stmt),
VisibleScope {
modifier: Modifier::Class,
visibility: Visibility::Public,
} => method_visibility(stmt),
_ => Visibility::Private,
},
},
Documentable::Class => VisibleScope {
modifier: Modifier::Class,
visibility: match scope {
VisibleScope {
modifier: Modifier::Module,
visibility: Visibility::Public,
} => class_visibility(stmt),
VisibleScope {
modifier: Modifier::Class,
visibility: Visibility::Public,
} => class_visibility(stmt),
_ => Visibility::Private,
},
},
}
}

View File

@@ -3,7 +3,7 @@
use rustpython_parser::ast::{Constant, Expr, ExprKind, Stmt, StmtKind};
use crate::docstrings::definition::{Definition, DefinitionKind, Documentable};
use ruff_python_ast::visibility::{Modifier, VisibleScope};
use crate::visibility::{Modifier, VisibleScope};
/// Extract a docstring from a function or class body.
pub fn docstring_from(suite: &[Stmt]) -> Option<&Expr> {

View File

@@ -1,7 +1,7 @@
use strum_macros::EnumIter;
use crate::ast::whitespace;
use crate::docstrings::styles::SectionStyle;
use ruff_python_ast::whitespace;
#[derive(EnumIter, PartialEq, Eq, Debug, Clone, Copy)]
pub enum SectionKind {

View File

@@ -1,3 +1,6 @@
use rustpython_parser::ast::Location;
use serde::{Deserialize, Serialize};
#[derive(Debug, Copy, Clone, Hash)]
pub enum FixMode {
Generate,
@@ -15,3 +18,40 @@ impl From<bool> for FixMode {
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Fix {
pub content: String,
pub location: Location,
pub end_location: Location,
}
impl Fix {
pub const fn deletion(start: Location, end: Location) -> Self {
Self {
content: String::new(),
location: start,
end_location: end,
}
}
pub fn replacement(content: String, start: Location, end: Location) -> Self {
debug_assert!(!content.is_empty(), "Prefer `Fix::deletion`");
Self {
content,
location: start,
end_location: end,
}
}
pub fn insertion(content: String, at: Location) -> Self {
debug_assert!(!content.is_empty(), "Insert content is empty");
Self {
content,
location: at,
end_location: at,
}
}
}

View File

@@ -20,7 +20,6 @@ use crate::rules::{
};
use crate::settings::options::Options;
use crate::settings::pyproject::Pyproject;
use crate::settings::types::PythonVersion;
use crate::warn_user;
const DEFAULT_SELECTORS: &[RuleSelector] = &[
@@ -425,15 +424,6 @@ pub fn convert(
}
}
if let Some(project) = &external_config.project {
if let Some(requires_python) = &project.requires_python {
if options.target_version.is_none() {
options.target_version =
PythonVersion::get_minimum_supported_version(requires_python);
}
}
}
// Create the pyproject.toml.
Ok(Pyproject::new(options))
}
@@ -449,17 +439,13 @@ fn resolve_select(plugins: &[Plugin]) -> HashSet<RuleSelector> {
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use std::str::FromStr;
use anyhow::Result;
use itertools::Itertools;
use pep440_rs::VersionSpecifiers;
use pretty_assertions::assert_eq;
use super::super::plugin::Plugin;
use super::convert;
use crate::flake8_to_ruff::converter::DEFAULT_SELECTORS;
use crate::flake8_to_ruff::pep621::Project;
use crate::flake8_to_ruff::ExternalConfig;
use crate::registry::Linter;
use crate::rule_selector::RuleSelector;
@@ -467,7 +453,6 @@ mod tests {
use crate::rules::{flake8_quotes, pydocstyle};
use crate::settings::options::Options;
use crate::settings::pyproject::Pyproject;
use crate::settings::types::PythonVersion;
fn default_options(plugins: impl IntoIterator<Item = RuleSelector>) -> Options {
Options {
@@ -624,25 +609,4 @@ mod tests {
Ok(())
}
#[test]
fn it_converts_project_requires_python() -> Result<()> {
let actual = convert(
&HashMap::from([("flake8".to_string(), HashMap::default())]),
&ExternalConfig {
project: Some(&Project {
requires_python: Some(VersionSpecifiers::from_str(">=3.8.16, <3.11")?),
}),
..ExternalConfig::default()
},
Some(vec![]),
)?;
let expected = Pyproject::new(Options {
target_version: Some(PythonVersion::Py38),
..default_options([])
});
assert_eq!(actual, expected);
Ok(())
}
}

View File

@@ -1,10 +1,8 @@
use super::black::Black;
use super::isort::Isort;
use super::pep621::Project;
#[derive(Default)]
pub struct ExternalConfig<'a> {
pub black: Option<&'a Black>,
pub isort: Option<&'a Isort>,
pub project: Option<&'a Project>,
}

View File

@@ -3,7 +3,6 @@ mod converter;
mod external_config;
mod isort;
mod parser;
pub mod pep621;
mod plugin;
mod pyproject;

View File

@@ -1,10 +0,0 @@
//! Extract PEP 621 configuration settings from a pyproject.toml.
use pep440_rs::VersionSpecifiers;
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct Project {
#[serde(alias = "requires-python", alias = "requires_python")]
pub requires_python: Option<VersionSpecifiers>,
}

View File

@@ -5,7 +5,6 @@ use serde::{Deserialize, Serialize};
use super::black::Black;
use super::isort::Isort;
use super::pep621::Project;
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Tools {
@@ -16,7 +15,6 @@ pub struct Tools {
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Pyproject {
pub tool: Option<Tools>,
pub project: Option<Project>,
}
pub fn parse<P: AsRef<Path>>(path: P) -> Result<Pyproject> {

View File

@@ -74,20 +74,10 @@ pub fn normalize_path_to<P: AsRef<Path>, R: AsRef<Path>>(path: P, project_root:
}
/// Convert an absolute path to be relative to the current working directory.
pub fn relativize_path<P: AsRef<Path>>(path: P) -> String {
pub fn relativize_path(path: impl AsRef<Path>) -> String {
let path = path.as_ref();
if let Ok(path) = path.strip_prefix(&*path_dedot::CWD) {
return format!("{}", path.display());
}
format!("{}", path.display())
}
/// Convert an absolute path to be relative to the specified project root.
pub fn relativize_path_to<P: AsRef<Path>, R: AsRef<Path>>(path: P, project_root: R) -> String {
format!(
"{}",
pathdiff::diff_paths(&path, project_root)
.expect("Could not diff paths")
.display()
)
}

View File

@@ -5,16 +5,18 @@
//!
//! [Ruff]: https://github.com/charliermarsh/ruff
pub use ruff_python_ast::source_code::round_trip;
pub use ruff_python_ast::types::Range;
pub use ast::types::Range;
use cfg_if::cfg_if;
pub use rule_selector::RuleSelector;
pub use rules::pycodestyle::rules::IOError;
pub use violation::{AutofixKind, Availability as AutofixAvailability};
mod ast;
mod autofix;
mod checkers;
mod codes;
mod cst;
pub mod directives;
mod directives;
mod doc_lines;
mod docstrings;
pub mod fix;
@@ -25,13 +27,24 @@ pub mod linter;
pub mod logging;
pub mod message;
mod noqa;
pub mod packaging;
pub mod registry;
pub mod resolver;
mod rule_redirects;
mod rule_selector;
pub mod rules;
mod rules;
pub mod settings;
pub mod source_code;
mod violation;
mod visibility;
cfg_if! {
if #[cfg(target_family = "wasm")] {
mod lib_wasm;
pub use lib_wasm::check;
} else {
pub mod packaging;
}
}
#[cfg(test)]
mod test;

View File

@@ -2,22 +2,22 @@ use std::path::Path;
use rustpython_parser::ast::Location;
use rustpython_parser::lexer::LexResult;
use serde::{Deserialize, Serialize};
use serde::Serialize;
use wasm_bindgen::prelude::*;
use ruff::directives;
use ruff::linter::{check_path, LinterResult};
use ruff::registry::AsRule;
use ruff::rules::{
use crate::directives;
use crate::linter::{check_path, LinterResult};
use crate::registry::Rule;
use crate::rules::{
flake8_annotations, flake8_bandit, flake8_bugbear, flake8_builtins, flake8_comprehensions,
flake8_errmsg, flake8_implicit_str_concat, flake8_import_conventions, flake8_pytest_style,
flake8_quotes, flake8_self, flake8_tidy_imports, flake8_type_checking, flake8_unused_arguments,
isort, mccabe, pep8_naming, pycodestyle, pydocstyle, pylint, pyupgrade,
};
use ruff::settings::configuration::Configuration;
use ruff::settings::options::Options;
use ruff::settings::{defaults, flags, Settings};
use ruff_python_ast::source_code::{Indexer, Locator, Stylist};
use crate::settings::configuration::Configuration;
use crate::settings::options::Options;
use crate::settings::{defaults, flags, Settings};
use crate::source_code::{Indexer, Locator, Stylist};
const VERSION: &str = env!("CARGO_PKG_VERSION");
@@ -49,17 +49,34 @@ export interface Diagnostic {
};
"#;
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
pub struct ExpandedMessage {
pub code: String,
pub message: String,
pub location: Location,
pub end_location: Location,
pub fix: Option<ExpandedFix>,
#[derive(Serialize)]
struct ExpandedMessage {
code: SerializeRuleAsCode,
message: String,
location: Location,
end_location: Location,
fix: Option<ExpandedFix>,
}
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
pub struct ExpandedFix {
struct SerializeRuleAsCode(Rule);
impl Serialize for SerializeRuleAsCode {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.0.noqa_code().to_string())
}
}
impl From<Rule> for SerializeRuleAsCode {
fn from(rule: Rule) -> Self {
Self(rule)
}
}
#[derive(Serialize)]
struct ExpandedFix {
content: String,
message: Option<String>,
location: Location,
@@ -69,16 +86,7 @@ pub struct ExpandedFix {
#[wasm_bindgen(start)]
pub fn run() {
use log::Level;
// When the `console_error_panic_hook` feature is enabled, we can call the
// `set_panic_hook` function at least once during initialization, and then
// we will get better error messages if our code ever panics.
//
// For more details see
// https://github.com/rustwasm/console_error_panic_hook#readme
#[cfg(feature = "console_error_panic_hook")]
console_error_panic_hook::set_once();
console_log::init_with_level(Level::Debug).expect("Initializing logger went wrong.");
}
@@ -199,14 +207,14 @@ pub fn check(contents: &str, options: JsValue) -> Result<JsValue, JsValue> {
let messages: Vec<ExpandedMessage> = diagnostics
.into_iter()
.map(|message| ExpandedMessage {
code: message.kind.rule().noqa_code().to_string(),
message: message.kind.body,
location: message.location,
end_location: message.end_location,
fix: message.fix.map(|fix| ExpandedFix {
.map(|diagnostic| ExpandedMessage {
code: diagnostic.kind.rule().clone().into(),
message: diagnostic.kind.body(),
location: diagnostic.location,
end_location: diagnostic.end_location,
fix: diagnostic.fix.map(|fix| ExpandedFix {
content: fix.content,
message: message.kind.suggestion,
message: diagnostic.kind.commit(),
location: fix.location,
end_location: fix.end_location,
}),
@@ -215,3 +223,55 @@ pub fn check(contents: &str, options: JsValue) -> Result<JsValue, JsValue> {
Ok(serde_wasm_bindgen::to_value(&messages)?)
}
#[cfg(test)]
mod test {
use js_sys;
use wasm_bindgen_test::*;
use super::*;
macro_rules! check {
($source:expr, $config:expr, $expected:expr) => {{
let foo = js_sys::JSON::parse($config).unwrap();
match check($source, foo) {
Ok(output) => {
let result: Vec<Message> = serde_wasm_bindgen::from_value(output).unwrap();
assert_eq!(result, $expected);
}
Err(e) => assert!(false, "{:#?}", e),
}
}};
}
#[wasm_bindgen_test]
fn empty_config() {
check!(
"if (1, 2): pass",
r#"{}"#,
[ExpandedMessage {
code: Rule::IfTuple.into(),
message: "If test is a tuple, which is always `True`".to_string(),
location: Location::new(1, 0),
end_location: Location::new(1, 15),
fix: None,
}]
);
}
#[wasm_bindgen_test]
fn partial_config() {
check!("if (1, 2): pass", r#"{"ignore": ["F"]}"#, []);
}
#[wasm_bindgen_test]
fn partial_nested_config() {
let config = r#"{
"select": ["Q"],
"flake8-quotes": {
"inline-quotes": "single"
}
}"#;
check!(r#"print('hello world')"#, config, []);
}
}

View File

@@ -8,10 +8,6 @@ use rustc_hash::FxHashMap;
use rustpython_parser::lexer::LexResult;
use rustpython_parser::ParseError;
use ruff_diagnostics::Diagnostic;
use ruff_python_ast::source_code::{Indexer, Locator, Stylist};
use ruff_python_stdlib::path::is_python_stub_file;
use crate::autofix::fix_file;
use crate::checkers::ast::check_ast;
use crate::checkers::filesystem::check_file_path;
@@ -24,9 +20,11 @@ use crate::directives::Directives;
use crate::doc_lines::{doc_lines_from_ast, doc_lines_from_tokens};
use crate::message::{Message, Source};
use crate::noqa::{add_noqa, rule_is_ignored};
use crate::registry::{AsRule, Rule};
use crate::registry::{Diagnostic, Rule};
use crate::resolver::is_interface_definition_path;
use crate::rules::pycodestyle;
use crate::settings::{flags, Settings};
use crate::source_code::{Indexer, Locator, Stylist};
use crate::{directives, fs};
const CARGO_PKG_NAME: &str = env!("CARGO_PKG_NAME");
@@ -86,8 +84,14 @@ pub fn check_path(
.iter_enabled()
.any(|rule_code| rule_code.lint_source().is_tokens())
{
let is_stub = is_python_stub_file(path);
diagnostics.extend(check_tokens(locator, &tokens, settings, autofix, is_stub));
let is_interface_definition = is_interface_definition_path(path);
diagnostics.extend(check_tokens(
locator,
&tokens,
settings,
autofix,
is_interface_definition,
));
}
// Run the filesystem-based rules.
@@ -191,8 +195,8 @@ pub fn check_path(
{
diagnostics.extend(check_physical_lines(
path,
locator,
stylist,
contents,
indexer.commented_lines(),
&doc_lines,
settings,
@@ -204,7 +208,7 @@ pub fn check_path(
if !diagnostics.is_empty() && !settings.per_file_ignores.is_empty() {
let ignores = fs::ignores_from_path(path, &settings.per_file_ignores);
if !ignores.is_empty() {
diagnostics.retain(|diagnostic| !ignores.contains(diagnostic.kind.rule()));
diagnostics.retain(|diagnostic| !ignores.contains(&diagnostic.kind.rule()));
}
};
@@ -215,7 +219,7 @@ pub fn check_path(
.iter_enabled()
.any(|rule_code| rule_code.lint_source().is_noqa())
{
let ignored = check_noqa(
check_noqa(
&mut diagnostics,
contents,
indexer.commented_lines(),
@@ -223,11 +227,6 @@ pub fn check_path(
settings,
error.as_ref().map_or(autofix, |_| flags::Autofix::Disabled),
);
if noqa.into() {
for index in ignored.iter().rev() {
diagnostics.swap_remove(*index);
}
}
}
LinterResult::new(diagnostics, error)

View File

@@ -1,31 +1,8 @@
use std::sync::Mutex;
use anyhow::Result;
use colored::Colorize;
use fern;
use log::Level;
use once_cell::sync::Lazy;
pub(crate) static WARNINGS: Lazy<Mutex<Vec<&'static str>>> = Lazy::new(Mutex::default);
/// Warn a user once, with uniqueness determined by the given ID.
#[macro_export]
macro_rules! warn_user_once_by_id {
($id:expr, $($arg:tt)*) => {
use colored::Colorize;
use log::warn;
if let Ok(mut states) = $crate::logging::WARNINGS.lock() {
if !states.contains(&$id) {
let message = format!("{}", format_args!($($arg)*));
warn!("{}", message.bold());
states.push($id);
}
}
};
}
/// Warn a user once, with uniqueness determined by the calling location itself.
#[macro_export]
macro_rules! warn_user_once {
($($arg:tt)*) => {

View File

@@ -3,9 +3,10 @@ use std::cmp::Ordering;
pub use rustpython_parser::ast::Location;
use serde::{Deserialize, Serialize};
use ruff_diagnostics::{Diagnostic, DiagnosticKind, Fix};
use ruff_python_ast::source_code::Locator;
use ruff_python_ast::types::Range;
use crate::ast::types::Range;
use crate::fix::Fix;
use crate::registry::{Diagnostic, DiagnosticKind};
use crate::source_code::Locator;
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Message {
@@ -73,9 +74,9 @@ impl Source {
} else {
Location::new(diagnostic.end_location.row() + 1, 0)
};
let source = locator.slice(Range::new(location, end_location));
let source = locator.slice(&Range::new(location, end_location));
let num_chars_in_range = locator
.slice(Range::new(diagnostic.location, diagnostic.end_location))
.slice(&Range::new(diagnostic.location, diagnostic.end_location))
.chars()
.count();
Source {

View File

@@ -11,18 +11,15 @@ use regex::Regex;
use rustc_hash::{FxHashMap, FxHashSet};
use rustpython_parser::ast::Location;
use ruff_diagnostics::Diagnostic;
use ruff_python_ast::newlines::StrExt;
use ruff_python_ast::source_code::{LineEnding, Locator};
use ruff_python_ast::types::Range;
use crate::ast::types::Range;
use crate::codes::NoqaCode;
use crate::registry::{AsRule, Rule};
use crate::registry::{Diagnostic, Rule};
use crate::rule_redirects::get_redirect_target;
use crate::source_code::{LineEnding, Locator};
static NOQA_LINE_REGEX: Lazy<Regex> = Lazy::new(|| {
Regex::new(
r"(?P<leading_spaces>\s*)(?P<noqa>(?i:# noqa)(?::\s?(?P<codes>(?:[A-Z]+[0-9]+)(?:[,\s]+[A-Z]+[0-9]+)*))?)(?P<trailing_spaces>\s*)",
r"(?P<spaces>\s*)(?P<noqa>(?i:# noqa)(?::\s?(?P<codes>([A-Z]+[0-9]+(?:[,\s]+)?)+))?)",
)
.unwrap()
});
@@ -74,42 +71,35 @@ pub fn extract_file_exemption(line: &str) -> Exemption {
#[derive(Debug)]
pub enum Directive<'a> {
None,
All(usize, usize, usize, usize),
Codes(usize, usize, usize, Vec<&'a str>, usize),
All(usize, usize, usize),
Codes(usize, usize, usize, Vec<&'a str>),
}
/// Extract the noqa `Directive` from a line of Python source code.
pub fn extract_noqa_directive(line: &str) -> Directive {
match NOQA_LINE_REGEX.captures(line) {
Some(caps) => match caps.name("leading_spaces") {
Some(leading_spaces) => match caps.name("trailing_spaces") {
Some(trailing_spaces) => match caps.name("noqa") {
Some(noqa) => match caps.name("codes") {
Some(codes) => {
let codes: Vec<&str> = SPLIT_COMMA_REGEX
.split(codes.as_str().trim())
.map(str::trim)
.filter(|code| !code.is_empty())
.collect();
if codes.is_empty() {
warn!("Expected rule codes on `noqa` directive: \"{line}\"");
}
Directive::Codes(
leading_spaces.as_str().chars().count(),
noqa.start(),
noqa.end(),
codes,
trailing_spaces.as_str().chars().count(),
)
Some(caps) => match caps.name("spaces") {
Some(spaces) => match caps.name("noqa") {
Some(noqa) => match caps.name("codes") {
Some(codes) => {
let codes: Vec<&str> = SPLIT_COMMA_REGEX
.split(codes.as_str().trim())
.map(str::trim)
.filter(|code| !code.is_empty())
.collect();
if codes.is_empty() {
warn!("Expected rule codes on `noqa` directive: \"{line}\"");
}
None => Directive::All(
leading_spaces.as_str().chars().count(),
Directive::Codes(
spaces.as_str().chars().count(),
noqa.start(),
noqa.end(),
trailing_spaces.as_str().chars().count(),
),
},
None => Directive::None,
codes,
)
}
None => {
Directive::All(spaces.as_str().chars().count(), noqa.start(), noqa.end())
}
},
None => Directive::None,
},
@@ -136,14 +126,14 @@ pub fn rule_is_ignored(
locator: &Locator,
) -> bool {
let noqa_lineno = noqa_line_for.get(&lineno).unwrap_or(&lineno);
let line = locator.slice(Range::new(
let line = locator.slice(&Range::new(
Location::new(*noqa_lineno, 0),
Location::new(noqa_lineno + 1, 0),
));
match extract_noqa_directive(line) {
Directive::None => false,
Directive::All(..) => true,
Directive::Codes(.., codes, _) => includes(code, &codes),
Directive::Codes(.., codes) => includes(code, &codes),
}
}
@@ -182,7 +172,7 @@ fn add_noqa_inner(
// Codes that are globally exempted (within the current file).
let mut file_exemptions: Vec<NoqaCode> = vec![];
let lines: Vec<&str> = contents.universal_newlines().collect();
let lines: Vec<&str> = contents.lines().collect();
for lineno in commented_lines {
match extract_file_exemption(lines[lineno - 1]) {
Exemption::All => {
@@ -224,7 +214,7 @@ fn add_noqa_inner(
Directive::All(..) => {
continue;
}
Directive::Codes(.., codes, _) => {
Directive::Codes(.., codes) => {
if includes(diagnostic.kind.rule(), &codes) {
continue;
}
@@ -244,7 +234,7 @@ fn add_noqa_inner(
Directive::All(..) => {
continue;
}
Directive::Codes(.., codes, _) => {
Directive::Codes(.., codes) => {
if includes(diagnostic.kind.rule(), &codes) {
continue;
}
@@ -264,7 +254,7 @@ fn add_noqa_inner(
let mut count: usize = 0;
let mut output = String::new();
for (lineno, line) in lines.into_iter().enumerate() {
for (lineno, line) in contents.lines().enumerate() {
match matches_by_line.get(&lineno) {
None => {
output.push_str(line);
@@ -289,7 +279,7 @@ fn add_noqa_inner(
output.push_str(line);
output.push_str(line_ending);
}
Directive::Codes(_, start_byte, _, existing, _) => {
Directive::Codes(_, start_byte, _, existing) => {
// Reconstruct the line based on the preserved rule codes.
// This enables us to tally the number of edits.
let mut formatted = String::with_capacity(line.len());
@@ -342,13 +332,12 @@ mod tests {
use nohash_hasher::IntMap;
use rustpython_parser::ast::Location;
use ruff_diagnostics::Diagnostic;
use ruff_python_ast::source_code::LineEnding;
use ruff_python_ast::types::Range;
use crate::ast::types::Range;
use crate::noqa::{add_noqa_inner, NOQA_LINE_REGEX};
use crate::registry::Diagnostic;
use crate::rules::pycodestyle::rules::AmbiguousVariableName;
use crate::rules::pyflakes;
use crate::source_code::LineEnding;
#[test]
fn regex() {

View File

@@ -1,12 +1,15 @@
//! Registry of all [`Rule`] implementations.
//! Registry of [`Rule`] to [`DiagnosticKind`] mappings.
use ruff_macros::RuleNamespace;
use rustpython_parser::ast::Location;
use serde::{Deserialize, Serialize};
use strum_macros::{AsRefStr, EnumIter};
use ruff_diagnostics::Violation;
use ruff_macros::RuleNamespace;
use crate::ast::types::Range;
use crate::codes::{self, RuleCodePrefix};
use crate::fix::Fix;
use crate::rules;
use crate::violation::Violation;
ruff_macros::register_rules!(
// pycodestyle errors
@@ -146,8 +149,6 @@ ruff_macros::register_rules!(
rules::pylint::rules::YieldInInit,
rules::pylint::rules::InvalidAllObject,
rules::pylint::rules::InvalidAllFormat,
rules::pylint::rules::InvalidEnvvarDefault,
rules::pylint::rules::InvalidEnvvarValue,
rules::pylint::rules::BadStringFormatType,
rules::pylint::rules::BidirectionalUnicode,
rules::pylint::rules::BadStrStripCall,
@@ -160,7 +161,6 @@ ruff_macros::register_rules!(
rules::pylint::rules::PropertyWithParameters,
rules::pylint::rules::ReturnInInit,
rules::pylint::rules::ConsiderUsingFromImport,
rules::pylint::rules::CompareToEmptyString,
rules::pylint::rules::ComparisonOfConstant,
rules::pylint::rules::ConsiderMergingIsinstance,
rules::pylint::rules::ConsiderUsingSysExit,
@@ -209,7 +209,6 @@ ruff_macros::register_rules!(
rules::flake8_bugbear::rules::RaiseWithoutFromInsideExcept,
rules::flake8_bugbear::rules::ZipWithoutExplicitStrict,
rules::flake8_bugbear::rules::ExceptWithEmptyTuple,
rules::flake8_bugbear::rules::ExceptWithNonExceptionClasses,
rules::flake8_bugbear::rules::UnintentionalTypeAnnotation,
// flake8-blind-except
rules::flake8_blind_except::rules::BlindExcept,
@@ -341,7 +340,7 @@ ruff_macros::register_rules!(
rules::pyupgrade::rules::FString,
rules::pyupgrade::rules::FunctoolsCache,
rules::pyupgrade::rules::ExtraneousParentheses,
rules::pyupgrade::rules::DeprecatedImport,
rules::pyupgrade::rules::ImportReplacements,
rules::pyupgrade::rules::OutdatedVersionBlock,
rules::pyupgrade::rules::QuotedAnnotation,
rules::pyupgrade::rules::IsinstanceWithTuple,
@@ -610,20 +609,6 @@ ruff_macros::register_rules!(
rules::flake8_django::rules::NonLeadingReceiverDecorator,
);
impl Rule {
pub fn from_code(code: &str) -> Result<Self, FromCodeError> {
let (linter, code) = Linter::parse_code(code).ok_or(FromCodeError::Unknown)?;
let prefix: RuleCodePrefix = RuleCodePrefix::parse(&linter, code)?;
Ok(prefix.into_iter().next().unwrap())
}
}
#[derive(thiserror::Error, Debug)]
pub enum FromCodeError {
#[error("unknown rule code")]
Unknown,
}
#[derive(EnumIter, Debug, PartialEq, Eq, Clone, Hash, RuleNamespace)]
pub enum Linter {
/// [Pyflakes](https://pypi.org/project/pyflakes/)
@@ -805,7 +790,7 @@ impl Linter {
}
}
#[derive(is_macro::Is, Copy, Clone)]
#[derive(is_macro::Is)]
pub enum LintSource {
Ast,
Io,
@@ -820,9 +805,9 @@ pub enum LintSource {
impl Rule {
/// The source for the diagnostic (either the AST, the filesystem, or the
/// physical lines).
pub const fn lint_source(&self) -> LintSource {
pub const fn lint_source(&self) -> &'static LintSource {
match self {
Rule::UnusedNOQA => LintSource::Noqa,
Rule::UnusedNOQA => &LintSource::Noqa,
Rule::BlanketNOQA
| Rule::BlanketTypeIgnore
| Rule::DocLineTooLong
@@ -838,7 +823,7 @@ impl Rule {
| Rule::ShebangWhitespace
| Rule::TrailingWhitespace
| Rule::IndentationContainsTabs
| Rule::BlankLineContainsWhitespace => LintSource::PhysicalLines,
| Rule::BlankLineContainsWhitespace => &LintSource::PhysicalLines,
Rule::AmbiguousUnicodeCharacterComment
| Rule::AmbiguousUnicodeCharacterDocstring
| Rule::AmbiguousUnicodeCharacterString
@@ -857,10 +842,10 @@ impl Rule {
| Rule::UselessSemicolon
| Rule::MultipleStatementsOnOneLineSemicolon
| Rule::TrailingCommaProhibited
| Rule::TypeCommentInStub => LintSource::Tokens,
Rule::IOError => LintSource::Io,
Rule::UnsortedImports | Rule::MissingRequiredImport => LintSource::Imports,
Rule::ImplicitNamespacePackage | Rule::InvalidModuleName => LintSource::Filesystem,
| Rule::TypeCommentInStub => &LintSource::Tokens,
Rule::IOError => &LintSource::Io,
Rule::UnsortedImports | Rule::MissingRequiredImport => &LintSource::Imports,
Rule::ImplicitNamespacePackage | Rule::InvalidModuleName => &LintSource::Filesystem,
#[cfg(feature = "logical_lines")]
Rule::IndentationWithInvalidMultiple
| Rule::IndentationWithInvalidMultipleComment
@@ -892,12 +877,43 @@ impl Rule {
| Rule::WhitespaceAfterOpenBracket
| Rule::WhitespaceBeforeCloseBracket
| Rule::WhitespaceBeforeParameters
| Rule::WhitespaceBeforePunctuation => LintSource::LogicalLines,
_ => LintSource::Ast,
| Rule::WhitespaceBeforePunctuation => &LintSource::LogicalLines,
_ => &LintSource::Ast,
}
}
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Diagnostic {
pub kind: DiagnosticKind,
pub location: Location,
pub end_location: Location,
pub fix: Option<Fix>,
pub parent: Option<Location>,
}
impl Diagnostic {
pub fn new<K: Into<DiagnosticKind>>(kind: K, range: Range) -> Self {
Self {
kind: kind.into(),
location: range.location,
end_location: range.end_location,
fix: None,
parent: None,
}
}
pub fn amend(&mut self, fix: Fix) -> &mut Self {
self.fix = Some(fix);
self
}
pub fn parent(&mut self, parent: Location) -> &mut Self {
self.parent = Some(parent);
self
}
}
/// Pairs of checks that shouldn't be enabled together.
pub const INCOMPATIBLE_CODES: &[(Rule, Rule, &str); 2] = &[
(

View File

@@ -10,7 +10,6 @@ use ignore::{DirEntry, WalkBuilder, WalkState};
use itertools::Itertools;
use log::debug;
use path_absolutize::path_dedot;
use ruff_python_stdlib::path::is_python_file;
use rustc_hash::FxHashSet;
use crate::fs;
@@ -127,8 +126,7 @@ pub fn resolve_configuration(
}
// Resolve the current path.
let options = pyproject::load_options(&path)
.map_err(|err| anyhow!("Failed to parse `{}`: {}", path.display(), err))?;
let options = pyproject::load_options(&path)?;
let project_root = relativity.resolve(&path);
let configuration = Configuration::from_options(options, &project_root)?;
@@ -193,9 +191,20 @@ fn match_exclusion(file_path: &str, file_basename: &str, exclusion: &globset::Gl
exclusion.is_match(file_path) || exclusion.is_match(file_basename)
}
/// Return `true` if the [`Path`] appears to be that of a Python file.
fn is_python_path(path: &Path) -> bool {
path.extension()
.map_or(false, |ext| ext == "py" || ext == "pyi")
}
/// Return `true` if the [`Path`] appears to be that of a Python interface definition file (`.pyi`).
pub fn is_interface_definition_path(path: &Path) -> bool {
path.extension().map_or(false, |ext| ext == "pyi")
}
/// Return `true` if the [`DirEntry`] appears to be that of a Python file.
pub fn is_python_entry(entry: &DirEntry) -> bool {
is_python_file(entry.path())
is_python_path(entry.path())
&& !entry
.file_type()
.map_or(false, |file_type| file_type.is_dir())
@@ -421,13 +430,28 @@ mod tests {
use crate::fs;
use crate::resolver::{
is_file_excluded, match_exclusion, resolve_settings_with_processor, NoOpProcessor,
PyprojectDiscovery, Relativity, Resolver,
is_file_excluded, is_python_path, match_exclusion, resolve_settings_with_processor,
NoOpProcessor, PyprojectDiscovery, Relativity, Resolver,
};
use crate::settings::pyproject::find_settings_toml;
use crate::settings::types::FilePattern;
use crate::test::test_resource_path;
#[test]
fn inclusions() {
let path = Path::new("foo/bar/baz.py").absolutize().unwrap();
assert!(is_python_path(&path));
let path = Path::new("foo/bar/baz.pyi").absolutize().unwrap();
assert!(is_python_path(&path));
let path = Path::new("foo/bar/baz.js").absolutize().unwrap();
assert!(!is_python_path(&path));
let path = Path::new("foo/bar/baz").absolutize().unwrap();
assert!(!is_python_path(&path));
}
fn make_exclusion(file_pattern: FilePattern) -> GlobSet {
let mut builder = globset::GlobSetBuilder::new();
file_pattern.add_to(&mut builder).unwrap();

View File

@@ -18,7 +18,9 @@ static REDIRECTS: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
// The following are here because we don't yet have the many-to-one mapping enabled.
("SIM111", "SIM110"),
// The following are deprecated.
("C", "C4"),
("C9", "C90"),
("T", "T10"),
("T1", "T10"),
("T2", "T20"),
// TODO(charlie): Remove by 2023-02-01.

View File

@@ -15,17 +15,9 @@ use crate::rule_redirects::get_redirect;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum RuleSelector {
/// Select all rules.
/// All rules
All,
/// Legacy category to select both the `mccabe` and `flake8-comprehensions` linters
/// via a single selector.
C,
/// Legacy category to select both the `flake8-debugger` and `flake8-print` linters
/// via a single selector.
T,
/// Select all rules for a given linter.
Linter(Linter),
/// Select all rules for a given linter with a given prefix.
Prefix {
prefix: RuleCodePrefix,
redirected_from: Option<&'static str>,
@@ -44,10 +36,6 @@ impl FromStr for RuleSelector {
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == "ALL" {
Ok(Self::All)
} else if s == "C" {
Ok(Self::C)
} else if s == "T" {
Ok(Self::T)
} else {
let (s, redirected_from) = match get_redirect(s) {
Some((from, target)) => (target, Some(from)),
@@ -63,7 +51,7 @@ impl FromStr for RuleSelector {
Ok(Self::Prefix {
prefix: RuleCodePrefix::parse(&linter, code)
.map_err(|_| ParseError::Unknown(s.to_string()))?,
.map_err(|_| ParseError::Unknown(code.to_string()))?,
redirected_from,
})
}
@@ -72,7 +60,7 @@ impl FromStr for RuleSelector {
#[derive(Debug, thiserror::Error)]
pub enum ParseError {
#[error("Unknown rule selector: `{0}`")]
#[error("Unknown rule selector `{0}`")]
// TODO(martin): tell the user how to discover rule codes via the CLI once such a command is
// implemented (but that should of course be done only in ruff_cli and not here)
Unknown(String),
@@ -82,8 +70,6 @@ impl RuleSelector {
pub fn prefix_and_code(&self) -> (&'static str, &'static str) {
match self {
RuleSelector::All => ("", "ALL"),
RuleSelector::C => ("", "C"),
RuleSelector::T => ("", "T"),
RuleSelector::Prefix { prefix, .. } => {
(prefix.linter().common_prefix(), prefix.short_code())
}
@@ -152,16 +138,6 @@ impl IntoIterator for &RuleSelector {
fn into_iter(self) -> Self::IntoIter {
match self {
RuleSelector::All => RuleSelectorIter::All(Rule::iter()),
RuleSelector::C => RuleSelectorIter::Chain(
Linter::Flake8Comprehensions
.into_iter()
.chain(Linter::McCabe.into_iter()),
),
RuleSelector::T => RuleSelectorIter::Chain(
Linter::Flake8Debugger
.into_iter()
.chain(Linter::Flake8Print.into_iter()),
),
RuleSelector::Linter(linter) => RuleSelectorIter::Vec(linter.into_iter()),
RuleSelector::Prefix { prefix, .. } => RuleSelectorIter::Vec(prefix.into_iter()),
}
@@ -170,7 +146,6 @@ impl IntoIterator for &RuleSelector {
pub enum RuleSelectorIter {
All(RuleIter),
Chain(std::iter::Chain<std::vec::IntoIter<Rule>, std::vec::IntoIter<Rule>>),
Vec(std::vec::IntoIter<Rule>),
}
@@ -180,14 +155,13 @@ impl Iterator for RuleSelectorIter {
fn next(&mut self) -> Option<Self::Item> {
match self {
RuleSelectorIter::All(iter) => iter.next(),
RuleSelectorIter::Chain(iter) => iter.next(),
RuleSelectorIter::Vec(iter) => iter.next(),
}
}
}
/// A const alternative to the `impl From<RuleCodePrefix> for RuleSelector`
/// to let us keep the fields of [`RuleSelector`] private.
// to let us keep the fields of RuleSelector private.
// Note that Rust doesn't yet support `impl const From<RuleCodePrefix> for
// RuleSelector` (see https://github.com/rust-lang/rust/issues/67792).
// TODO(martin): Remove once RuleSelector is an enum with Linter & Rule variants
@@ -203,27 +177,13 @@ impl JsonSchema for RuleSelector {
"RuleSelector".to_string()
}
fn json_schema(_gen: &mut schemars::gen::SchemaGenerator) -> Schema {
fn json_schema(_gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
Schema::Object(SchemaObject {
instance_type: Some(InstanceType::String.into()),
enum_values: Some(
std::iter::once("ALL".to_string())
.chain(
RuleCodePrefix::iter()
.filter(|p| {
// Once logical lines are active by default, please remove this.
// This is here because generate-all output otherwise depends on
// the feature sets which makes the test running with
// `--all-features` fail
!Rule::from_code(&format!(
"{}{}",
p.linter().common_prefix(),
p.short_code()
))
.unwrap()
.lint_source()
.is_logical_lines()
})
.map(|p| {
let prefix = p.linter().common_prefix();
let code = p.short_code();
@@ -247,8 +207,6 @@ impl RuleSelector {
pub(crate) fn specificity(&self) -> Specificity {
match self {
RuleSelector::All => Specificity::All,
RuleSelector::T => Specificity::LinterGroup,
RuleSelector::C => Specificity::LinterGroup,
RuleSelector::Linter(..) => Specificity::Linter,
RuleSelector::Prefix { prefix, .. } => {
let prefix: &'static str = prefix.short_code();
@@ -268,7 +226,6 @@ impl RuleSelector {
#[derive(EnumIter, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) enum Specificity {
All,
LinterGroup,
Linter,
Code1Char,
Code2Chars,
@@ -277,7 +234,6 @@ pub(crate) enum Specificity {
Code5Chars,
}
#[cfg(feature = "clap")]
mod clap_completion {
use clap::builder::{PossibleValue, TypedValueParser, ValueParserFactory};
use strum::IntoEnumIterator;
@@ -317,7 +273,9 @@ mod clap_completion {
.map_err(|e| clap::Error::raw(clap::error::ErrorKind::InvalidValue, e))
}
fn possible_values(&self) -> Option<Box<dyn Iterator<Item = PossibleValue> + '_>> {
fn possible_values(
&self,
) -> Option<Box<dyn Iterator<Item = clap::builder::PossibleValue> + '_>> {
Some(Box::new(
std::iter::once(PossibleValue::new("ALL").help("all rules")).chain(
Linter::iter()

View File

@@ -1,29 +1,28 @@
use ruff_macros::{define_violation, derive_message_formats};
use rustpython_parser::ast::Location;
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::source_code::Locator;
use ruff_python_ast::types::Range;
use crate::registry::Rule;
use crate::settings::{flags, Settings};
use super::detection::comment_contains_code;
use crate::ast::types::Range;
use crate::fix::Fix;
use crate::registry::{Diagnostic, Rule};
use crate::settings::{flags, Settings};
use crate::source_code::Locator;
use crate::violation::AlwaysAutofixableViolation;
/// ## What it does
/// Checks for commented-out Python code.
///
/// ## Why is this bad?
/// Commented-out code is dead code, and is often included inadvertently.
/// It should be removed.
///
/// ## Example
/// ```python
/// # print('foo')
/// ```
#[violation]
pub struct CommentedOutCode;
define_violation!(
/// ## What it does
/// Checks for commented-out Python code.
///
/// ## Why is this bad?
/// Commented-out code is dead code, and is often included inadvertently.
/// It should be removed.
///
/// ## Example
/// ```python
/// # print('foo')
/// ```
pub struct CommentedOutCode;
);
impl AlwaysAutofixableViolation for CommentedOutCode {
#[derive_message_formats]
fn message(&self) -> String {
@@ -56,7 +55,7 @@ pub fn commented_out_code(
) -> Option<Diagnostic> {
let location = Location::new(start.row(), 0);
let end_location = Location::new(end.row() + 1, 0);
let line = locator.slice(Range::new(location, end_location));
let line = locator.slice(&Range::new(location, end_location));
// Verify that the comment is on its own line, and that it contains code.
if is_standalone_comment(line) && comment_contains_code(line, &settings.task_tags[..]) {

View File

@@ -3,10 +3,7 @@ source: crates/ruff/src/rules/eradicate/mod.rs
expression: diagnostics
---
- kind:
name: CommentedOutCode
body: Found commented-out code
suggestion: Remove commented-out code
fixable: true
CommentedOutCode: ~
location:
row: 1
column: 0
@@ -23,10 +20,7 @@ expression: diagnostics
column: 0
parent: ~
- kind:
name: CommentedOutCode
body: Found commented-out code
suggestion: Remove commented-out code
fixable: true
CommentedOutCode: ~
location:
row: 2
column: 0
@@ -43,10 +37,7 @@ expression: diagnostics
column: 0
parent: ~
- kind:
name: CommentedOutCode
body: Found commented-out code
suggestion: Remove commented-out code
fixable: true
CommentedOutCode: ~
location:
row: 3
column: 0
@@ -63,10 +54,7 @@ expression: diagnostics
column: 0
parent: ~
- kind:
name: CommentedOutCode
body: Found commented-out code
suggestion: Remove commented-out code
fixable: true
CommentedOutCode: ~
location:
row: 5
column: 0
@@ -83,10 +71,7 @@ expression: diagnostics
column: 0
parent: ~
- kind:
name: CommentedOutCode
body: Found commented-out code
suggestion: Remove commented-out code
fixable: true
CommentedOutCode: ~
location:
row: 12
column: 4

View File

@@ -1,16 +1,15 @@
use num_bigint::BigInt;
use ruff_macros::{define_violation, derive_message_formats};
use rustpython_parser::ast::{Cmpop, Constant, Expr, ExprKind, Located};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::types::Range;
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::registry::Rule;
#[violation]
pub struct SysVersionSlice3Referenced;
use crate::registry::{Diagnostic, Rule};
use crate::violation::Violation;
define_violation!(
pub struct SysVersionSlice3Referenced;
);
impl Violation for SysVersionSlice3Referenced {
#[derive_message_formats]
fn message(&self) -> String {
@@ -18,9 +17,9 @@ impl Violation for SysVersionSlice3Referenced {
}
}
#[violation]
pub struct SysVersion2Referenced;
define_violation!(
pub struct SysVersion2Referenced;
);
impl Violation for SysVersion2Referenced {
#[derive_message_formats]
fn message(&self) -> String {
@@ -28,9 +27,9 @@ impl Violation for SysVersion2Referenced {
}
}
#[violation]
pub struct SysVersionCmpStr3;
define_violation!(
pub struct SysVersionCmpStr3;
);
impl Violation for SysVersionCmpStr3 {
#[derive_message_formats]
fn message(&self) -> String {
@@ -38,9 +37,9 @@ impl Violation for SysVersionCmpStr3 {
}
}
#[violation]
pub struct SysVersionInfo0Eq3Referenced;
define_violation!(
pub struct SysVersionInfo0Eq3Referenced;
);
impl Violation for SysVersionInfo0Eq3Referenced {
#[derive_message_formats]
fn message(&self) -> String {
@@ -48,9 +47,9 @@ impl Violation for SysVersionInfo0Eq3Referenced {
}
}
#[violation]
pub struct SixPY3Referenced;
define_violation!(
pub struct SixPY3Referenced;
);
impl Violation for SixPY3Referenced {
#[derive_message_formats]
fn message(&self) -> String {
@@ -58,9 +57,9 @@ impl Violation for SixPY3Referenced {
}
}
#[violation]
pub struct SysVersionInfo1CmpInt;
define_violation!(
pub struct SysVersionInfo1CmpInt;
);
impl Violation for SysVersionInfo1CmpInt {
#[derive_message_formats]
fn message(&self) -> String {
@@ -71,9 +70,9 @@ impl Violation for SysVersionInfo1CmpInt {
}
}
#[violation]
pub struct SysVersionInfoMinorCmpInt;
define_violation!(
pub struct SysVersionInfoMinorCmpInt;
);
impl Violation for SysVersionInfoMinorCmpInt {
#[derive_message_formats]
fn message(&self) -> String {
@@ -84,9 +83,9 @@ impl Violation for SysVersionInfoMinorCmpInt {
}
}
#[violation]
pub struct SysVersion0Referenced;
define_violation!(
pub struct SysVersion0Referenced;
);
impl Violation for SysVersion0Referenced {
#[derive_message_formats]
fn message(&self) -> String {
@@ -94,9 +93,9 @@ impl Violation for SysVersion0Referenced {
}
}
#[violation]
pub struct SysVersionCmpStr10;
define_violation!(
pub struct SysVersionCmpStr10;
);
impl Violation for SysVersionCmpStr10 {
#[derive_message_formats]
fn message(&self) -> String {
@@ -104,9 +103,9 @@ impl Violation for SysVersionCmpStr10 {
}
}
#[violation]
pub struct SysVersionSlice1Referenced;
define_violation!(
pub struct SysVersionSlice1Referenced;
);
impl Violation for SysVersionSlice1Referenced {
#[derive_message_formats]
fn message(&self) -> String {
@@ -144,7 +143,7 @@ pub fn subscript(checker: &mut Checker, value: &Expr, slice: &Expr) {
{
checker.diagnostics.push(Diagnostic::new(
SysVersionSlice1Referenced,
Range::from(value),
Range::from_located(value),
));
} else if *i == BigInt::from(3)
&& checker
@@ -154,7 +153,7 @@ pub fn subscript(checker: &mut Checker, value: &Expr, slice: &Expr) {
{
checker.diagnostics.push(Diagnostic::new(
SysVersionSlice3Referenced,
Range::from(value),
Range::from_located(value),
));
}
}
@@ -167,15 +166,17 @@ pub fn subscript(checker: &mut Checker, value: &Expr, slice: &Expr) {
if *i == BigInt::from(2)
&& checker.settings.rules.enabled(&Rule::SysVersion2Referenced)
{
checker
.diagnostics
.push(Diagnostic::new(SysVersion2Referenced, Range::from(value)));
checker.diagnostics.push(Diagnostic::new(
SysVersion2Referenced,
Range::from_located(value),
));
} else if *i == BigInt::from(0)
&& checker.settings.rules.enabled(&Rule::SysVersion0Referenced)
{
checker
.diagnostics
.push(Diagnostic::new(SysVersion0Referenced, Range::from(value)));
checker.diagnostics.push(Diagnostic::new(
SysVersion0Referenced,
Range::from_located(value),
));
}
}
@@ -214,7 +215,7 @@ pub fn compare(checker: &mut Checker, left: &Expr, ops: &[Cmpop], comparators: &
{
checker.diagnostics.push(Diagnostic::new(
SysVersionInfo0Eq3Referenced,
Range::from(left),
Range::from_located(left),
));
}
}
@@ -232,9 +233,10 @@ pub fn compare(checker: &mut Checker, left: &Expr, ops: &[Cmpop], comparators: &
) = (ops, comparators)
{
if checker.settings.rules.enabled(&Rule::SysVersionInfo1CmpInt) {
checker
.diagnostics
.push(Diagnostic::new(SysVersionInfo1CmpInt, Range::from(left)));
checker.diagnostics.push(Diagnostic::new(
SysVersionInfo1CmpInt,
Range::from_located(left),
));
}
}
}
@@ -263,7 +265,7 @@ pub fn compare(checker: &mut Checker, left: &Expr, ops: &[Cmpop], comparators: &
{
checker.diagnostics.push(Diagnostic::new(
SysVersionInfoMinorCmpInt,
Range::from(left),
Range::from_located(left),
));
}
}
@@ -287,14 +289,16 @@ pub fn compare(checker: &mut Checker, left: &Expr, ops: &[Cmpop], comparators: &
{
if s.len() == 1 {
if checker.settings.rules.enabled(&Rule::SysVersionCmpStr10) {
checker
.diagnostics
.push(Diagnostic::new(SysVersionCmpStr10, Range::from(left)));
checker.diagnostics.push(Diagnostic::new(
SysVersionCmpStr10,
Range::from_located(left),
));
}
} else if checker.settings.rules.enabled(&Rule::SysVersionCmpStr3) {
checker
.diagnostics
.push(Diagnostic::new(SysVersionCmpStr3, Range::from(left)));
checker.diagnostics.push(Diagnostic::new(
SysVersionCmpStr3,
Range::from_located(left),
));
}
}
}
@@ -309,6 +313,6 @@ pub fn name_or_attribute(checker: &mut Checker, expr: &Expr) {
{
checker
.diagnostics
.push(Diagnostic::new(SixPY3Referenced, Range::from(expr)));
.push(Diagnostic::new(SixPY3Referenced, Range::from_located(expr)));
}
}

View File

@@ -1,12 +1,9 @@
---
source: crates/ruff/src/rules/flake8_2020/mod.rs
source: src/rules/flake8_2020/mod.rs
expression: diagnostics
---
- kind:
name: SysVersionSlice3Referenced
body: "`sys.version[:3]` referenced (python3.10), use `sys.version_info`"
suggestion: ~
fixable: false
SysVersionSlice3Referenced: ~
location:
row: 6
column: 6
@@ -16,10 +13,7 @@ expression: diagnostics
fix: ~
parent: ~
- kind:
name: SysVersionSlice3Referenced
body: "`sys.version[:3]` referenced (python3.10), use `sys.version_info`"
suggestion: ~
fixable: false
SysVersionSlice3Referenced: ~
location:
row: 7
column: 6
@@ -29,10 +23,7 @@ expression: diagnostics
fix: ~
parent: ~
- kind:
name: SysVersionSlice3Referenced
body: "`sys.version[:3]` referenced (python3.10), use `sys.version_info`"
suggestion: ~
fixable: false
SysVersionSlice3Referenced: ~
location:
row: 8
column: 6

View File

@@ -1,12 +1,9 @@
---
source: crates/ruff/src/rules/flake8_2020/mod.rs
source: src/rules/flake8_2020/mod.rs
expression: diagnostics
---
- kind:
name: SysVersion2Referenced
body: "`sys.version[2]` referenced (python3.10), use `sys.version_info`"
suggestion: ~
fixable: false
SysVersion2Referenced: ~
location:
row: 4
column: 11
@@ -16,10 +13,7 @@ expression: diagnostics
fix: ~
parent: ~
- kind:
name: SysVersion2Referenced
body: "`sys.version[2]` referenced (python3.10), use `sys.version_info`"
suggestion: ~
fixable: false
SysVersion2Referenced: ~
location:
row: 5
column: 11

View File

@@ -1,12 +1,9 @@
---
source: crates/ruff/src/rules/flake8_2020/mod.rs
source: src/rules/flake8_2020/mod.rs
expression: diagnostics
---
- kind:
name: SysVersionCmpStr3
body: "`sys.version` compared to string (python3.10), use `sys.version_info`"
suggestion: ~
fixable: false
SysVersionCmpStr3: ~
location:
row: 4
column: 0
@@ -16,10 +13,7 @@ expression: diagnostics
fix: ~
parent: ~
- kind:
name: SysVersionCmpStr3
body: "`sys.version` compared to string (python3.10), use `sys.version_info`"
suggestion: ~
fixable: false
SysVersionCmpStr3: ~
location:
row: 5
column: 0
@@ -29,10 +23,7 @@ expression: diagnostics
fix: ~
parent: ~
- kind:
name: SysVersionCmpStr3
body: "`sys.version` compared to string (python3.10), use `sys.version_info`"
suggestion: ~
fixable: false
SysVersionCmpStr3: ~
location:
row: 6
column: 0
@@ -42,10 +33,7 @@ expression: diagnostics
fix: ~
parent: ~
- kind:
name: SysVersionCmpStr3
body: "`sys.version` compared to string (python3.10), use `sys.version_info`"
suggestion: ~
fixable: false
SysVersionCmpStr3: ~
location:
row: 7
column: 0
@@ -55,10 +43,7 @@ expression: diagnostics
fix: ~
parent: ~
- kind:
name: SysVersionCmpStr3
body: "`sys.version` compared to string (python3.10), use `sys.version_info`"
suggestion: ~
fixable: false
SysVersionCmpStr3: ~
location:
row: 8
column: 0

View File

@@ -1,12 +1,9 @@
---
source: crates/ruff/src/rules/flake8_2020/mod.rs
source: src/rules/flake8_2020/mod.rs
expression: diagnostics
---
- kind:
name: SysVersionInfo0Eq3Referenced
body: "`sys.version_info[0] == 3` referenced (python4), use `>=`"
suggestion: ~
fixable: false
SysVersionInfo0Eq3Referenced: ~
location:
row: 7
column: 6
@@ -16,10 +13,7 @@ expression: diagnostics
fix: ~
parent: ~
- kind:
name: SysVersionInfo0Eq3Referenced
body: "`sys.version_info[0] == 3` referenced (python4), use `>=`"
suggestion: ~
fixable: false
SysVersionInfo0Eq3Referenced: ~
location:
row: 8
column: 6
@@ -29,10 +23,7 @@ expression: diagnostics
fix: ~
parent: ~
- kind:
name: SysVersionInfo0Eq3Referenced
body: "`sys.version_info[0] == 3` referenced (python4), use `>=`"
suggestion: ~
fixable: false
SysVersionInfo0Eq3Referenced: ~
location:
row: 9
column: 6
@@ -42,10 +33,7 @@ expression: diagnostics
fix: ~
parent: ~
- kind:
name: SysVersionInfo0Eq3Referenced
body: "`sys.version_info[0] == 3` referenced (python4), use `>=`"
suggestion: ~
fixable: false
SysVersionInfo0Eq3Referenced: ~
location:
row: 10
column: 6

View File

@@ -1,12 +1,9 @@
---
source: crates/ruff/src/rules/flake8_2020/mod.rs
source: src/rules/flake8_2020/mod.rs
expression: diagnostics
---
- kind:
name: SixPY3Referenced
body: "`six.PY3` referenced (python4), use `not six.PY2`"
suggestion: ~
fixable: false
SixPY3Referenced: ~
location:
row: 4
column: 3
@@ -16,10 +13,7 @@ expression: diagnostics
fix: ~
parent: ~
- kind:
name: SixPY3Referenced
body: "`six.PY3` referenced (python4), use `not six.PY2`"
suggestion: ~
fixable: false
SixPY3Referenced: ~
location:
row: 6
column: 3

View File

@@ -1,12 +1,9 @@
---
source: crates/ruff/src/rules/flake8_2020/mod.rs
source: src/rules/flake8_2020/mod.rs
expression: diagnostics
---
- kind:
name: SysVersionInfo1CmpInt
body: "`sys.version_info[1]` compared to integer (python4), compare `sys.version_info` to tuple"
suggestion: ~
fixable: false
SysVersionInfo1CmpInt: ~
location:
row: 4
column: 0
@@ -16,10 +13,7 @@ expression: diagnostics
fix: ~
parent: ~
- kind:
name: SysVersionInfo1CmpInt
body: "`sys.version_info[1]` compared to integer (python4), compare `sys.version_info` to tuple"
suggestion: ~
fixable: false
SysVersionInfo1CmpInt: ~
location:
row: 5
column: 0

View File

@@ -1,12 +1,9 @@
---
source: crates/ruff/src/rules/flake8_2020/mod.rs
source: src/rules/flake8_2020/mod.rs
expression: diagnostics
---
- kind:
name: SysVersionInfoMinorCmpInt
body: "`sys.version_info.minor` compared to integer (python4), compare `sys.version_info` to tuple"
suggestion: ~
fixable: false
SysVersionInfoMinorCmpInt: ~
location:
row: 4
column: 0
@@ -16,10 +13,7 @@ expression: diagnostics
fix: ~
parent: ~
- kind:
name: SysVersionInfoMinorCmpInt
body: "`sys.version_info.minor` compared to integer (python4), compare `sys.version_info` to tuple"
suggestion: ~
fixable: false
SysVersionInfoMinorCmpInt: ~
location:
row: 5
column: 0

View File

@@ -1,12 +1,9 @@
---
source: crates/ruff/src/rules/flake8_2020/mod.rs
source: src/rules/flake8_2020/mod.rs
expression: diagnostics
---
- kind:
name: SysVersion0Referenced
body: "`sys.version[0]` referenced (python10), use `sys.version_info`"
suggestion: ~
fixable: false
SysVersion0Referenced: ~
location:
row: 4
column: 11
@@ -16,10 +13,7 @@ expression: diagnostics
fix: ~
parent: ~
- kind:
name: SysVersion0Referenced
body: "`sys.version[0]` referenced (python10), use `sys.version_info`"
suggestion: ~
fixable: false
SysVersion0Referenced: ~
location:
row: 5
column: 11

View File

@@ -1,12 +1,9 @@
---
source: crates/ruff/src/rules/flake8_2020/mod.rs
source: src/rules/flake8_2020/mod.rs
expression: diagnostics
---
- kind:
name: SysVersionCmpStr10
body: "`sys.version` compared to string (python10), use `sys.version_info`"
suggestion: ~
fixable: false
SysVersionCmpStr10: ~
location:
row: 4
column: 0
@@ -16,10 +13,7 @@ expression: diagnostics
fix: ~
parent: ~
- kind:
name: SysVersionCmpStr10
body: "`sys.version` compared to string (python10), use `sys.version_info`"
suggestion: ~
fixable: false
SysVersionCmpStr10: ~
location:
row: 5
column: 0
@@ -29,10 +23,7 @@ expression: diagnostics
fix: ~
parent: ~
- kind:
name: SysVersionCmpStr10
body: "`sys.version` compared to string (python10), use `sys.version_info`"
suggestion: ~
fixable: false
SysVersionCmpStr10: ~
location:
row: 6
column: 0
@@ -42,10 +33,7 @@ expression: diagnostics
fix: ~
parent: ~
- kind:
name: SysVersionCmpStr10
body: "`sys.version` compared to string (python10), use `sys.version_info`"
suggestion: ~
fixable: false
SysVersionCmpStr10: ~
location:
row: 7
column: 0
@@ -55,10 +43,7 @@ expression: diagnostics
fix: ~
parent: ~
- kind:
name: SysVersionCmpStr10
body: "`sys.version` compared to string (python10), use `sys.version_info`"
suggestion: ~
fixable: false
SysVersionCmpStr10: ~
location:
row: 8
column: 0

View File

@@ -1,12 +1,9 @@
---
source: crates/ruff/src/rules/flake8_2020/mod.rs
source: src/rules/flake8_2020/mod.rs
expression: diagnostics
---
- kind:
name: SysVersionSlice1Referenced
body: "`sys.version[:1]` referenced (python10), use `sys.version_info`"
suggestion: ~
fixable: false
SysVersionSlice1Referenced: ~
location:
row: 4
column: 6
@@ -16,10 +13,7 @@ expression: diagnostics
fix: ~
parent: ~
- kind:
name: SysVersionSlice1Referenced
body: "`sys.version[:1]` referenced (python10), use `sys.version_info`"
suggestion: ~
fixable: false
SysVersionSlice1Referenced: ~
location:
row: 5
column: 6

Some files were not shown because too many files have changed in this diff Show More