Compare commits
138 Commits
zb/fmt-ski
...
dhruv/ecos
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b9d133f718 | ||
|
|
18452cf477 | ||
|
|
cb99815c3e | ||
|
|
45f603000d | ||
|
|
1a65e544c5 | ||
|
|
4d2ee5bf98 | ||
|
|
8314c8bb05 | ||
|
|
cb201bc4a5 | ||
|
|
6c0068eeec | ||
|
|
c306f85691 | ||
|
|
b972455ac7 | ||
|
|
5559827a78 | ||
|
|
cb8eea64a8 | ||
|
|
8e9bf84047 | ||
|
|
2993c342d2 | ||
|
|
108260298f | ||
|
|
a53d59f6bd | ||
|
|
1026ece946 | ||
|
|
f452bf8cad | ||
|
|
07380e0657 | ||
|
|
3aa6a30395 | ||
|
|
efb76ffa64 | ||
|
|
4e461cbf03 | ||
|
|
93417b5644 | ||
|
|
b8dd499b2a | ||
|
|
cd2bf26845 | ||
|
|
6e36dcfefe | ||
|
|
febc69ab48 | ||
|
|
6c2613b44e | ||
|
|
cb8a2f5615 | ||
|
|
b7b137abc8 | ||
|
|
f69a35a021 | ||
|
|
829a808526 | ||
|
|
85fc57e7f9 | ||
|
|
20e33bf514 | ||
|
|
b7dd2b5941 | ||
|
|
e043bd46b5 | ||
|
|
d0d88d9375 | ||
|
|
a224f19903 | ||
|
|
2414298289 | ||
|
|
6bbabceead | ||
|
|
04ec11a73d | ||
|
|
b021ede481 | ||
|
|
96ae9fe685 | ||
|
|
cdac90ef68 | ||
|
|
fcc08894cf | ||
|
|
ebc7ac31cb | ||
|
|
981a0703ed | ||
|
|
946b308197 | ||
|
|
d22ce5372d | ||
|
|
acab5f3cf2 | ||
|
|
06c9f625b6 | ||
|
|
bbb0a0c360 | ||
|
|
9361e22fe9 | ||
|
|
f484df5470 | ||
|
|
af88ffc57e | ||
|
|
b918647927 | ||
|
|
ef7778d794 | ||
|
|
bd443ebe91 | ||
|
|
ee6548d7dd | ||
|
|
b4a050c21d | ||
|
|
958702ded0 | ||
|
|
268d95e911 | ||
|
|
3def18fc21 | ||
|
|
c48ba690eb | ||
|
|
fd49fb935f | ||
|
|
fe54ef08aa | ||
|
|
b7ffd73edd | ||
|
|
8d9912a83a | ||
|
|
93258e8d5b | ||
|
|
b90027d037 | ||
|
|
060a25df09 | ||
|
|
f5d4676c13 | ||
|
|
df69dc9f8d | ||
|
|
cb6c37abd9 | ||
|
|
54de990621 | ||
|
|
b91b09b961 | ||
|
|
0bda1913d1 | ||
|
|
7e390d3772 | ||
|
|
0bf0aa28ac | ||
|
|
8088c5367a | ||
|
|
6fe8f8a272 | ||
|
|
bfae1f1412 | ||
|
|
b358cbf398 | ||
|
|
17c8817695 | ||
|
|
1dda669f9a | ||
|
|
3fbabfe126 | ||
|
|
20ab14e354 | ||
|
|
22d8a989d4 | ||
|
|
35082b28cd | ||
|
|
5aaf99b856 | ||
|
|
58bf6f5762 | ||
|
|
277cd80175 | ||
|
|
20a40771a5 | ||
|
|
4af3f43e5e | ||
|
|
0b1a36f8c8 | ||
|
|
64c2535e28 | ||
|
|
5510a6131e | ||
|
|
e5db72459e | ||
|
|
d66063bb33 | ||
|
|
506be68782 | ||
|
|
cb1d3df085 | ||
|
|
69dfe0a207 | ||
|
|
46a174a22e | ||
|
|
912c39ce2a | ||
|
|
b2638c62a5 | ||
|
|
eaa310429f | ||
|
|
019d9aebe9 | ||
|
|
f06c5dc896 | ||
|
|
c1dc4a60be | ||
|
|
70febb1862 | ||
|
|
4212b41796 | ||
|
|
bbad4b4c93 | ||
|
|
3ee1ec70cc | ||
|
|
ee5d95f751 | ||
|
|
d674e7946d | ||
|
|
20782ab02c | ||
|
|
073eddb1d9 | ||
|
|
e8da95d09c | ||
|
|
c324cb6202 | ||
|
|
774c77adae | ||
|
|
fd70cd789f | ||
|
|
08f3110f1e | ||
|
|
2314b9aaca | ||
|
|
cddc696896 | ||
|
|
6435e4e4aa | ||
|
|
ec7456bac0 | ||
|
|
b28556d739 | ||
|
|
4957d94beb | ||
|
|
5d554edace | ||
|
|
412688826c | ||
|
|
47d80f29a7 | ||
|
|
9dee1883ce | ||
|
|
f585e3e2dc | ||
|
|
578ddf1bb1 | ||
|
|
ed14fd9163 | ||
|
|
60eb11fa50 | ||
|
|
33caa2ab1c |
49
.github/workflows/ci.yaml
vendored
49
.github/workflows/ci.yaml
vendored
@@ -23,8 +23,13 @@ jobs:
|
||||
name: "Determine changes"
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
# Flag that is raised when any code that affects linter is changed
|
||||
linter: ${{ steps.changed.outputs.linter_any_changed }}
|
||||
# Flag that is raised when any code that affects formatter is changed
|
||||
formatter: ${{ steps.changed.outputs.formatter_any_changed }}
|
||||
# Flag that is raised when any code is changed
|
||||
# This is superset of the linter and formatter
|
||||
code: ${{ steps.changed.outputs.code_any_changed }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -43,8 +48,8 @@ jobs:
|
||||
- "!crates/ruff_dev/**"
|
||||
- "!crates/ruff_shrinking/**"
|
||||
- scripts/*
|
||||
- .github/workflows/ci.yaml
|
||||
- python/**
|
||||
- .github/workflows/ci.yaml
|
||||
|
||||
formatter:
|
||||
- Cargo.toml
|
||||
@@ -62,6 +67,12 @@ jobs:
|
||||
- python/**
|
||||
- .github/workflows/ci.yaml
|
||||
|
||||
code:
|
||||
- "**/*"
|
||||
- "!**/*.md"
|
||||
- "!docs/**"
|
||||
- "!assets/**"
|
||||
|
||||
cargo-fmt:
|
||||
name: "cargo fmt"
|
||||
runs-on: ubuntu-latest
|
||||
@@ -74,6 +85,8 @@ jobs:
|
||||
cargo-clippy:
|
||||
name: "cargo clippy"
|
||||
runs-on: ubuntu-latest
|
||||
needs: determine_changes
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: "Install Rust toolchain"
|
||||
@@ -88,6 +101,8 @@ jobs:
|
||||
|
||||
cargo-test-linux:
|
||||
runs-on: ubuntu-latest
|
||||
needs: determine_changes
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
name: "cargo test (linux)"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -112,6 +127,8 @@ jobs:
|
||||
|
||||
cargo-test-windows:
|
||||
runs-on: windows-latest
|
||||
needs: determine_changes
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
name: "cargo test (windows)"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -129,6 +146,8 @@ jobs:
|
||||
|
||||
cargo-test-wasm:
|
||||
runs-on: ubuntu-latest
|
||||
needs: determine_changes
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
name: "cargo test (wasm)"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -148,6 +167,8 @@ jobs:
|
||||
|
||||
cargo-fuzz:
|
||||
runs-on: ubuntu-latest
|
||||
needs: determine_changes
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
name: "cargo fuzz"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -165,6 +186,8 @@ jobs:
|
||||
scripts:
|
||||
name: "test scripts"
|
||||
runs-on: ubuntu-latest
|
||||
needs: determine_changes
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: "Install Rust toolchain"
|
||||
@@ -188,12 +211,11 @@ jobs:
|
||||
# Only runs on pull requests, since that is the only we way we can find the base version for comparison.
|
||||
# Ecosystem check needs linter and/or formatter changes.
|
||||
if: github.event_name == 'pull_request' && ${{
|
||||
needs.determine_changes.outputs.linter == 'true' ||
|
||||
needs.determine_changes.outputs.formatter == 'true'
|
||||
needs.determine_changes.outputs.code == 'true'
|
||||
}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
@@ -298,6 +320,8 @@ jobs:
|
||||
cargo-udeps:
|
||||
name: "cargo udeps"
|
||||
runs-on: ubuntu-latest
|
||||
needs: determine_changes
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: "Install nightly Rust toolchain"
|
||||
@@ -314,7 +338,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
@@ -338,7 +362,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: "Install Rust toolchain"
|
||||
@@ -368,7 +392,7 @@ jobs:
|
||||
MKDOCS_INSIDERS_SSH_KEY_EXISTS: ${{ secrets.MKDOCS_INSIDERS_SSH_KEY != '' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
- name: "Add SSH key"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
uses: webfactory/ssh-agent@v0.8.0
|
||||
@@ -417,7 +441,10 @@ jobs:
|
||||
check-ruff-lsp:
|
||||
name: "test ruff-lsp"
|
||||
runs-on: ubuntu-latest
|
||||
needs: cargo-test-linux
|
||||
needs:
|
||||
- cargo-test-linux
|
||||
- determine_changes
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
steps:
|
||||
- uses: extractions/setup-just@v1
|
||||
env:
|
||||
@@ -428,7 +455,7 @@ jobs:
|
||||
with:
|
||||
repository: "astral-sh/ruff-lsp"
|
||||
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
@@ -455,6 +482,8 @@ jobs:
|
||||
|
||||
benchmarks:
|
||||
runs-on: ubuntu-latest
|
||||
needs: determine_changes
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
steps:
|
||||
- name: "Checkout Branch"
|
||||
uses: actions/checkout@v4
|
||||
@@ -473,7 +502,7 @@ jobs:
|
||||
run: cargo codspeed build --features codspeed -p ruff_benchmark
|
||||
|
||||
- name: "Run benchmarks"
|
||||
uses: CodSpeedHQ/action@v1
|
||||
uses: CodSpeedHQ/action@v2
|
||||
with:
|
||||
run: cargo codspeed run
|
||||
token: ${{ secrets.CODSPEED_TOKEN }}
|
||||
|
||||
2
.github/workflows/docs.yaml
vendored
2
.github/workflows/docs.yaml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.ref }}
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
- name: "Add SSH key"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
uses: webfactory/ssh-agent@v0.8.0
|
||||
|
||||
16
.github/workflows/flake8-to-ruff.yaml
vendored
16
.github/workflows/flake8-to-ruff.yaml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
@@ -69,7 +69,7 @@ jobs:
|
||||
target: [x64, x86]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: ${{ matrix.target }}
|
||||
@@ -97,7 +97,7 @@ jobs:
|
||||
target: [x86_64, i686]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
@@ -124,7 +124,7 @@ jobs:
|
||||
target: [aarch64, armv7, s390x, ppc64le, ppc64]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: "Build wheels"
|
||||
@@ -161,7 +161,7 @@ jobs:
|
||||
- i686-unknown-linux-musl
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
@@ -197,7 +197,7 @@ jobs:
|
||||
arch: armv7
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: "Build wheels"
|
||||
@@ -237,7 +237,7 @@ jobs:
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: wheels
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
- name: "Publish to PyPi"
|
||||
env:
|
||||
TWINE_USERNAME: __token__
|
||||
|
||||
45
.github/workflows/release.yaml
vendored
45
.github/workflows/release.yaml
vendored
@@ -36,7 +36,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.sha }}
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: "Prep README.md"
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.sha }}
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
@@ -86,7 +86,7 @@ jobs:
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-x86_64-apple-darwin.tar.gz
|
||||
ARCHIVE_FILE=ruff-${{ inputs.tag }}-x86_64-apple-darwin.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/x86_64-apple-darwin/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
@@ -103,7 +103,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.sha }}
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
@@ -125,7 +125,7 @@ jobs:
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-aarch64-apple-darwin.tar.gz
|
||||
ARCHIVE_FILE=ruff-${{ inputs.tag }}-aarch64-apple-darwin.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/aarch64-apple-darwin/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
@@ -151,7 +151,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.sha }}
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: ${{ matrix.platform.arch }}
|
||||
@@ -177,7 +177,7 @@ jobs:
|
||||
- name: "Archive binary"
|
||||
shell: bash
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-${{ matrix.platform.target }}.zip
|
||||
ARCHIVE_FILE=ruff-${{ inputs.tag }}-${{ matrix.platform.target }}.zip
|
||||
7z a $ARCHIVE_FILE ./target/${{ matrix.platform.target }}/release/ruff.exe
|
||||
sha256sum $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
@@ -199,7 +199,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.sha }}
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
@@ -224,7 +224,7 @@ jobs:
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-${{ matrix.target }}.tar.gz
|
||||
ARCHIVE_FILE=ruff-${{ inputs.tag }}-${{ matrix.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
@@ -258,7 +258,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.sha }}
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: "Prep README.md"
|
||||
@@ -291,7 +291,7 @@ jobs:
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-${{ matrix.platform.target }}.tar.gz
|
||||
ARCHIVE_FILE=ruff-${{ inputs.tag }}-${{ matrix.platform.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.platform.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
@@ -313,7 +313,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.sha }}
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
architecture: x64
|
||||
@@ -332,10 +332,10 @@ jobs:
|
||||
image: alpine:latest
|
||||
options: -v ${{ github.workspace }}:/io -w /io
|
||||
run: |
|
||||
apk add py3-pip
|
||||
pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links /io/dist/ --force-reinstall
|
||||
ruff --help
|
||||
python -m ruff --help
|
||||
apk add python3
|
||||
python -m venv .venv
|
||||
.venv/bin/pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
|
||||
.venv/bin/ruff check --help
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
@@ -343,7 +343,7 @@ jobs:
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-${{ matrix.target }}.tar.gz
|
||||
ARCHIVE_FILE=ruff-${{ inputs.tag }}-${{ matrix.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
@@ -369,7 +369,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.sha }}
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: "Prep README.md"
|
||||
@@ -388,10 +388,11 @@ jobs:
|
||||
distro: alpine_latest
|
||||
githubToken: ${{ github.token }}
|
||||
install: |
|
||||
apk add py3-pip
|
||||
apk add python3
|
||||
run: |
|
||||
pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
|
||||
ruff check --help
|
||||
python -m venv .venv
|
||||
.venv/bin/pip3 install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
|
||||
.venv/bin/ruff check --help
|
||||
- name: "Upload wheels"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
@@ -399,7 +400,7 @@ jobs:
|
||||
path: dist
|
||||
- name: "Archive binary"
|
||||
run: |
|
||||
ARCHIVE_FILE=ruff-${{ matrix.platform.target }}.tar.gz
|
||||
ARCHIVE_FILE=ruff-${{ inputs.tag }}-${{ matrix.platform.target }}.tar.gz
|
||||
tar czvf $ARCHIVE_FILE -C target/${{ matrix.platform.target }}/release ruff
|
||||
shasum -a 256 $ARCHIVE_FILE > $ARCHIVE_FILE.sha256
|
||||
- name: "Upload binary"
|
||||
|
||||
94
CHANGELOG.md
94
CHANGELOG.md
@@ -1,5 +1,99 @@
|
||||
# Changelog
|
||||
|
||||
## 0.1.7
|
||||
|
||||
### Preview features
|
||||
|
||||
- Implement multiline dictionary and list hugging for preview style ([#8293](https://github.com/astral-sh/ruff/pull/8293))
|
||||
- Implement the `fix_power_op_line_length` preview style ([#8947](https://github.com/astral-sh/ruff/pull/8947))
|
||||
- Use Python version to determine typing rewrite safety ([#8919](https://github.com/astral-sh/ruff/pull/8919))
|
||||
- \[`flake8-annotations`\] Enable auto-return-type involving `Optional` and `Union` annotations ([#8885](https://github.com/astral-sh/ruff/pull/8885))
|
||||
- \[`flake8-bandit`\] Implement `django-raw-sql` (`S611`) ([#8651](https://github.com/astral-sh/ruff/pull/8651))
|
||||
- \[`flake8-bandit`\] Implement `tarfile-unsafe-members` (`S202`) ([#8829](https://github.com/astral-sh/ruff/pull/8829))
|
||||
- \[`flake8-pyi`\] Implement fix for `unnecessary-literal-union` (`PYI030`) ([#7934](https://github.com/astral-sh/ruff/pull/7934))
|
||||
- \[`flake8-simplify`\] Extend `dict-get-with-none-default` (`SIM910`) to non-literals ([#8762](https://github.com/astral-sh/ruff/pull/8762))
|
||||
- \[`pylint`\] - add `unnecessary-list-index-lookup` (`PLR1736`) + autofix ([#7999](https://github.com/astral-sh/ruff/pull/7999))
|
||||
- \[`pylint`\] - implement R0202 and R0203 with autofixes ([#8335](https://github.com/astral-sh/ruff/pull/8335))
|
||||
- \[`pylint`\] Implement `repeated-keyword` (`PLe1132`) ([#8706](https://github.com/astral-sh/ruff/pull/8706))
|
||||
- \[`pylint`\] Implement `too-many-positional` (`PLR0917`) ([#8995](https://github.com/astral-sh/ruff/pull/8995))
|
||||
- \[`pylint`\] Implement `unnecessary-dict-index-lookup` (`PLR1733`) ([#8036](https://github.com/astral-sh/ruff/pull/8036))
|
||||
- \[`refurb`\] Implement `redundant-log-base` (`FURB163`) ([#8842](https://github.com/astral-sh/ruff/pull/8842))
|
||||
|
||||
### Rule changes
|
||||
|
||||
- \[`flake8-boolean-trap`\] Allow booleans in `@override` methods ([#8882](https://github.com/astral-sh/ruff/pull/8882))
|
||||
- \[`flake8-bugbear`\] Avoid `B015`,`B018` for last expression in a cell ([#8815](https://github.com/astral-sh/ruff/pull/8815))
|
||||
- \[`flake8-pie`\] Allow ellipses for enum values in stub files ([#8825](https://github.com/astral-sh/ruff/pull/8825))
|
||||
- \[`flake8-pyi`\] Check PEP 695 type aliases for `snake-case-type-alias` and `t-suffixed-type-alias` ([#8966](https://github.com/astral-sh/ruff/pull/8966))
|
||||
- \[`flake8-pyi`\] Check for kwarg and vararg `NoReturn` type annotations ([#8948](https://github.com/astral-sh/ruff/pull/8948))
|
||||
- \[`flake8-simplify`\] Omit select context managers from `SIM117` ([#8801](https://github.com/astral-sh/ruff/pull/8801))
|
||||
- \[`pep8-naming`\] Allow Django model loads in `non-lowercase-variable-in-function` (`N806`) ([#8917](https://github.com/astral-sh/ruff/pull/8917))
|
||||
- \[`pycodestyle`\] Avoid `E703` for last expression in a cell ([#8821](https://github.com/astral-sh/ruff/pull/8821))
|
||||
- \[`pycodestyle`\] Update `E402` to work at cell level for notebooks ([#8872](https://github.com/astral-sh/ruff/pull/8872))
|
||||
- \[`pydocstyle`\] Avoid `D100` for Jupyter Notebooks ([#8816](https://github.com/astral-sh/ruff/pull/8816))
|
||||
- \[`pylint`\] Implement fix for `unspecified-encoding` (`PLW1514`) ([#8928](https://github.com/astral-sh/ruff/pull/8928))
|
||||
|
||||
### Formatter
|
||||
|
||||
- Avoid unstable formatting in ellipsis-only body with trailing comment ([#8984](https://github.com/astral-sh/ruff/pull/8984))
|
||||
- Inline trailing comments for type alias similar to assignments ([#8941](https://github.com/astral-sh/ruff/pull/8941))
|
||||
- Insert trailing comma when function breaks with single argument ([#8921](https://github.com/astral-sh/ruff/pull/8921))
|
||||
|
||||
### CLI
|
||||
|
||||
- Update `ruff check` and `ruff format` to default to the current directory ([#8791](https://github.com/astral-sh/ruff/pull/8791))
|
||||
- Stop at the first resolved parent configuration ([#8864](https://github.com/astral-sh/ruff/pull/8864))
|
||||
|
||||
### Configuration
|
||||
|
||||
- \[`pylint`\] Default `max-positional-args` to `max-args` ([#8998](https://github.com/astral-sh/ruff/pull/8998))
|
||||
- \[`pylint`\] Add `allow-dunder-method-names` setting for `bad-dunder-method-name` (`PLW3201`) ([#8812](https://github.com/astral-sh/ruff/pull/8812))
|
||||
- \[`isort`\] Add support for `from-first` setting ([#8663](https://github.com/astral-sh/ruff/pull/8663))
|
||||
- \[`isort`\] Add support for `length-sort` settings ([#8841](https://github.com/astral-sh/ruff/pull/8841))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Add support for `@functools.singledispatch` ([#8934](https://github.com/astral-sh/ruff/pull/8934))
|
||||
- Avoid off-by-one error in stripping noqa following multi-byte char ([#8979](https://github.com/astral-sh/ruff/pull/8979))
|
||||
- Avoid off-by-one error in with-item named expressions ([#8915](https://github.com/astral-sh/ruff/pull/8915))
|
||||
- Avoid syntax error via invalid ur string prefix ([#8971](https://github.com/astral-sh/ruff/pull/8971))
|
||||
- Avoid underflow in `get_model` matching ([#8965](https://github.com/astral-sh/ruff/pull/8965))
|
||||
- Avoid unnecessary index diagnostics when value is modified ([#8970](https://github.com/astral-sh/ruff/pull/8970))
|
||||
- Convert over-indentation rule to use number of characters ([#8983](https://github.com/astral-sh/ruff/pull/8983))
|
||||
- Detect implicit returns in auto-return-types ([#8952](https://github.com/astral-sh/ruff/pull/8952))
|
||||
- Fix start >= end error in over-indentation ([#8982](https://github.com/astral-sh/ruff/pull/8982))
|
||||
- Ignore `@overload` and `@override` methods for too-many-arguments checks ([#8954](https://github.com/astral-sh/ruff/pull/8954))
|
||||
- Lexer start of line is false only for `Mode::Expression` ([#8880](https://github.com/astral-sh/ruff/pull/8880))
|
||||
- Mark `pydantic_settings.BaseSettings` as having default copy semantics ([#8793](https://github.com/astral-sh/ruff/pull/8793))
|
||||
- Respect dictionary unpacking in `NamedTuple` assignments ([#8810](https://github.com/astral-sh/ruff/pull/8810))
|
||||
- Respect local subclasses in `flake8-type-checking` ([#8768](https://github.com/astral-sh/ruff/pull/8768))
|
||||
- Support type alias statements in simple statement positions ([#8916](https://github.com/astral-sh/ruff/pull/8916))
|
||||
- \[`flake8-annotations`\] Avoid filtering out un-representable types in return annotation ([#8881](https://github.com/astral-sh/ruff/pull/8881))
|
||||
- \[`flake8-pie`\] Retain extra ellipses in protocols and abstract methods ([#8769](https://github.com/astral-sh/ruff/pull/8769))
|
||||
- \[`flake8-pyi`\] Respect local enum subclasses in `simple-defaults` (`PYI052`) ([#8767](https://github.com/astral-sh/ruff/pull/8767))
|
||||
- \[`flake8-trio`\] Use correct range for `TRIO115` fix ([#8933](https://github.com/astral-sh/ruff/pull/8933))
|
||||
- \[`flake8-trio`\] Use full arguments range for zero-sleep-call ([#8936](https://github.com/astral-sh/ruff/pull/8936))
|
||||
- \[`isort`\] fix: mark `__main__` as first-party import ([#8805](https://github.com/astral-sh/ruff/pull/8805))
|
||||
- \[`pep8-naming`\] Avoid `N806` errors for type alias statements ([#8785](https://github.com/astral-sh/ruff/pull/8785))
|
||||
- \[`perflint`\] Avoid `PERF101` if there's an append in loop body ([#8809](https://github.com/astral-sh/ruff/pull/8809))
|
||||
- \[`pycodestyle`\] Allow space-before-colon after end-of-slice ([#8838](https://github.com/astral-sh/ruff/pull/8838))
|
||||
- \[`pydocstyle`\] Avoid non-character breaks in `over-indentation` (`D208`) ([#8866](https://github.com/astral-sh/ruff/pull/8866))
|
||||
- \[`pydocstyle`\] Ignore underlines when determining docstring logical lines ([#8929](https://github.com/astral-sh/ruff/pull/8929))
|
||||
- \[`pylint`\] Extend `self-assigning-variable` to multi-target assignments ([#8839](https://github.com/astral-sh/ruff/pull/8839))
|
||||
- \[`tryceratops`\] Avoid repeated triggers in nested `tryceratops` diagnostics ([#8772](https://github.com/astral-sh/ruff/pull/8772))
|
||||
|
||||
### Documentation
|
||||
|
||||
- Add advice for fixing RUF008 when mutability is not desired ([#8853](https://github.com/astral-sh/ruff/pull/8853))
|
||||
- Added the command to run ruff using pkgx to the installation.md ([#8955](https://github.com/astral-sh/ruff/pull/8955))
|
||||
- Document fix safety for flake8-comprehensions and some pyupgrade rules ([#8918](https://github.com/astral-sh/ruff/pull/8918))
|
||||
- Fix doc formatting for zero-sleep-call ([#8937](https://github.com/astral-sh/ruff/pull/8937))
|
||||
- Remove duplicate imports from os-stat documentation ([#8930](https://github.com/astral-sh/ruff/pull/8930))
|
||||
- Replace generated reference to MkDocs ([#8806](https://github.com/astral-sh/ruff/pull/8806))
|
||||
- Update Arch Linux package URL in installation.md ([#8802](https://github.com/astral-sh/ruff/pull/8802))
|
||||
- \[`flake8-pyi`\] Fix error in `t-suffixed-type-alias` (`PYI043`) example ([#8963](https://github.com/astral-sh/ruff/pull/8963))
|
||||
- \[`flake8-pyi`\] Improve motivation for `custom-type-var-return-type` (`PYI019`) ([#8766](https://github.com/astral-sh/ruff/pull/8766))
|
||||
|
||||
## 0.1.6
|
||||
|
||||
### Preview features
|
||||
|
||||
218
Cargo.lock
generated
218
Cargo.lock
generated
@@ -16,14 +16,15 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.3"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
|
||||
checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -381,7 +382,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -606,7 +607,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -617,7 +618,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -790,14 +791,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.22"
|
||||
version = "0.2.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0"
|
||||
checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall 0.3.5",
|
||||
"windows-sys 0.48.0",
|
||||
"redox_syscall 0.4.1",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -808,7 +809,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -848,18 +849,18 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.0"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
|
||||
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fs-err"
|
||||
version = "2.10.0"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb5fd9bcbe8b1087cbd395b51498c01bc997cef73e778a80b77a811af5e2d29f"
|
||||
checksum = "88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
@@ -987,9 +988,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
|
||||
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
@@ -1122,15 +1123,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "is-macro"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4467ed1321b310c2625c5aa6c1b1ffc5de4d9e42668cf697a08fb033ee8265e"
|
||||
checksum = "bc74b7abae208af9314a406bd7dcc65091230b6e749c09e07a645885fecf34f9"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"pmutil 0.6.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1170,9 +1171,9 @@ checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.65"
|
||||
version = "0.3.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8"
|
||||
checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
@@ -1622,9 +1623,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.0"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
|
||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
@@ -1708,7 +1709,7 @@ checksum = "52a40bc70c2c58040d2d8b167ba9a5ff59fc9dab7ad44771cfde3dcfde7a09c6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2062,7 +2063,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_cli"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.2",
|
||||
"anyhow",
|
||||
@@ -2198,7 +2199,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_linter"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"annotate-snippets 0.9.2",
|
||||
@@ -2258,6 +2259,7 @@ dependencies = [
|
||||
"typed-arena",
|
||||
"unicode-width",
|
||||
"unicode_names2",
|
||||
"url",
|
||||
"wsl",
|
||||
]
|
||||
|
||||
@@ -2269,7 +2271,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"ruff_python_trivia",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2450,7 +2452,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_shrinking"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -2618,9 +2620,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "schemars"
|
||||
version = "0.8.15"
|
||||
version = "0.8.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f7b0ce13155372a76ee2e1c5ffba1fe61ede73fbea5630d61eee6fac4929c0c"
|
||||
checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29"
|
||||
dependencies = [
|
||||
"dyn-clone",
|
||||
"schemars_derive",
|
||||
@@ -2630,9 +2632,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "schemars_derive"
|
||||
version = "0.8.15"
|
||||
version = "0.8.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e85e2a16b12bdb763244c69ab79363d71db2b4b918a2def53f80b02e0574b13c"
|
||||
checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2676,18 +2678,18 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.190"
|
||||
version = "1.0.193"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7"
|
||||
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde-wasm-bindgen"
|
||||
version = "0.6.1"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17ba92964781421b6cef36bf0d7da26d201e96d84e1b10e7ae6ed416e516906d"
|
||||
checksum = "b9b713f70513ae1f8d92665bbbbda5c295c2cf1da5542881ae5eefe20c9af132"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"serde",
|
||||
@@ -2696,13 +2698,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.190"
|
||||
version = "1.0.193"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3"
|
||||
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2764,7 +2766,7 @@ dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2868,7 +2870,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2884,9 +2886,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.39"
|
||||
version = "2.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
|
||||
checksum = "13fa70a4ee923979ffb522cacce59d34421ebdea5625e1073c4326ef9d2dd42e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2973,7 +2975,7 @@ dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2985,7 +2987,7 @@ dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
"test-case-core",
|
||||
]
|
||||
|
||||
@@ -3006,7 +3008,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3168,7 +3170,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3334,9 +3336,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "ureq"
|
||||
version = "2.8.0"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5ccd538d4a604753ebc2f17cd9946e89b77bf87f6a8e2309667c6f2e87855e3"
|
||||
checksum = "f8cdd25c339e200129fe4de81451814e5228c9b771d57378817d6117cc2b3f97"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"flate2",
|
||||
@@ -3350,9 +3352,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.4.1"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5"
|
||||
checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
@@ -3386,7 +3388,7 @@ checksum = "f49e7f3f3db8040a100710a11932239fd30697115e2ba4107080d8252939845e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3461,9 +3463,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.88"
|
||||
version = "0.2.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce"
|
||||
checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
@@ -3471,16 +3473,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.88"
|
||||
version = "0.2.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217"
|
||||
checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -3498,9 +3500,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.88"
|
||||
version = "0.2.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2"
|
||||
checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -3508,22 +3510,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.88"
|
||||
version = "0.2.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907"
|
||||
checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.88"
|
||||
version = "0.2.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b"
|
||||
checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test"
|
||||
@@ -3547,7 +3549,7 @@ checksum = "493fcbab756bb764fa37e6bee8cec2dd709eb4273d06d0c282a5e74275ded735"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3644,6 +3646,15 @@ dependencies = [
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.42.2"
|
||||
@@ -3674,6 +3685,21 @@ dependencies = [
|
||||
"windows_x86_64_msvc 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.0",
|
||||
"windows_aarch64_msvc 0.52.0",
|
||||
"windows_i686_gnu 0.52.0",
|
||||
"windows_i686_msvc 0.52.0",
|
||||
"windows_x86_64_gnu 0.52.0",
|
||||
"windows_x86_64_gnullvm 0.52.0",
|
||||
"windows_x86_64_msvc 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.2"
|
||||
@@ -3686,6 +3712,12 @@ version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.2"
|
||||
@@ -3698,6 +3730,12 @@ version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.2"
|
||||
@@ -3710,6 +3748,12 @@ version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.2"
|
||||
@@ -3722,6 +3766,12 @@ version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.2"
|
||||
@@ -3734,6 +3784,12 @@ version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.2"
|
||||
@@ -3746,6 +3802,12 @@ version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.2"
|
||||
@@ -3758,6 +3820,12 @@ version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.5.15"
|
||||
@@ -3796,3 +3864,23 @@ checksum = "fe5c30ade05e61656247b2e334a031dfd0cc466fadef865bdcdea8d537951bf1"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "306dca4455518f1f31635ec308b6b3e4eb1b11758cefafc782827d0aa7acb5c7"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be912bf68235a88fbefd1b73415cb218405958d1655b2ece9035a19920bdf6ba"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.40",
|
||||
]
|
||||
|
||||
10
Cargo.toml
10
Cargo.toml
@@ -17,12 +17,12 @@ bitflags = { version = "2.4.1" }
|
||||
chrono = { version = "0.4.31", default-features = false, features = ["clock"] }
|
||||
clap = { version = "4.4.7", features = ["derive"] }
|
||||
colored = { version = "2.0.0" }
|
||||
filetime = { version = "0.2.20" }
|
||||
filetime = { version = "0.2.23" }
|
||||
glob = { version = "0.3.1" }
|
||||
globset = { version = "0.4.14" }
|
||||
ignore = { version = "0.4.20" }
|
||||
insta = { version = "1.34.0", feature = ["filters", "glob"] }
|
||||
is-macro = { version = "0.3.0" }
|
||||
is-macro = { version = "0.3.1" }
|
||||
itertools = { version = "0.11.0" }
|
||||
libcst = { version = "1.1.0", default-features = false }
|
||||
log = { version = "0.4.17" }
|
||||
@@ -33,8 +33,8 @@ proc-macro2 = { version = "1.0.70" }
|
||||
quote = { version = "1.0.23" }
|
||||
regex = { version = "1.10.2" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
schemars = { version = "0.8.15" }
|
||||
serde = { version = "1.0.190", features = ["derive"] }
|
||||
schemars = { version = "0.8.16" }
|
||||
serde = { version = "1.0.193", features = ["derive"] }
|
||||
serde_json = { version = "1.0.108" }
|
||||
shellexpand = { version = "3.0.0" }
|
||||
similar = { version = "2.3.0", features = ["inline"] }
|
||||
@@ -42,7 +42,7 @@ smallvec = { version = "1.11.2" }
|
||||
static_assertions = "1.1.0"
|
||||
strum = { version = "0.25.0", features = ["strum_macros"] }
|
||||
strum_macros = { version = "0.25.3" }
|
||||
syn = { version = "2.0.39" }
|
||||
syn = { version = "2.0.40" }
|
||||
test-case = { version = "3.2.1" }
|
||||
thiserror = { version = "1.0.50" }
|
||||
toml = { version = "0.7.8" }
|
||||
|
||||
@@ -150,7 +150,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff
|
||||
```yaml
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.1.6
|
||||
rev: v0.1.7
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
description = """
|
||||
Convert Flake8 configuration files to Ruff configuration files.
|
||||
"""
|
||||
|
||||
@@ -34,8 +34,8 @@ harness = false
|
||||
once_cell.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
url = "2.3.1"
|
||||
ureq = "2.8.0"
|
||||
url = "2.5.0"
|
||||
ureq = "2.9.1"
|
||||
criterion = { version = "0.5.1", default-features = false }
|
||||
codspeed-criterion-compat = { version="2.3.3", default-features = false, optional = true}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use ruff_benchmark::criterion::{
|
||||
criterion_group, criterion_main, BenchmarkId, Criterion, Throughput,
|
||||
};
|
||||
use ruff_benchmark::{TestCase, TestFile, TestFileDownloadError};
|
||||
use ruff_python_formatter::{format_module_ast, PyFormatOptions};
|
||||
use ruff_python_formatter::{format_module_ast, PreviewMode, PyFormatOptions};
|
||||
use ruff_python_index::CommentRangesBuilder;
|
||||
use ruff_python_parser::lexer::lex;
|
||||
use ruff_python_parser::{parse_tokens, Mode};
|
||||
@@ -69,7 +69,8 @@ fn benchmark_formatter(criterion: &mut Criterion) {
|
||||
.expect("Input to be a valid python program");
|
||||
|
||||
b.iter(|| {
|
||||
let options = PyFormatOptions::from_extension(Path::new(case.name()));
|
||||
let options = PyFormatOptions::from_extension(Path::new(case.name()))
|
||||
.with_preview(PreviewMode::Enabled);
|
||||
let formatted =
|
||||
format_module_ast(&module, &comment_ranges, case.code(), options)
|
||||
.expect("Formatting to succeed");
|
||||
|
||||
@@ -3,7 +3,9 @@ use ruff_benchmark::criterion::{
|
||||
};
|
||||
use ruff_benchmark::{TestCase, TestFile, TestFileDownloadError};
|
||||
use ruff_linter::linter::lint_only;
|
||||
use ruff_linter::rule_selector::PreviewOptions;
|
||||
use ruff_linter::settings::rule_table::RuleTable;
|
||||
use ruff_linter::settings::types::PreviewMode;
|
||||
use ruff_linter::settings::{flags, LinterSettings};
|
||||
use ruff_linter::source_kind::SourceKind;
|
||||
use ruff_linter::{registry::Rule, RuleSelector};
|
||||
@@ -78,12 +80,21 @@ fn benchmark_default_rules(criterion: &mut Criterion) {
|
||||
benchmark_linter(group, &LinterSettings::default());
|
||||
}
|
||||
|
||||
fn benchmark_all_rules(criterion: &mut Criterion) {
|
||||
let mut rules: RuleTable = RuleSelector::All.all_rules().collect();
|
||||
|
||||
// Disable IO based rules because it is a source of flakiness
|
||||
/// Disables IO based rules because they are a source of flakiness
|
||||
fn disable_io_rules(rules: &mut RuleTable) {
|
||||
rules.disable(Rule::ShebangMissingExecutableFile);
|
||||
rules.disable(Rule::ShebangNotExecutable);
|
||||
}
|
||||
|
||||
fn benchmark_all_rules(criterion: &mut Criterion) {
|
||||
let mut rules: RuleTable = RuleSelector::All
|
||||
.rules(&PreviewOptions {
|
||||
mode: PreviewMode::Disabled,
|
||||
require_explicit: false,
|
||||
})
|
||||
.collect();
|
||||
|
||||
disable_io_rules(&mut rules);
|
||||
|
||||
let settings = LinterSettings {
|
||||
rules,
|
||||
@@ -94,6 +105,22 @@ fn benchmark_all_rules(criterion: &mut Criterion) {
|
||||
benchmark_linter(group, &settings);
|
||||
}
|
||||
|
||||
fn benchmark_preview_rules(criterion: &mut Criterion) {
|
||||
let mut rules: RuleTable = RuleSelector::All.all_rules().collect();
|
||||
|
||||
disable_io_rules(&mut rules);
|
||||
|
||||
let settings = LinterSettings {
|
||||
rules,
|
||||
preview: PreviewMode::Enabled,
|
||||
..LinterSettings::default()
|
||||
};
|
||||
|
||||
let group = criterion.benchmark_group("linter/all-with-preview-rules");
|
||||
benchmark_linter(group, &settings);
|
||||
}
|
||||
|
||||
criterion_group!(default_rules, benchmark_default_rules);
|
||||
criterion_group!(all_rules, benchmark_all_rules);
|
||||
criterion_main!(default_rules, all_rules);
|
||||
criterion_group!(preview_rules, benchmark_preview_rules);
|
||||
criterion_main!(default_rules, all_rules, preview_rules);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_cli"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
@@ -69,7 +69,7 @@ insta = { workspace = true, features = ["filters", "json"] }
|
||||
insta-cmd = { version = "0.4.0" }
|
||||
tempfile = "3.8.1"
|
||||
test-case = { workspace = true }
|
||||
ureq = { version = "2.8.0", features = [] }
|
||||
ureq = { version = "2.9.1", features = [] }
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
mimalloc = "0.1.39"
|
||||
|
||||
@@ -13,7 +13,7 @@ use ruff_linter::fs::relativize_path;
|
||||
use ruff_linter::logging::LogLevel;
|
||||
use ruff_linter::message::{
|
||||
AzureEmitter, Emitter, EmitterContext, GithubEmitter, GitlabEmitter, GroupedEmitter,
|
||||
JsonEmitter, JsonLinesEmitter, JunitEmitter, PylintEmitter, TextEmitter,
|
||||
JsonEmitter, JsonLinesEmitter, JunitEmitter, PylintEmitter, SarifEmitter, TextEmitter,
|
||||
};
|
||||
use ruff_linter::notify_user;
|
||||
use ruff_linter::registry::{AsRule, Rule};
|
||||
@@ -125,15 +125,7 @@ impl Printer {
|
||||
if let Some(fixables) = fixables {
|
||||
let fix_prefix = format!("[{}]", "*".cyan());
|
||||
|
||||
if self.unsafe_fixes.is_enabled() {
|
||||
if fixables.applicable > 0 {
|
||||
writeln!(
|
||||
writer,
|
||||
"{fix_prefix} {} fixable with the --fix option.",
|
||||
fixables.applicable
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
if self.unsafe_fixes.is_hint() {
|
||||
if fixables.applicable > 0 && fixables.unapplicable_unsafe > 0 {
|
||||
let es = if fixables.unapplicable_unsafe == 1 {
|
||||
""
|
||||
@@ -163,6 +155,14 @@ impl Printer {
|
||||
fixables.unapplicable_unsafe
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
if fixables.applicable > 0 {
|
||||
writeln!(
|
||||
writer,
|
||||
"{fix_prefix} {} fixable with the --fix option.",
|
||||
fixables.applicable
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -291,6 +291,9 @@ impl Printer {
|
||||
SerializationFormat::Azure => {
|
||||
AzureEmitter.emit(writer, &diagnostics.messages, &context)?;
|
||||
}
|
||||
SerializationFormat::Sarif => {
|
||||
SarifEmitter.emit(writer, &diagnostics.messages, &context)?;
|
||||
}
|
||||
}
|
||||
|
||||
writer.flush()?;
|
||||
|
||||
@@ -1158,6 +1158,44 @@ fn check_hints_hidden_unsafe_fixes_with_no_safe_fixes() {
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_no_hint_for_hidden_unsafe_fixes_when_disabled() {
|
||||
let mut cmd = RuffCheck::default()
|
||||
.args(["--select", "F601,UP034", "--no-unsafe-fixes"])
|
||||
.build();
|
||||
assert_cmd_snapshot!(cmd
|
||||
.pass_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\n"),
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:14: F601 Dictionary key literal `'a'` repeated
|
||||
-:2:7: UP034 [*] Avoid extraneous parentheses
|
||||
Found 2 errors.
|
||||
[*] 1 fixable with the --fix option.
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_no_hint_for_hidden_unsafe_fixes_with_no_safe_fixes_when_disabled() {
|
||||
let mut cmd = RuffCheck::default()
|
||||
.args(["--select", "F601", "--no-unsafe-fixes"])
|
||||
.build();
|
||||
assert_cmd_snapshot!(cmd
|
||||
.pass_stdin("x = {'a': 1, 'a': 1}\n"),
|
||||
@r###"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:14: F601 Dictionary key literal `'a'` repeated
|
||||
Found 1 error.
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_shows_unsafe_fixes_with_opt_in() {
|
||||
let mut cmd = RuffCheck::default()
|
||||
|
||||
@@ -396,3 +396,43 @@ if __name__ == "__main__":
|
||||
"###);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Regression test for [#8858](https://github.com/astral-sh/ruff/issues/8858)
|
||||
#[test]
|
||||
fn parent_configuration_override() -> Result<()> {
|
||||
let tempdir = TempDir::new()?;
|
||||
let root_ruff = tempdir.path().join("ruff.toml");
|
||||
fs::write(
|
||||
root_ruff,
|
||||
r#"
|
||||
[lint]
|
||||
select = ["ALL"]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let sub_dir = tempdir.path().join("subdirectory");
|
||||
fs::create_dir(&sub_dir)?;
|
||||
|
||||
let subdirectory_ruff = sub_dir.join("ruff.toml");
|
||||
fs::write(
|
||||
subdirectory_ruff,
|
||||
r#"
|
||||
[lint]
|
||||
ignore = ["D203", "D212"]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
|
||||
.current_dir(sub_dir)
|
||||
.arg("check")
|
||||
.args(STDIN_BASE_OPTIONS)
|
||||
, @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
warning: No Python files found under the given path(s)
|
||||
"###);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,30 +1,34 @@
|
||||
//! Print the AST for a given Python file.
|
||||
#![allow(clippy::print_stdout, clippy::print_stderr)]
|
||||
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::Result;
|
||||
use ruff_python_parser::{parse, Mode};
|
||||
|
||||
use ruff_linter::source_kind::SourceKind;
|
||||
use ruff_python_ast::PySourceType;
|
||||
use ruff_python_parser::{parse, AsMode};
|
||||
|
||||
#[derive(clap::Args)]
|
||||
pub(crate) struct Args {
|
||||
/// Python file for which to generate the AST.
|
||||
#[arg(required = true)]
|
||||
file: PathBuf,
|
||||
/// Run in Jupyter mode i.e., allow line magics.
|
||||
#[arg(long)]
|
||||
jupyter: bool,
|
||||
}
|
||||
|
||||
pub(crate) fn main(args: &Args) -> Result<()> {
|
||||
let contents = fs::read_to_string(&args.file)?;
|
||||
let mode = if args.jupyter {
|
||||
Mode::Ipython
|
||||
} else {
|
||||
Mode::Module
|
||||
};
|
||||
let python_ast = parse(&contents, mode, &args.file.to_string_lossy())?;
|
||||
let source_type = PySourceType::from(&args.file);
|
||||
let source_kind = SourceKind::from_path(&args.file, source_type)?.ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"Could not determine source kind for file: {}",
|
||||
args.file.display()
|
||||
)
|
||||
})?;
|
||||
let python_ast = parse(
|
||||
source_kind.source_code(),
|
||||
source_type.as_mode(),
|
||||
&args.file.to_string_lossy(),
|
||||
)?;
|
||||
println!("{python_ast:#?}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
//! Print the token stream for a given Python file.
|
||||
#![allow(clippy::print_stdout, clippy::print_stderr)]
|
||||
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::Result;
|
||||
use ruff_python_parser::{lexer, Mode};
|
||||
|
||||
use ruff_linter::source_kind::SourceKind;
|
||||
use ruff_python_ast::PySourceType;
|
||||
use ruff_python_parser::{lexer, AsMode};
|
||||
|
||||
#[derive(clap::Args)]
|
||||
pub(crate) struct Args {
|
||||
/// Python file for which to generate the AST.
|
||||
#[arg(required = true)]
|
||||
file: PathBuf,
|
||||
/// Run in Jupyter mode i.e., allow line magics (`%`, `!`, `?`, `/`, `,`, `;`).
|
||||
#[arg(long)]
|
||||
jupyter: bool,
|
||||
}
|
||||
|
||||
pub(crate) fn main(args: &Args) -> Result<()> {
|
||||
let contents = fs::read_to_string(&args.file)?;
|
||||
let mode = if args.jupyter {
|
||||
Mode::Ipython
|
||||
} else {
|
||||
Mode::Module
|
||||
};
|
||||
for (tok, range) in lexer::lex(&contents, mode).flatten() {
|
||||
let source_type = PySourceType::from(&args.file);
|
||||
let source_kind = SourceKind::from_path(&args.file, source_type)?.ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"Could not determine source kind for file: {}",
|
||||
args.file.display()
|
||||
)
|
||||
})?;
|
||||
for (tok, range) in lexer::lex(source_kind.source_code(), source_type.as_mode()).flatten() {
|
||||
println!(
|
||||
"{start:#?} {tok:#?} {end:#?}",
|
||||
start = range.start(),
|
||||
|
||||
@@ -1,23 +1,9 @@
|
||||
use super::{Buffer, Format, Formatter};
|
||||
use crate::FormatResult;
|
||||
use std::ffi::c_void;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// Mono-morphed type to format an object. Used by the [`crate::format`!], [`crate::format_args`!], and
|
||||
/// [`crate::write`!] macros.
|
||||
///
|
||||
/// This struct is similar to a dynamic dispatch (using `dyn Format`) because it stores a pointer to the value.
|
||||
/// However, it doesn't store the pointer to `dyn Format`'s vtable, instead it statically resolves the function
|
||||
/// pointer of `Format::format` and stores it in `formatter`.
|
||||
/// A convenience wrapper for representing a formattable argument.
|
||||
pub struct Argument<'fmt, Context> {
|
||||
/// The value to format stored as a raw pointer where `lifetime` stores the value's lifetime.
|
||||
value: *const c_void,
|
||||
|
||||
/// Stores the lifetime of the value. To get the most out of our dear borrow checker.
|
||||
lifetime: PhantomData<&'fmt ()>,
|
||||
|
||||
/// The function pointer to `value`'s `Format::format` method
|
||||
formatter: fn(*const c_void, &mut Formatter<'_, Context>) -> FormatResult<()>,
|
||||
value: &'fmt dyn Format<Context>,
|
||||
}
|
||||
|
||||
impl<Context> Clone for Argument<'_, Context> {
|
||||
@@ -28,32 +14,19 @@ impl<Context> Clone for Argument<'_, Context> {
|
||||
impl<Context> Copy for Argument<'_, Context> {}
|
||||
|
||||
impl<'fmt, Context> Argument<'fmt, Context> {
|
||||
/// Called by the [ruff_formatter::format_args] macro. Creates a mono-morphed value for formatting
|
||||
/// an object.
|
||||
/// Called by the [ruff_formatter::format_args] macro.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
pub fn new<F: Format<Context>>(value: &'fmt F) -> Self {
|
||||
#[inline]
|
||||
fn formatter<F: Format<Context>, Context>(
|
||||
ptr: *const c_void,
|
||||
fmt: &mut Formatter<Context>,
|
||||
) -> FormatResult<()> {
|
||||
// SAFETY: Safe because the 'fmt lifetime is captured by the 'lifetime' field.
|
||||
#[allow(unsafe_code)]
|
||||
F::fmt(unsafe { &*ptr.cast::<F>() }, fmt)
|
||||
}
|
||||
|
||||
Self {
|
||||
value: (value as *const F).cast::<std::ffi::c_void>(),
|
||||
lifetime: PhantomData,
|
||||
formatter: formatter::<F, Context>,
|
||||
}
|
||||
Self { value }
|
||||
}
|
||||
|
||||
/// Formats the value stored by this argument using the given formatter.
|
||||
#[inline]
|
||||
// Seems to only be triggered on wasm32 and looks like a false positive?
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
pub(super) fn format(&self, f: &mut Formatter<Context>) -> FormatResult<()> {
|
||||
(self.formatter)(self.value, f)
|
||||
self.value.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2555,17 +2555,17 @@ pub struct BestFitting<'a, Context> {
|
||||
}
|
||||
|
||||
impl<'a, Context> BestFitting<'a, Context> {
|
||||
/// Creates a new best fitting IR with the given variants. The method itself isn't unsafe
|
||||
/// but it is to discourage people from using it because the printer will panic if
|
||||
/// the slice doesn't contain at least the least and most expanded variants.
|
||||
/// Creates a new best fitting IR with the given variants.
|
||||
///
|
||||
/// Callers are required to ensure that the number of variants given
|
||||
/// is at least 2.
|
||||
///
|
||||
/// You're looking for a way to create a `BestFitting` object, use the `best_fitting![least_expanded, most_expanded]` macro.
|
||||
///
|
||||
/// ## Safety
|
||||
|
||||
/// The slice must contain at least two variants.
|
||||
#[allow(unsafe_code)]
|
||||
pub unsafe fn from_arguments_unchecked(variants: Arguments<'a, Context>) -> Self {
|
||||
/// # Panics
|
||||
///
|
||||
/// When the slice contains less than two variants.
|
||||
pub fn from_arguments_unchecked(variants: Arguments<'a, Context>) -> Self {
|
||||
assert!(
|
||||
variants.0.len() >= 2,
|
||||
"Requires at least the least expanded and most expanded variants"
|
||||
@@ -2696,14 +2696,12 @@ impl<Context> Format<Context> for BestFitting<'_, Context> {
|
||||
buffer.write_element(FormatElement::Tag(EndBestFittingEntry));
|
||||
}
|
||||
|
||||
// SAFETY: The constructor guarantees that there are always at least two variants. It's, therefore,
|
||||
// safe to call into the unsafe `from_vec_unchecked` function
|
||||
#[allow(unsafe_code)]
|
||||
let element = unsafe {
|
||||
FormatElement::BestFitting {
|
||||
variants: BestFittingVariants::from_vec_unchecked(buffer.into_vec()),
|
||||
mode: self.mode,
|
||||
}
|
||||
// OK because the constructor guarantees that there are always at
|
||||
// least two variants.
|
||||
let variants = BestFittingVariants::from_vec_unchecked(buffer.into_vec());
|
||||
let element = FormatElement::BestFitting {
|
||||
variants,
|
||||
mode: self.mode,
|
||||
};
|
||||
|
||||
f.write_element(element);
|
||||
|
||||
@@ -332,17 +332,14 @@ pub enum BestFittingMode {
|
||||
pub struct BestFittingVariants(Box<[FormatElement]>);
|
||||
|
||||
impl BestFittingVariants {
|
||||
/// Creates a new best fitting IR with the given variants. The method itself isn't unsafe
|
||||
/// but it is to discourage people from using it because the printer will panic if
|
||||
/// the slice doesn't contain at least the least and most expanded variants.
|
||||
/// Creates a new best fitting IR with the given variants.
|
||||
///
|
||||
/// Callers are required to ensure that the number of variants given
|
||||
/// is at least 2 when using `most_expanded` or `most_flag`.
|
||||
///
|
||||
/// You're looking for a way to create a `BestFitting` object, use the `best_fitting![least_expanded, most_expanded]` macro.
|
||||
///
|
||||
/// ## Safety
|
||||
/// The slice must contain at least two variants.
|
||||
#[doc(hidden)]
|
||||
#[allow(unsafe_code)]
|
||||
pub unsafe fn from_vec_unchecked(variants: Vec<FormatElement>) -> Self {
|
||||
pub fn from_vec_unchecked(variants: Vec<FormatElement>) -> Self {
|
||||
debug_assert!(
|
||||
variants
|
||||
.iter()
|
||||
@@ -351,12 +348,23 @@ impl BestFittingVariants {
|
||||
>= 2,
|
||||
"Requires at least the least expanded and most expanded variants"
|
||||
);
|
||||
|
||||
Self(variants.into_boxed_slice())
|
||||
}
|
||||
|
||||
/// Returns the most expanded variant
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// When the number of variants is less than two.
|
||||
pub fn most_expanded(&self) -> &[FormatElement] {
|
||||
assert!(
|
||||
self.as_slice()
|
||||
.iter()
|
||||
.filter(|element| matches!(element, FormatElement::Tag(Tag::StartBestFittingEntry)))
|
||||
.count()
|
||||
>= 2,
|
||||
"Requires at least the least expanded and most expanded variants"
|
||||
);
|
||||
self.into_iter().last().unwrap()
|
||||
}
|
||||
|
||||
@@ -365,7 +373,19 @@ impl BestFittingVariants {
|
||||
}
|
||||
|
||||
/// Returns the least expanded variant
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// When the number of variants is less than two.
|
||||
pub fn most_flat(&self) -> &[FormatElement] {
|
||||
assert!(
|
||||
self.as_slice()
|
||||
.iter()
|
||||
.filter(|element| matches!(element, FormatElement::Tag(Tag::StartBestFittingEntry)))
|
||||
.count()
|
||||
>= 2,
|
||||
"Requires at least the least expanded and most expanded variants"
|
||||
);
|
||||
self.into_iter().next().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,10 +329,8 @@ macro_rules! format {
|
||||
#[macro_export]
|
||||
macro_rules! best_fitting {
|
||||
($least_expanded:expr, $($tail:expr),+ $(,)?) => {{
|
||||
#[allow(unsafe_code)]
|
||||
unsafe {
|
||||
$crate::BestFitting::from_arguments_unchecked($crate::format_args!($least_expanded, $($tail),+))
|
||||
}
|
||||
// OK because the macro syntax requires at least two variants.
|
||||
$crate::BestFitting::from_arguments_unchecked($crate::format_args!($least_expanded, $($tail),+))
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_linter"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
@@ -71,6 +71,7 @@ toml = { workspace = true }
|
||||
typed-arena = { version = "2.0.2" }
|
||||
unicode-width = { workspace = true }
|
||||
unicode_names2 = { workspace = true }
|
||||
url = { version = "2.2.2" }
|
||||
wsl = { version = "0.1.0" }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -39,3 +39,18 @@ def func():
|
||||
for i in range(1110):
|
||||
if True:
|
||||
break
|
||||
|
||||
# TODO(charlie): The `pass` here does not get properly redirected to the top of the
|
||||
# loop, unlike below.
|
||||
def func():
|
||||
for i in range(5):
|
||||
pass
|
||||
else:
|
||||
return 1
|
||||
|
||||
def func():
|
||||
for i in range(5):
|
||||
pass
|
||||
else:
|
||||
return 1
|
||||
x = 1
|
||||
|
||||
@@ -129,3 +129,11 @@ def func():
|
||||
print("Grass is green")
|
||||
case Color.BLUE:
|
||||
print("I'm feeling the blues :(")
|
||||
|
||||
|
||||
def func(point):
|
||||
match point:
|
||||
case (0, 0):
|
||||
print("Origin")
|
||||
case foo:
|
||||
raise ValueError("oops")
|
||||
|
||||
@@ -30,3 +30,155 @@ def func(x: int):
|
||||
|
||||
def func(x: int):
|
||||
return 1 + 2.5 if x > 0 else 1.5 or "str"
|
||||
|
||||
|
||||
def func(x: int):
|
||||
if not x:
|
||||
return None
|
||||
return {"foo": 1}
|
||||
|
||||
|
||||
def func(x: int):
|
||||
return {"foo": 1}
|
||||
|
||||
|
||||
def func(x: int):
|
||||
if not x:
|
||||
return 1
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def func(x: int):
|
||||
if not x:
|
||||
return 1
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def func(x: int):
|
||||
if not x:
|
||||
return 1
|
||||
elif x > 5:
|
||||
return "str"
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def func(x: int):
|
||||
if x:
|
||||
return 1
|
||||
|
||||
|
||||
def func():
|
||||
x = 1
|
||||
|
||||
|
||||
def func(x: int):
|
||||
if x > 0:
|
||||
return 1
|
||||
|
||||
|
||||
def func(x: int):
|
||||
match x:
|
||||
case [1, 2, 3]:
|
||||
return 1
|
||||
case 4 as y:
|
||||
return "foo"
|
||||
|
||||
|
||||
def func(x: int):
|
||||
for i in range(5):
|
||||
if i > 0:
|
||||
return 1
|
||||
|
||||
|
||||
def func(x: int):
|
||||
for i in range(5):
|
||||
if i > 0:
|
||||
return 1
|
||||
else:
|
||||
return 4
|
||||
|
||||
|
||||
def func(x: int):
|
||||
for i in range(5):
|
||||
if i > 0:
|
||||
break
|
||||
else:
|
||||
return 4
|
||||
|
||||
|
||||
def func(x: int):
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
return 1
|
||||
|
||||
|
||||
def func(x: int):
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
return 1
|
||||
finally:
|
||||
return 2
|
||||
|
||||
|
||||
def func(x: int):
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
return 1
|
||||
else:
|
||||
return 2
|
||||
|
||||
|
||||
def func(x: int):
|
||||
try:
|
||||
return 1
|
||||
except:
|
||||
return 2
|
||||
else:
|
||||
pass
|
||||
|
||||
|
||||
def func(x: int):
|
||||
while x > 0:
|
||||
break
|
||||
return 1
|
||||
|
||||
|
||||
import abc
|
||||
from abc import abstractmethod
|
||||
|
||||
|
||||
class Foo(abc.ABC):
|
||||
@abstractmethod
|
||||
def method(self):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def method(self):
|
||||
"""Docstring."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def method(self):
|
||||
...
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def method():
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def method(cls):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def method(self):
|
||||
if self.x > 0:
|
||||
return 1
|
||||
else:
|
||||
return 1.5
|
||||
|
||||
@@ -8,6 +8,7 @@ def func(address):
|
||||
# Error
|
||||
"0.0.0.0"
|
||||
'0.0.0.0'
|
||||
f"0.0.0.0"
|
||||
|
||||
|
||||
# Error
|
||||
|
||||
@@ -5,6 +5,9 @@ with open("/abc/tmp", "w") as f:
|
||||
with open("/tmp/abc", "w") as f:
|
||||
f.write("def")
|
||||
|
||||
with open(f"/tmp/abc", "w") as f:
|
||||
f.write("def")
|
||||
|
||||
with open("/var/tmp/123", "w") as f:
|
||||
f.write("def")
|
||||
|
||||
|
||||
@@ -106,3 +106,11 @@ def func(x: bool | str):
|
||||
|
||||
def func(x: int | str):
|
||||
pass
|
||||
|
||||
|
||||
from typing import override
|
||||
|
||||
|
||||
@override
|
||||
def func(x: bool):
|
||||
pass
|
||||
|
||||
@@ -27,7 +27,7 @@ def f_ok():
|
||||
raise RuntimeError(msg)
|
||||
|
||||
|
||||
def f_unfixable():
|
||||
def f_msg_defined():
|
||||
msg = "hello"
|
||||
raise RuntimeError("This is an example exception")
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ Foo.objects.create(**{**bar}) # PIE804
|
||||
|
||||
foo(**{})
|
||||
|
||||
|
||||
foo(**{**data, "foo": "buzz"})
|
||||
foo(**buzz)
|
||||
foo(**{"bar-foo": True})
|
||||
@@ -20,3 +19,8 @@ foo(**{buzz: True})
|
||||
foo(**{"": True})
|
||||
foo(**{f"buzz__{bar}": True})
|
||||
abc(**{"for": 3})
|
||||
foo(**{},)
|
||||
|
||||
# Duplicated key names won't be fixed, to avoid syntax errors.
|
||||
abc(**{'a': b}, **{'a': c}) # PIE804
|
||||
abc(a=1, **{'a': c}, **{'b': c}) # PIE804
|
||||
|
||||
@@ -36,3 +36,54 @@ field10: (Literal[1] | str) | Literal[2] # Error
|
||||
|
||||
# Should emit for union in generic parent type.
|
||||
field11: dict[Literal[1] | Literal[2], str] # Error
|
||||
|
||||
# Should emit for unions with more than two cases
|
||||
field12: Literal[1] | Literal[2] | Literal[3] # Error
|
||||
field13: Literal[1] | Literal[2] | Literal[3] | Literal[4] # Error
|
||||
|
||||
# Should emit for unions with more than two cases, even if not directly adjacent
|
||||
field14: Literal[1] | Literal[2] | str | Literal[3] # Error
|
||||
|
||||
# Should emit for unions with mixed literal internal types
|
||||
field15: Literal[1] | Literal["foo"] | Literal[True] # Error
|
||||
|
||||
# Shouldn't emit for duplicate field types with same value; covered by Y016
|
||||
field16: Literal[1] | Literal[1] # OK
|
||||
|
||||
# Shouldn't emit if in new parent type
|
||||
field17: Literal[1] | dict[Literal[2], str] # OK
|
||||
|
||||
# Shouldn't emit if not in a union parent
|
||||
field18: dict[Literal[1], Literal[2]] # OK
|
||||
|
||||
# Should respect name of literal type used
|
||||
field19: typing.Literal[1] | typing.Literal[2] # Error
|
||||
|
||||
# Should emit in cases with newlines
|
||||
field20: typing.Union[
|
||||
Literal[
|
||||
1 # test
|
||||
],
|
||||
Literal[2],
|
||||
] # Error, newline and comment will not be emitted in message
|
||||
|
||||
# Should handle multiple unions with multiple members
|
||||
field21: Literal[1, 2] | Literal[3, 4] # Error
|
||||
|
||||
# Should emit in cases with `typing.Union` instead of `|`
|
||||
field22: typing.Union[Literal[1], Literal[2]] # Error
|
||||
|
||||
# Should emit in cases with `typing_extensions.Literal`
|
||||
field23: typing_extensions.Literal[1] | typing_extensions.Literal[2] # Error
|
||||
|
||||
# Should emit in cases with nested `typing.Union`
|
||||
field24: typing.Union[Literal[1], typing.Union[Literal[2], str]] # Error
|
||||
|
||||
# Should emit in cases with mixed `typing.Union` and `|`
|
||||
field25: typing.Union[Literal[1], Literal[2] | str] # Error
|
||||
|
||||
# Should emit only once in cases with multiple nested `typing.Union`
|
||||
field24: typing.Union[Literal[1], typing.Union[Literal[2], typing.Union[Literal[3], Literal[4]]]] # Error
|
||||
|
||||
# Should use the first literal subscript attribute when fixing
|
||||
field25: typing.Union[typing_extensions.Literal[1], typing.Union[Literal[2], typing.Union[Literal[3], Literal[4]]], str] # Error
|
||||
|
||||
@@ -84,3 +84,6 @@ field25: typing.Union[Literal[1], Literal[2] | str] # Error
|
||||
|
||||
# Should emit only once in cases with multiple nested `typing.Union`
|
||||
field24: typing.Union[Literal[1], typing.Union[Literal[2], typing.Union[Literal[3], Literal[4]]]] # Error
|
||||
|
||||
# Should use the first literal subscript attribute when fixing
|
||||
field25: typing.Union[typing_extensions.Literal[1], typing.Union[Literal[2], typing.Union[Literal[3], Literal[4]]], str] # Error
|
||||
|
||||
@@ -22,3 +22,7 @@ Snake_case_alias: TypeAlias = int | float # PYI042, since not camel case
|
||||
|
||||
# check that this edge case doesn't crash
|
||||
_: TypeAlias = str | int
|
||||
|
||||
# PEP 695
|
||||
type foo_bar = int | str
|
||||
type FooBar = int | str
|
||||
|
||||
@@ -22,3 +22,7 @@ Snake_case_alias: TypeAlias = int | float # PYI042, since not camel case
|
||||
|
||||
# check that this edge case doesn't crash
|
||||
_: TypeAlias = str | int
|
||||
|
||||
# PEP 695
|
||||
type foo_bar = int | str
|
||||
type FooBar = int | str
|
||||
|
||||
@@ -21,3 +21,7 @@ _PrivateAliasS2: TypeAlias = Annotated[str, "also okay"]
|
||||
|
||||
# check that this edge case doesn't crash
|
||||
_: TypeAlias = str | int
|
||||
|
||||
# PEP 695
|
||||
type _FooT = str | int
|
||||
type Foo = str | int
|
||||
|
||||
@@ -21,3 +21,7 @@ _PrivateAliasS2: TypeAlias = Annotated[str, "also okay"]
|
||||
|
||||
# check that this edge case doesn't crash
|
||||
_: TypeAlias = str | int
|
||||
|
||||
# PEP 695
|
||||
type _FooT = str | int
|
||||
type Foo = str | int
|
||||
|
||||
@@ -10,3 +10,14 @@ def foo_no_return_typing_extensions(
|
||||
def foo_no_return_kwarg(arg: int, *, arg2: NoReturn): ... # Error: PYI050
|
||||
def foo_no_return_pos_only(arg: int, /, arg2: NoReturn): ... # Error: PYI050
|
||||
def foo_never(arg: Never): ...
|
||||
def foo_args(*args: NoReturn): ... # Error: PYI050
|
||||
def foo_kwargs(**kwargs: NoReturn): ... # Error: PYI050
|
||||
def foo_args_kwargs(*args: NoReturn, **kwargs: NoReturn): ... # Error: PYI050
|
||||
def foo_int_args(*args: int): ...
|
||||
def foo_int_kwargs(**kwargs: int): ...
|
||||
def foo_int_args_kwargs(*args: int, **kwargs: int): ...
|
||||
def foo_int_args_no_return(*args: int, **kwargs: NoReturn): ... # Error: PYI050
|
||||
def foo_int_kwargs_no_return(*args: NoReturn, **kwargs: int): ... # Error: PYI050
|
||||
def foo_args_never(*args: Never): ...
|
||||
def foo_kwargs_never(**kwargs: Never): ...
|
||||
def foo_args_kwargs_never(*args: Never, **kwargs: Never): ...
|
||||
|
||||
@@ -32,6 +32,7 @@ def f8(x: bytes = b"50 character byte stringgggggggggggggggggggggggggg\xff") ->
|
||||
|
||||
foo: str = "50 character stringggggggggggggggggggggggggggggggg"
|
||||
bar: str = "51 character stringgggggggggggggggggggggggggggggggg"
|
||||
baz: str = f"51 character stringgggggggggggggggggggggggggggggggg"
|
||||
|
||||
baz: bytes = b"50 character byte stringgggggggggggggggggggggggggg"
|
||||
|
||||
|
||||
@@ -29,6 +29,10 @@ baz: bytes = b"50 character byte stringgggggggggggggggggggggggggg" # OK
|
||||
|
||||
qux: bytes = b"51 character byte stringggggggggggggggggggggggggggg\xff" # Error: PYI053
|
||||
|
||||
ffoo: str = f"50 character stringggggggggggggggggggggggggggggggg" # OK
|
||||
|
||||
fbar: str = f"51 character stringgggggggggggggggggggggggggggggggg" # Error: PYI053
|
||||
|
||||
class Demo:
|
||||
"""Docstrings are excluded from this rule. Some padding.""" # OK
|
||||
|
||||
|
||||
@@ -82,3 +82,14 @@ raise IndexError();
|
||||
|
||||
# RSE102
|
||||
raise Foo()
|
||||
|
||||
# OK
|
||||
raise ctypes.WinError()
|
||||
|
||||
|
||||
def func():
|
||||
pass
|
||||
|
||||
|
||||
# OK
|
||||
raise func()
|
||||
|
||||
@@ -19,12 +19,48 @@ async def func():
|
||||
bar = "bar"
|
||||
trio.sleep(bar)
|
||||
|
||||
x, y = 0, 2000
|
||||
trio.sleep(x) # TRIO115
|
||||
trio.sleep(y) # OK
|
||||
|
||||
(a, b, [c, (d, e)]) = (1, 2, (0, [4, 0]))
|
||||
trio.sleep(c) # TRIO115
|
||||
trio.sleep(d) # OK
|
||||
trio.sleep(e) # TRIO115
|
||||
|
||||
m_x, m_y = 0
|
||||
trio.sleep(m_y) # OK
|
||||
trio.sleep(m_x) # OK
|
||||
|
||||
m_a = m_b = 0
|
||||
trio.sleep(m_a) # TRIO115
|
||||
trio.sleep(m_b) # TRIO115
|
||||
|
||||
m_c = (m_d, m_e) = (0, 0)
|
||||
trio.sleep(m_c) # OK
|
||||
trio.sleep(m_d) # TRIO115
|
||||
trio.sleep(m_e) # TRIO115
|
||||
|
||||
|
||||
def func():
|
||||
import trio
|
||||
|
||||
trio.run(trio.sleep(0)) # TRIO115
|
||||
|
||||
|
||||
from trio import Event, sleep
|
||||
|
||||
|
||||
def func():
|
||||
sleep(0) # TRIO115
|
||||
|
||||
|
||||
async def func():
|
||||
await sleep(seconds=0) # TRIO115
|
||||
|
||||
|
||||
def func():
|
||||
import trio
|
||||
|
||||
if (walrus := 0) == 0:
|
||||
trio.sleep(walrus) # TRIO115
|
||||
|
||||
67
crates/ruff_linter/resources/test/fixtures/flake8_type_checking/quote.py
vendored
Normal file
67
crates/ruff_linter/resources/test/fixtures/flake8_type_checking/quote.py
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
def f():
|
||||
from pandas import DataFrame
|
||||
|
||||
def baz() -> DataFrame:
|
||||
...
|
||||
|
||||
|
||||
def f():
|
||||
from pandas import DataFrame
|
||||
|
||||
def baz() -> DataFrame[int]:
|
||||
...
|
||||
|
||||
|
||||
def f():
|
||||
from pandas import DataFrame
|
||||
|
||||
def baz() -> DataFrame["int"]:
|
||||
...
|
||||
|
||||
|
||||
def f():
|
||||
import pandas as pd
|
||||
|
||||
def baz() -> pd.DataFrame:
|
||||
...
|
||||
|
||||
|
||||
def f():
|
||||
import pandas as pd
|
||||
|
||||
def baz() -> pd.DataFrame.Extra:
|
||||
...
|
||||
|
||||
|
||||
def f():
|
||||
import pandas as pd
|
||||
|
||||
def baz() -> pd.DataFrame | int:
|
||||
...
|
||||
|
||||
|
||||
|
||||
def f():
|
||||
from pandas import DataFrame
|
||||
|
||||
def baz() -> DataFrame():
|
||||
...
|
||||
|
||||
|
||||
def f():
|
||||
from typing import Literal
|
||||
|
||||
from pandas import DataFrame
|
||||
|
||||
def baz() -> DataFrame[Literal["int"]]:
|
||||
...
|
||||
|
||||
|
||||
def f():
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pandas import DataFrame
|
||||
|
||||
def func(value: DataFrame):
|
||||
...
|
||||
34
crates/ruff_linter/resources/test/fixtures/flake8_type_checking/singledispatch.py
vendored
Normal file
34
crates/ruff_linter/resources/test/fixtures/flake8_type_checking/singledispatch.py
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
"""Test module."""
|
||||
from __future__ import annotations
|
||||
|
||||
from functools import singledispatch
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from numpy import asarray
|
||||
from numpy.typing import ArrayLike
|
||||
from scipy.sparse import spmatrix
|
||||
from pandas import DataFrame
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from numpy import ndarray
|
||||
|
||||
|
||||
@singledispatch
|
||||
def to_array_or_mat(a: ArrayLike | spmatrix) -> ndarray | spmatrix:
|
||||
"""Convert arg to array or leaves it as sparse matrix."""
|
||||
msg = f"Unhandled type {type(a)}"
|
||||
raise NotImplementedError(msg)
|
||||
|
||||
|
||||
@to_array_or_mat.register
|
||||
def _(a: ArrayLike) -> ndarray:
|
||||
return asarray(a)
|
||||
|
||||
|
||||
@to_array_or_mat.register
|
||||
def _(a: spmatrix) -> spmatrix:
|
||||
return a
|
||||
|
||||
|
||||
def _(a: DataFrame) -> DataFrame:
|
||||
return a
|
||||
2
crates/ruff_linter/resources/test/fixtures/isort/force_sort_within_sections_future.py
vendored
Normal file
2
crates/ruff_linter/resources/test/fixtures/isort/force_sort_within_sections_future.py
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
import __future__
|
||||
from __future__ import annotations
|
||||
4
crates/ruff_linter/resources/test/fixtures/isort/force_sort_within_sections_lines_between.py
vendored
Normal file
4
crates/ruff_linter/resources/test/fixtures/isort/force_sort_within_sections_lines_between.py
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
from a import x
|
||||
import b
|
||||
from c import y
|
||||
import d
|
||||
2
crates/ruff_linter/resources/test/fixtures/isort/future_from.py
vendored
Normal file
2
crates/ruff_linter/resources/test/fixtures/isort/future_from.py
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
import __future__
|
||||
from __future__ import annotations
|
||||
3
crates/ruff_linter/resources/test/fixtures/isort/length_sort_from_imports.py
vendored
Normal file
3
crates/ruff_linter/resources/test/fixtures/isort/length_sort_from_imports.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
from mediuuuuuuuuuuum import a
|
||||
from short import b
|
||||
from loooooooooooooooooooooog import c
|
||||
11
crates/ruff_linter/resources/test/fixtures/isort/length_sort_non_ascii_members.py
vendored
Normal file
11
crates/ruff_linter/resources/test/fixtures/isort/length_sort_non_ascii_members.py
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
from module1 import (
|
||||
loooooooooooooong,
|
||||
σηορτ,
|
||||
mediuuuuum,
|
||||
shoort,
|
||||
looooooooooooooong,
|
||||
μεδιυυυυυμ,
|
||||
short,
|
||||
mediuuuuuum,
|
||||
λοοοοοοοοοοοοοονγ,
|
||||
)
|
||||
9
crates/ruff_linter/resources/test/fixtures/isort/length_sort_non_ascii_modules.py
vendored
Normal file
9
crates/ruff_linter/resources/test/fixtures/isort/length_sort_non_ascii_modules.py
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import loooooooooooooong
|
||||
import mediuuuuuum
|
||||
import short
|
||||
import σηορτ
|
||||
import shoort
|
||||
import mediuuuuum
|
||||
import λοοοοοοοοοοοοοονγ
|
||||
import μεδιυυυυυμ
|
||||
import looooooooooooooong
|
||||
6
crates/ruff_linter/resources/test/fixtures/isort/length_sort_straight_and_from_imports.py
vendored
Normal file
6
crates/ruff_linter/resources/test/fixtures/isort/length_sort_straight_and_from_imports.py
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
import mediuuuuuum
|
||||
import short
|
||||
import looooooooooooooooong
|
||||
from looooooooooooooong import a
|
||||
from mediuuuum import c
|
||||
from short import b
|
||||
4
crates/ruff_linter/resources/test/fixtures/isort/length_sort_straight_imports.py
vendored
Normal file
4
crates/ruff_linter/resources/test/fixtures/isort/length_sort_straight_imports.py
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
import mediuuuuuumb
|
||||
import short
|
||||
import looooooooooooooooong
|
||||
import mediuuuuuuma
|
||||
7
crates/ruff_linter/resources/test/fixtures/isort/length_sort_with_relative_imports.py
vendored
Normal file
7
crates/ruff_linter/resources/test/fixtures/isort/length_sort_with_relative_imports.py
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
from ..looooooooooooooong import a
|
||||
from ...mediuuuum import b
|
||||
from .short import c
|
||||
from ....short import c
|
||||
from . import d
|
||||
from .mediuuuum import a
|
||||
from ......short import b
|
||||
3
crates/ruff_linter/resources/test/fixtures/isort/length_sort_with_star_import.py
vendored
Normal file
3
crates/ruff_linter/resources/test/fixtures/isort/length_sort_with_star_import.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
from looooooooooooooong import a
|
||||
from mediuuuum import *
|
||||
from short import *
|
||||
@@ -1,6 +1,6 @@
|
||||
import collections
|
||||
from collections import namedtuple
|
||||
from typing import TypeAlias, TypeVar, NewType, NamedTuple, TypedDict
|
||||
from typing import Type, TypeAlias, TypeVar, NewType, NamedTuple, TypedDict
|
||||
|
||||
GLOBAL: str = "foo"
|
||||
|
||||
@@ -40,3 +40,21 @@ def loop_assign():
|
||||
global CURRENT_PORT
|
||||
for CURRENT_PORT in range(5):
|
||||
pass
|
||||
|
||||
|
||||
def model_assign() -> None:
|
||||
Bad = apps.get_model("zerver", "Stream") # N806
|
||||
Attachment = apps.get_model("zerver", "Attachment") # OK
|
||||
Recipient = apps.get_model("zerver", model_name="Recipient") # OK
|
||||
Address: Type = apps.get_model("zerver", "Address") # OK
|
||||
|
||||
from django.utils.module_loading import import_string
|
||||
|
||||
Bad = import_string("django.core.exceptions.ValidationError") # N806
|
||||
ValidationError = import_string("django.core.exceptions.ValidationError") # OK
|
||||
|
||||
Bad = apps.get_model() # N806
|
||||
Bad = apps.get_model(model_name="Stream") # N806
|
||||
|
||||
Address: Type = apps.get_model("zerver", variable) # OK
|
||||
ValidationError = import_string(variable) # N806
|
||||
|
||||
@@ -63,3 +63,8 @@ for i in list(foo_tuple): # Ok
|
||||
|
||||
for i in list(foo_set): # Ok
|
||||
foo_set.append(i + 1)
|
||||
|
||||
x, y, nested_tuple = (1, 2, (3, 4, 5))
|
||||
|
||||
for i in list(nested_tuple): # PERF101
|
||||
pass
|
||||
|
||||
@@ -72,3 +72,15 @@ a = 42 # (Two spaces)
|
||||
# EF Means test is giving error and Failing
|
||||
#! Means test is segfaulting
|
||||
# 8 Means test runs forever
|
||||
|
||||
#: Colon prefix is okay
|
||||
|
||||
###This is a variable ###
|
||||
|
||||
# We should strip the space, but preserve the hashes.
|
||||
#: E266:1:3
|
||||
## Foo
|
||||
|
||||
a = 1 ## Foo
|
||||
|
||||
a = 1 #:Foo
|
||||
|
||||
@@ -60,3 +60,6 @@ def f():
|
||||
if (a and
|
||||
b):
|
||||
pass
|
||||
#: Okay
|
||||
def f():
|
||||
return 1
|
||||
|
||||
113
crates/ruff_linter/resources/test/fixtures/pycodestyle/E402.ipynb
vendored
Normal file
113
crates/ruff_linter/resources/test/fixtures/pycodestyle/E402.ipynb
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "33faf7ad-a3fd-4ac4-a0c3-52e507ed49df",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"sys.path"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "1331140f-2741-4661-9086-0764368710c9",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "a4113383-725d-4f04-80b8-a3080b2b8c4b",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"os.path\n",
|
||||
"\n",
|
||||
"import pathlib"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "a5d2ef63-ae60-4311-bae3-42e845afba3f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "79599475-a5ee-4f60-80d1-6efa77693da0",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import a\n",
|
||||
"\n",
|
||||
"try:\n",
|
||||
" import b\n",
|
||||
"except ImportError:\n",
|
||||
" pass\n",
|
||||
"else:\n",
|
||||
" pass\n",
|
||||
"\n",
|
||||
"__some__magic = 1\n",
|
||||
"\n",
|
||||
"import c"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "863dcc35-5c8d-4d05-8b4a-91059e944112",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import ok\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def foo() -> None:\n",
|
||||
" import e\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"import no_ok"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "6b2377d0-b814-4057-83ec-d443d8e19401",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python (ruff-playground)",
|
||||
"language": "python",
|
||||
"name": "ruff-playground"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -19,21 +19,32 @@ if x > 0:
|
||||
else:
|
||||
import e
|
||||
|
||||
__some__magic = 1
|
||||
import sys
|
||||
sys.path.insert(0, "some/path")
|
||||
|
||||
import f
|
||||
|
||||
import matplotlib
|
||||
|
||||
matplotlib.use("Agg")
|
||||
|
||||
import g
|
||||
|
||||
__some__magic = 1
|
||||
|
||||
import h
|
||||
|
||||
|
||||
def foo() -> None:
|
||||
import e
|
||||
import i
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import g
|
||||
import j
|
||||
|
||||
import h; import i
|
||||
import k; import l
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import j; \
|
||||
import k
|
||||
import m; \
|
||||
import n
|
||||
|
||||
@@ -43,3 +43,6 @@ regex = '''
|
||||
''' # noqa
|
||||
|
||||
regex = '\\\_'
|
||||
|
||||
#: W605:1:7
|
||||
u'foo\ bar'
|
||||
|
||||
@@ -1,40 +1,54 @@
|
||||
# Same as `W605_0.py` but using f-strings instead.
|
||||
|
||||
#: W605:1:10
|
||||
regex = '\.png$'
|
||||
regex = f'\.png$'
|
||||
|
||||
#: W605:2:1
|
||||
regex = '''
|
||||
regex = f'''
|
||||
\.png$
|
||||
'''
|
||||
|
||||
#: W605:2:6
|
||||
f(
|
||||
'\_'
|
||||
f'\_'
|
||||
)
|
||||
|
||||
#: W605:4:6
|
||||
"""
|
||||
f"""
|
||||
multi-line
|
||||
literal
|
||||
with \_ somewhere
|
||||
in the middle
|
||||
"""
|
||||
|
||||
#: W605:1:38
|
||||
value = f'new line\nand invalid escape \_ here'
|
||||
|
||||
def f():
|
||||
#: W605:1:11
|
||||
return'\.png$'
|
||||
|
||||
#: Okay
|
||||
regex = r'\.png$'
|
||||
regex = '\\.png$'
|
||||
regex = r'''
|
||||
regex = fr'\.png$'
|
||||
regex = f'\\.png$'
|
||||
regex = fr'''
|
||||
\.png$
|
||||
'''
|
||||
regex = r'''
|
||||
regex = fr'''
|
||||
\\.png$
|
||||
'''
|
||||
s = '\\'
|
||||
regex = '\w' # noqa
|
||||
regex = '''
|
||||
s = f'\\'
|
||||
regex = f'\w' # noqa
|
||||
regex = f'''
|
||||
\w
|
||||
''' # noqa
|
||||
|
||||
regex = f'\\\_'
|
||||
value = f'\{{1}}'
|
||||
value = f'\{1}'
|
||||
value = f'{1:\}'
|
||||
value = f"{f"\{1}"}"
|
||||
value = rf"{f"\{1}"}"
|
||||
|
||||
# Okay
|
||||
value = rf'\{{1}}'
|
||||
value = rf'\{1}'
|
||||
value = rf'{1:\}'
|
||||
value = f"{rf"\{1}"}"
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
# Same as `W605_0.py` but using f-strings instead.
|
||||
|
||||
#: W605:1:10
|
||||
regex = f'\.png$'
|
||||
|
||||
#: W605:2:1
|
||||
regex = f'''
|
||||
\.png$
|
||||
'''
|
||||
|
||||
#: W605:2:6
|
||||
f(
|
||||
f'\_'
|
||||
)
|
||||
|
||||
#: W605:4:6
|
||||
f"""
|
||||
multi-line
|
||||
literal
|
||||
with \_ somewhere
|
||||
in the middle
|
||||
"""
|
||||
|
||||
#: W605:1:38
|
||||
value = f'new line\nand invalid escape \_ here'
|
||||
|
||||
|
||||
#: Okay
|
||||
regex = fr'\.png$'
|
||||
regex = f'\\.png$'
|
||||
regex = fr'''
|
||||
\.png$
|
||||
'''
|
||||
regex = fr'''
|
||||
\\.png$
|
||||
'''
|
||||
s = f'\\'
|
||||
regex = f'\w' # noqa
|
||||
regex = f'''
|
||||
\w
|
||||
''' # noqa
|
||||
|
||||
regex = f'\\\_'
|
||||
value = f'\{{1}}'
|
||||
value = f'\{1}'
|
||||
value = f'{1:\}'
|
||||
value = f"{f"\{1}"}"
|
||||
value = rf"{f"\{1}"}"
|
||||
|
||||
# Okay
|
||||
value = rf'\{{1}}'
|
||||
value = rf'\{1}'
|
||||
value = rf'{1:\}'
|
||||
value = f"{rf"\{1}"}"
|
||||
16
crates/ruff_linter/resources/test/fixtures/pydocstyle/D208.py
vendored
Normal file
16
crates/ruff_linter/resources/test/fixtures/pydocstyle/D208.py
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
Author
|
||||
"""
|
||||
|
||||
|
||||
class Platform:
|
||||
""" Remove sampler
|
||||
Args:
|
||||
Returns:
|
||||
"""
|
||||
|
||||
|
||||
def memory_test():
|
||||
"""
|
||||
参数含义:precision:精确到小数点后几位
|
||||
"""
|
||||
@@ -91,3 +91,12 @@ def f(rounds: list[int], number: int) -> bool:
|
||||
bool: was the round played?
|
||||
"""
|
||||
return number in rounds
|
||||
|
||||
|
||||
def f():
|
||||
"""
|
||||
My example
|
||||
==========
|
||||
|
||||
My example explanation
|
||||
"""
|
||||
|
||||
19
crates/ruff_linter/resources/test/fixtures/pylint/no_method_decorator.py
vendored
Normal file
19
crates/ruff_linter/resources/test/fixtures/pylint/no_method_decorator.py
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
from random import choice
|
||||
|
||||
class Fruit:
|
||||
COLORS = []
|
||||
|
||||
def __init__(self, color):
|
||||
self.color = color
|
||||
|
||||
def pick_colors(cls, *args): # [no-classmethod-decorator]
|
||||
"""classmethod to pick fruit colors"""
|
||||
cls.COLORS = args
|
||||
|
||||
pick_colors = classmethod(pick_colors)
|
||||
|
||||
def pick_one_color(): # [no-staticmethod-decorator]
|
||||
"""staticmethod to pick one fruit color"""
|
||||
return choice(Fruit.COLORS)
|
||||
|
||||
pick_one_color = staticmethod(pick_one_color)
|
||||
@@ -3,6 +3,11 @@ import subprocess
|
||||
# Errors.
|
||||
subprocess.run("ls")
|
||||
subprocess.run("ls", shell=True)
|
||||
subprocess.run(
|
||||
["ls"],
|
||||
shell=False,
|
||||
)
|
||||
subprocess.run(["ls"], **kwargs)
|
||||
|
||||
# Non-errors.
|
||||
subprocess.run("ls", check=True)
|
||||
|
||||
@@ -32,3 +32,16 @@ def f(x, y, z, *, u, v, w): # Too many arguments (6/5)
|
||||
|
||||
def f(x, y, z, a, b, c, *, u, v, w): # Too many arguments (9/5)
|
||||
pass
|
||||
|
||||
|
||||
from typing import override, overload
|
||||
|
||||
|
||||
@override
|
||||
def f(x, y, z, a, b, c, *, u, v, w): # OK
|
||||
pass
|
||||
|
||||
|
||||
@overload
|
||||
def f(x, y, z, a, b, c, *, u, v, w): # OK
|
||||
pass
|
||||
|
||||
30
crates/ruff_linter/resources/test/fixtures/pylint/too_many_positional.py
vendored
Normal file
30
crates/ruff_linter/resources/test/fixtures/pylint/too_many_positional.py
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
def f(x, y, z, t, u, v, w, r): # Too many positional arguments (8/3)
|
||||
pass
|
||||
|
||||
|
||||
def f(x): # OK
|
||||
pass
|
||||
|
||||
|
||||
def f(x, y, z, _t, _u, _v, _w, r): # OK (underscore-prefixed names are ignored
|
||||
pass
|
||||
|
||||
|
||||
def f(x, y, z, *, u=1, v=1, r=1): # OK
|
||||
pass
|
||||
|
||||
|
||||
def f(x=1, y=1, z=1): # OK
|
||||
pass
|
||||
|
||||
|
||||
def f(x, y, z, /, u, v, w): # Too many positional arguments (6/3)
|
||||
pass
|
||||
|
||||
|
||||
def f(x, y, z, *, u, v, w): # OK
|
||||
pass
|
||||
|
||||
|
||||
def f(x, y, z, a, b, c, *, u, v, w): # Too many positional arguments (6/3)
|
||||
pass
|
||||
10
crates/ruff_linter/resources/test/fixtures/pylint/too_many_positional_params.py
vendored
Normal file
10
crates/ruff_linter/resources/test/fixtures/pylint/too_many_positional_params.py
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# Too many positional arguments (7/4) for max_positional=4
|
||||
# OK for dummy_variable_rgx ~ "skip_.*"
|
||||
def f(w, x, y, z, skip_t, skip_u, skip_v):
|
||||
pass
|
||||
|
||||
|
||||
# Too many positional arguments (7/4) for max_args=4
|
||||
# Too many positional arguments (7/3) for dummy_variable_rgx ~ "skip_.*"
|
||||
def f(w, x, y, z, t, u, v):
|
||||
pass
|
||||
40
crates/ruff_linter/resources/test/fixtures/pylint/unnecessary_dict_index_lookup.py
vendored
Normal file
40
crates/ruff_linter/resources/test/fixtures/pylint/unnecessary_dict_index_lookup.py
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
FRUITS = {"apple": 1, "orange": 10, "berry": 22}
|
||||
|
||||
def fix_these():
|
||||
[FRUITS[fruit_name] for fruit_name, fruit_count in FRUITS.items()] # PLR1733
|
||||
{FRUITS[fruit_name] for fruit_name, fruit_count in FRUITS.items()} # PLR1733
|
||||
{fruit_name: FRUITS[fruit_name] for fruit_name, fruit_count in FRUITS.items()} # PLR1733
|
||||
|
||||
for fruit_name, fruit_count in FRUITS.items():
|
||||
print(FRUITS[fruit_name]) # PLR1733
|
||||
blah = FRUITS[fruit_name] # PLR1733
|
||||
assert FRUITS[fruit_name] == "pear" # PLR1733
|
||||
|
||||
|
||||
def dont_fix_these():
|
||||
# once there is an assignment to the dict[index], we stop emitting diagnostics
|
||||
for fruit_name, fruit_count in FRUITS.items():
|
||||
FRUITS[fruit_name] = 0 # OK
|
||||
assert FRUITS[fruit_name] == 0 # OK
|
||||
|
||||
# once there is an assignment to the key, we stop emitting diagnostics
|
||||
for fruit_name, fruit_count in FRUITS.items():
|
||||
fruit_name = 0 # OK
|
||||
assert FRUITS[fruit_name] == 0 # OK
|
||||
|
||||
# once there is an assignment to the value, we stop emitting diagnostics
|
||||
for fruit_name, fruit_count in FRUITS.items():
|
||||
if fruit_count < 5:
|
||||
fruit_count = -fruit_count
|
||||
assert FRUITS[fruit_name] == 0 # OK
|
||||
|
||||
|
||||
def value_intentionally_unused():
|
||||
[FRUITS[fruit_name] for fruit_name, _ in FRUITS.items()] # OK
|
||||
{FRUITS[fruit_name] for fruit_name, _ in FRUITS.items()} # OK
|
||||
{fruit_name: FRUITS[fruit_name] for fruit_name, _ in FRUITS.items()} # OK
|
||||
|
||||
for fruit_name, _ in FRUITS.items():
|
||||
print(FRUITS[fruit_name]) # OK
|
||||
blah = FRUITS[fruit_name] # OK
|
||||
assert FRUITS[fruit_name] == "pear" # OK
|
||||
64
crates/ruff_linter/resources/test/fixtures/pylint/unnecessary_list_index_lookup.py
vendored
Normal file
64
crates/ruff_linter/resources/test/fixtures/pylint/unnecessary_list_index_lookup.py
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
import builtins
|
||||
|
||||
letters = ["a", "b", "c"]
|
||||
|
||||
|
||||
def fix_these():
|
||||
[letters[index] for index, letter in enumerate(letters)] # PLR1736
|
||||
{letters[index] for index, letter in enumerate(letters)} # PLR1736
|
||||
{letter: letters[index] for index, letter in enumerate(letters)} # PLR1736
|
||||
|
||||
for index, letter in enumerate(letters):
|
||||
print(letters[index]) # PLR1736
|
||||
blah = letters[index] # PLR1736
|
||||
assert letters[index] == "d" # PLR1736
|
||||
|
||||
for index, letter in builtins.enumerate(letters):
|
||||
print(letters[index]) # PLR1736
|
||||
blah = letters[index] # PLR1736
|
||||
assert letters[index] == "d" # PLR1736
|
||||
|
||||
|
||||
def dont_fix_these():
|
||||
# once there is an assignment to the sequence[index], we stop emitting diagnostics
|
||||
for index, letter in enumerate(letters):
|
||||
letters[index] = "d" # OK
|
||||
letters[index] += "e" # OK
|
||||
assert letters[index] == "de" # OK
|
||||
|
||||
# once there is an assignment to the index, we stop emitting diagnostics
|
||||
for index, letter in enumerate(letters):
|
||||
index += 1 # OK
|
||||
print(letters[index]) # OK
|
||||
|
||||
# once there is an assignment to the sequence, we stop emitting diagnostics
|
||||
for index, letter in enumerate(letters):
|
||||
letters = ["d", "e", "f"] # OK
|
||||
print(letters[index]) # OK
|
||||
|
||||
# once there is an assignment to the value, we stop emitting diagnostics
|
||||
for index, letter in enumerate(letters):
|
||||
letter = "d"
|
||||
print(letters[index]) # OK
|
||||
|
||||
# once there is an deletion from or of the sequence or index, we stop emitting diagnostics
|
||||
for index, letter in enumerate(letters):
|
||||
del letters[index] # OK
|
||||
print(letters[index]) # OK
|
||||
for index, letter in enumerate(letters):
|
||||
del letters # OK
|
||||
print(letters[index]) # OK
|
||||
for index, letter in enumerate(letters):
|
||||
del index # OK
|
||||
print(letters[index]) # OK
|
||||
|
||||
|
||||
def value_intentionally_unused():
|
||||
[letters[index] for index, _ in enumerate(letters)] # OK
|
||||
{letters[index] for index, _ in enumerate(letters)} # OK
|
||||
{index: letters[index] for index, _ in enumerate(letters)} # OK
|
||||
|
||||
for index, _ in enumerate(letters):
|
||||
print(letters[index]) # OK
|
||||
blah = letters[index] # OK
|
||||
letters[index] = "d" # OK
|
||||
@@ -42,3 +42,30 @@ tempfile.SpooledTemporaryFile(0, "w", encoding="utf-8")
|
||||
tempfile.SpooledTemporaryFile(0, "w", -1, "utf-8")
|
||||
tempfile.SpooledTemporaryFile(0, "wb")
|
||||
tempfile.SpooledTemporaryFile(0, )
|
||||
|
||||
open("test.txt",)
|
||||
open()
|
||||
open(
|
||||
"test.txt", # comment
|
||||
)
|
||||
open(
|
||||
"test.txt",
|
||||
# comment
|
||||
)
|
||||
open(("test.txt"),)
|
||||
open(
|
||||
("test.txt"), # comment
|
||||
)
|
||||
open(
|
||||
("test.txt"),
|
||||
# comment
|
||||
)
|
||||
|
||||
open((("test.txt")),)
|
||||
open(
|
||||
(("test.txt")), # comment
|
||||
)
|
||||
open(
|
||||
(("test.txt")),
|
||||
# comment
|
||||
)
|
||||
|
||||
@@ -110,3 +110,10 @@ print('Hello %(arg)s' % bar['bop'])
|
||||
"%s" % (
|
||||
x, # comment
|
||||
)
|
||||
|
||||
|
||||
path = "%s-%s-%s.pem" % (
|
||||
safe_domain_name(cn), # common name, which should be filename safe because it is IDNA-encoded, but in case of a malformed cert make sure it's ok to use as a filename
|
||||
cert.not_valid_after.date().isoformat().replace("-", ""), # expiration date
|
||||
hexlify(cert.fingerprint(hashes.SHA256())).decode("ascii")[0:8], # fingerprint prefix
|
||||
)
|
||||
|
||||
@@ -5,3 +5,11 @@ A = 3.14 * r ** 2 # FURB152
|
||||
C = 6.28 * r # FURB152
|
||||
|
||||
e = 2.71 # FURB152
|
||||
|
||||
r = 3.15 # OK
|
||||
|
||||
r = 3.141 # FURB152
|
||||
|
||||
r = 3.1415 # FURB152
|
||||
|
||||
e = 2.7 # OK
|
||||
|
||||
52
crates/ruff_linter/resources/test/fixtures/refurb/FURB163.py
vendored
Normal file
52
crates/ruff_linter/resources/test/fixtures/refurb/FURB163.py
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
import math
|
||||
|
||||
from math import e as special_e
|
||||
from math import log as special_log
|
||||
|
||||
# Errors.
|
||||
math.log(1, 2)
|
||||
math.log(1, 10)
|
||||
math.log(1, math.e)
|
||||
foo = ...
|
||||
math.log(foo, 2)
|
||||
math.log(foo, 10)
|
||||
math.log(foo, math.e)
|
||||
math.log(1, special_e)
|
||||
special_log(1, 2)
|
||||
special_log(1, 10)
|
||||
special_log(1, math.e)
|
||||
special_log(1, special_e)
|
||||
math.log(1, 2.0)
|
||||
math.log(1, 10.0)
|
||||
|
||||
# Ok.
|
||||
math.log2(1)
|
||||
math.log10(1)
|
||||
math.log(1)
|
||||
math.log(1, 3)
|
||||
math.log(1, math.pi)
|
||||
|
||||
two = 2
|
||||
math.log(1, two)
|
||||
|
||||
ten = 10
|
||||
math.log(1, ten)
|
||||
|
||||
e = math.e
|
||||
math.log(1, e)
|
||||
|
||||
math.log2(1, 10) # math.log2 takes only one argument.
|
||||
math.log10(1, 2) # math.log10 takes only one argument.
|
||||
|
||||
math.log(1, base=2) # math.log does not accept keyword arguments.
|
||||
|
||||
def log(*args):
|
||||
print(f"Logging: {args}")
|
||||
|
||||
|
||||
log(1, 2)
|
||||
log(1, 10)
|
||||
log(1, math.e)
|
||||
|
||||
math.log(1, 2.0001)
|
||||
math.log(1, 10.0001)
|
||||
57
crates/ruff_linter/resources/test/fixtures/refurb/FURB181.py
vendored
Normal file
57
crates/ruff_linter/resources/test/fixtures/refurb/FURB181.py
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
import hashlib
|
||||
from hashlib import (
|
||||
blake2b,
|
||||
blake2s,
|
||||
md5,
|
||||
sha1,
|
||||
sha3_224,
|
||||
sha3_256,
|
||||
sha3_384,
|
||||
sha3_512,
|
||||
sha224,
|
||||
)
|
||||
from hashlib import sha256
|
||||
from hashlib import sha256 as hash_algo
|
||||
from hashlib import sha384, sha512, shake_128, shake_256
|
||||
|
||||
# these will match
|
||||
|
||||
blake2b().digest().hex()
|
||||
blake2s().digest().hex()
|
||||
md5().digest().hex()
|
||||
sha1().digest().hex()
|
||||
sha224().digest().hex()
|
||||
sha256().digest().hex()
|
||||
sha384().digest().hex()
|
||||
sha3_224().digest().hex()
|
||||
sha3_256().digest().hex()
|
||||
sha3_384().digest().hex()
|
||||
sha3_512().digest().hex()
|
||||
sha512().digest().hex()
|
||||
shake_128().digest(10).hex()
|
||||
shake_256().digest(10).hex()
|
||||
|
||||
hashlib.sha256().digest().hex()
|
||||
|
||||
sha256(b"text").digest().hex()
|
||||
|
||||
hash_algo().digest().hex()
|
||||
|
||||
# not yet supported
|
||||
h = sha256()
|
||||
h.digest().hex()
|
||||
|
||||
|
||||
# these will not
|
||||
|
||||
sha256().digest()
|
||||
sha256().digest().hex("_")
|
||||
sha256().digest().hex(bytes_per_sep=4)
|
||||
sha256().hexdigest()
|
||||
|
||||
class Hash:
|
||||
def digest(self) -> bytes:
|
||||
return b""
|
||||
|
||||
|
||||
Hash().digest().hex()
|
||||
@@ -63,11 +63,29 @@ def f():
|
||||
tasks = [asyncio.create_task(task) for task in tasks]
|
||||
|
||||
|
||||
# OK (false negative)
|
||||
# Error
|
||||
def f():
|
||||
task = asyncio.create_task(coordinator.ws_connect())
|
||||
|
||||
|
||||
# Error
|
||||
def f():
|
||||
loop = asyncio.get_running_loop()
|
||||
task: asyncio.Task = loop.create_task(coordinator.ws_connect())
|
||||
|
||||
|
||||
# OK (potential false negative)
|
||||
def f():
|
||||
task = asyncio.create_task(coordinator.ws_connect())
|
||||
background_tasks.add(task)
|
||||
|
||||
|
||||
# OK
|
||||
async def f():
|
||||
task = asyncio.create_task(coordinator.ws_connect())
|
||||
await task
|
||||
|
||||
|
||||
# OK (potential false negative)
|
||||
def f():
|
||||
do_nothing_with_the_task(asyncio.create_task(coordinator.ws_connect()))
|
||||
@@ -88,3 +106,19 @@ def f():
|
||||
def f():
|
||||
loop = asyncio.get_running_loop()
|
||||
loop.do_thing(coordinator.ws_connect())
|
||||
|
||||
|
||||
# OK
|
||||
async def f():
|
||||
task = unused = asyncio.create_task(coordinator.ws_connect())
|
||||
await task
|
||||
|
||||
|
||||
# OK (false negative)
|
||||
async def f():
|
||||
task = unused = asyncio.create_task(coordinator.ws_connect())
|
||||
|
||||
|
||||
# OK
|
||||
async def f():
|
||||
task[i] = asyncio.create_task(coordinator.ws_connect())
|
||||
|
||||
@@ -23,3 +23,6 @@ print(a) # noqa: E501, F821 # comment
|
||||
print(a) # noqa: E501, F821 # comment
|
||||
print(a) # noqa: E501, F821 comment
|
||||
print(a) # noqa: E501, F821 comment
|
||||
|
||||
print(a) # comment with unicode µ # noqa: E501
|
||||
print(a) # comment with unicode µ # noqa: E501, F821
|
||||
|
||||
@@ -3,11 +3,12 @@ use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::codes::Rule;
|
||||
use crate::rules::{flake8_import_conventions, flake8_pyi, pyflakes, pylint};
|
||||
use crate::rules::{flake8_import_conventions, flake8_pyi, pyflakes, pylint, ruff};
|
||||
|
||||
/// Run lint rules over the [`Binding`]s.
|
||||
pub(crate) fn bindings(checker: &mut Checker) {
|
||||
if !checker.any_enabled(&[
|
||||
Rule::AsyncioDanglingTask,
|
||||
Rule::InvalidAllFormat,
|
||||
Rule::InvalidAllObject,
|
||||
Rule::NonAsciiName,
|
||||
@@ -71,5 +72,12 @@ pub(crate) fn bindings(checker: &mut Checker) {
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::AsyncioDanglingTask) {
|
||||
if let Some(diagnostic) =
|
||||
ruff::rules::asyncio_dangling_binding(binding, &checker.semantic)
|
||||
{
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
|
||||
flake8_type_checking::helpers::is_valid_runtime_import(
|
||||
binding,
|
||||
&checker.semantic,
|
||||
&checker.settings.flake8_type_checking,
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
|
||||
@@ -205,19 +205,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
ExprContext::Store => {
|
||||
if checker.enabled(Rule::NonLowercaseVariableInFunction) {
|
||||
if checker.semantic.current_scope().kind.is_function() {
|
||||
// Ignore globals.
|
||||
if !checker
|
||||
.semantic
|
||||
.current_scope()
|
||||
.get(id)
|
||||
.is_some_and(|binding_id| {
|
||||
checker.semantic.binding(binding_id).is_global()
|
||||
})
|
||||
{
|
||||
pep8_naming::rules::non_lowercase_variable_in_function(
|
||||
checker, expr, id,
|
||||
);
|
||||
}
|
||||
pep8_naming::rules::non_lowercase_variable_in_function(
|
||||
checker, expr, id,
|
||||
);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::MixedCaseVariableInClassScope) {
|
||||
@@ -366,6 +356,8 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
Rule::FString,
|
||||
// flynt
|
||||
Rule::StaticJoinToFString,
|
||||
// refurb
|
||||
Rule::HashlibDigestHex,
|
||||
]) {
|
||||
if let Expr::Attribute(ast::ExprAttribute { value, attr, .. }) = func.as_ref() {
|
||||
let attr = attr.as_str();
|
||||
@@ -380,13 +372,13 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
flynt::rules::static_join_to_fstring(
|
||||
checker,
|
||||
expr,
|
||||
string_value.as_str(),
|
||||
string_value.to_str(),
|
||||
);
|
||||
}
|
||||
} else if attr == "format" {
|
||||
// "...".format(...) call
|
||||
let location = expr.range();
|
||||
match pyflakes::format::FormatSummary::try_from(string_value.as_str()) {
|
||||
match pyflakes::format::FormatSummary::try_from(string_value.to_str()) {
|
||||
Err(e) => {
|
||||
if checker.enabled(Rule::StringDotFormatInvalidFormat) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
@@ -432,7 +424,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::BadStringFormatCharacter) {
|
||||
pylint::rules::bad_string_format_character::call(
|
||||
checker,
|
||||
string_value.as_str(),
|
||||
string_value.to_str(),
|
||||
location,
|
||||
);
|
||||
}
|
||||
@@ -553,7 +545,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
flake8_bugbear::rules::no_explicit_stacklevel(checker, call);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryDictKwargs) {
|
||||
flake8_pie::rules::unnecessary_dict_kwargs(checker, expr, keywords);
|
||||
flake8_pie::rules::unnecessary_dict_kwargs(checker, call);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryRangeStart) {
|
||||
flake8_pie::rules::unnecessary_range_start(checker, call);
|
||||
@@ -591,6 +583,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::HashlibInsecureHashFunction) {
|
||||
flake8_bandit::rules::hashlib_insecure_hash_functions(checker, call);
|
||||
}
|
||||
if checker.enabled(Rule::HashlibDigestHex) {
|
||||
refurb::rules::hashlib_digest_hex(checker, call);
|
||||
}
|
||||
if checker.enabled(Rule::RequestWithoutTimeout) {
|
||||
flake8_bandit::rules::request_without_timeout(checker, call);
|
||||
}
|
||||
@@ -928,6 +923,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::PrintEmptyString) {
|
||||
refurb::rules::print_empty_string(checker, call);
|
||||
}
|
||||
if checker.enabled(Rule::RedundantLogBase) {
|
||||
refurb::rules::redundant_log_base(checker, call);
|
||||
}
|
||||
if checker.enabled(Rule::QuadraticListSummation) {
|
||||
ruff::rules::quadratic_list_summation(checker, call);
|
||||
}
|
||||
@@ -1042,7 +1040,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
Rule::PercentFormatUnsupportedFormatCharacter,
|
||||
]) {
|
||||
let location = expr.range();
|
||||
match pyflakes::cformat::CFormatSummary::try_from(value.as_str()) {
|
||||
match pyflakes::cformat::CFormatSummary::try_from(value.to_str()) {
|
||||
Err(CFormatError {
|
||||
typ: CFormatErrorType::UnsupportedFormatChar(c),
|
||||
..
|
||||
@@ -1277,32 +1275,12 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
refurb::rules::math_constant(checker, number_literal);
|
||||
}
|
||||
}
|
||||
Expr::BytesLiteral(_) => {
|
||||
if checker.source_type.is_stub() && checker.enabled(Rule::StringOrBytesTooLong) {
|
||||
flake8_pyi::rules::string_or_bytes_too_long(checker, expr);
|
||||
}
|
||||
}
|
||||
Expr::StringLiteral(string) => {
|
||||
if checker.enabled(Rule::HardcodedBindAllInterfaces) {
|
||||
if let Some(diagnostic) =
|
||||
flake8_bandit::rules::hardcoded_bind_all_interfaces(string)
|
||||
{
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::HardcodedTempFile) {
|
||||
flake8_bandit::rules::hardcoded_tmp_directory(checker, string);
|
||||
}
|
||||
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
|
||||
if checker.enabled(Rule::UnicodeKindPrefix) {
|
||||
for string_part in string.value.parts() {
|
||||
for string_part in value {
|
||||
pyupgrade::rules::unicode_kind_prefix(checker, string_part);
|
||||
}
|
||||
}
|
||||
if checker.source_type.is_stub() {
|
||||
if checker.enabled(Rule::StringOrBytesTooLong) {
|
||||
flake8_pyi::rules::string_or_bytes_too_long(checker, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
Expr::IfExp(
|
||||
if_exp @ ast::ExprIfExp {
|
||||
@@ -1337,6 +1315,12 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
range: _,
|
||||
},
|
||||
) => {
|
||||
if checker.enabled(Rule::UnnecessaryListIndexLookup) {
|
||||
pylint::rules::unnecessary_list_index_lookup_comprehension(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryDictIndexLookup) {
|
||||
pylint::rules::unnecessary_dict_index_lookup_comprehension(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryComprehension) {
|
||||
flake8_comprehensions::rules::unnecessary_list_set_comprehension(
|
||||
checker, expr, elt, generators,
|
||||
@@ -1361,6 +1345,12 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
range: _,
|
||||
},
|
||||
) => {
|
||||
if checker.enabled(Rule::UnnecessaryListIndexLookup) {
|
||||
pylint::rules::unnecessary_list_index_lookup_comprehension(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryDictIndexLookup) {
|
||||
pylint::rules::unnecessary_dict_index_lookup_comprehension(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryComprehension) {
|
||||
flake8_comprehensions::rules::unnecessary_list_set_comprehension(
|
||||
checker, expr, elt, generators,
|
||||
@@ -1384,6 +1374,12 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
generators,
|
||||
range: _,
|
||||
}) => {
|
||||
if checker.enabled(Rule::UnnecessaryListIndexLookup) {
|
||||
pylint::rules::unnecessary_list_index_lookup_comprehension(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryDictIndexLookup) {
|
||||
pylint::rules::unnecessary_dict_index_lookup_comprehension(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryComprehension) {
|
||||
flake8_comprehensions::rules::unnecessary_dict_comprehension(
|
||||
checker, expr, key, value, generators,
|
||||
@@ -1408,6 +1404,12 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
range: _,
|
||||
},
|
||||
) => {
|
||||
if checker.enabled(Rule::UnnecessaryListIndexLookup) {
|
||||
pylint::rules::unnecessary_list_index_lookup_comprehension(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryDictIndexLookup) {
|
||||
pylint::rules::unnecessary_dict_index_lookup_comprehension(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::FunctionUsesLoopVariable) {
|
||||
flake8_bugbear::rules::function_uses_loop_variable(checker, &Node::Expr(expr));
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ pub(super) use module::module;
|
||||
pub(super) use parameter::parameter;
|
||||
pub(super) use parameters::parameters;
|
||||
pub(super) use statement::statement;
|
||||
pub(super) use string_like::string_like;
|
||||
pub(super) use suite::suite;
|
||||
pub(super) use unresolved_references::unresolved_references;
|
||||
|
||||
@@ -25,5 +26,6 @@ mod module;
|
||||
mod parameter;
|
||||
mod parameters;
|
||||
mod statement;
|
||||
mod string_like;
|
||||
mod suite;
|
||||
mod unresolved_references;
|
||||
|
||||
@@ -248,7 +248,10 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
pylint::rules::property_with_parameters(checker, stmt, decorator_list, parameters);
|
||||
}
|
||||
if checker.enabled(Rule::TooManyArguments) {
|
||||
pylint::rules::too_many_arguments(checker, parameters, stmt);
|
||||
pylint::rules::too_many_arguments(checker, function_def);
|
||||
}
|
||||
if checker.enabled(Rule::TooManyPositional) {
|
||||
pylint::rules::too_many_positional(checker, function_def);
|
||||
}
|
||||
if checker.enabled(Rule::TooManyReturnStatements) {
|
||||
if let Some(diagnostic) = pylint::rules::too_many_return_statements(
|
||||
@@ -384,6 +387,12 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
range: _,
|
||||
},
|
||||
) => {
|
||||
if checker.enabled(Rule::NoClassmethodDecorator) {
|
||||
pylint::rules::no_classmethod_decorator(checker, class_def);
|
||||
}
|
||||
if checker.enabled(Rule::NoStaticmethodDecorator) {
|
||||
pylint::rules::no_staticmethod_decorator(checker, class_def);
|
||||
}
|
||||
if checker.enabled(Rule::DjangoNullableModelStringField) {
|
||||
flake8_django::rules::nullable_model_string_field(checker, body);
|
||||
}
|
||||
@@ -1271,6 +1280,12 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::UnnecessaryListCast) {
|
||||
perflint::rules::unnecessary_list_cast(checker, iter, body);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryListIndexLookup) {
|
||||
pylint::rules::unnecessary_list_index_lookup(checker, for_stmt);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryDictIndexLookup) {
|
||||
pylint::rules::unnecessary_dict_index_lookup(checker, for_stmt);
|
||||
}
|
||||
if !is_async {
|
||||
if checker.enabled(Rule::ReimplementedBuiltin) {
|
||||
flake8_simplify::rules::convert_for_loop_to_any_all(checker, stmt);
|
||||
@@ -1522,6 +1537,14 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
}
|
||||
Stmt::TypeAlias(ast::StmtTypeAlias { name, .. }) => {
|
||||
if checker.enabled(Rule::SnakeCaseTypeAlias) {
|
||||
flake8_pyi::rules::snake_case_type_alias(checker, name);
|
||||
}
|
||||
if checker.enabled(Rule::TSuffixedTypeAlias) {
|
||||
flake8_pyi::rules::t_suffixed_type_alias(checker, name);
|
||||
}
|
||||
}
|
||||
Stmt::Delete(delete @ ast::StmtDelete { targets, range: _ }) => {
|
||||
if checker.enabled(Rule::GlobalStatement) {
|
||||
for target in targets {
|
||||
@@ -1548,7 +1571,11 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
pylint::rules::named_expr_without_context(checker, value);
|
||||
}
|
||||
if checker.enabled(Rule::AsyncioDanglingTask) {
|
||||
ruff::rules::asyncio_dangling_task(checker, value);
|
||||
if let Some(diagnostic) =
|
||||
ruff::rules::asyncio_dangling_task(value, checker.semantic())
|
||||
{
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::RepeatedAppend) {
|
||||
refurb::rules::repeated_append(checker, stmt);
|
||||
|
||||
20
crates/ruff_linter/src/checkers/ast/analyze/string_like.rs
Normal file
20
crates/ruff_linter/src/checkers/ast/analyze/string_like.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use ruff_python_ast::StringLike;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::codes::Rule;
|
||||
use crate::rules::{flake8_bandit, flake8_pyi};
|
||||
|
||||
/// Run lint rules over a [`StringLike`] syntax nodes.
|
||||
pub(crate) fn string_like(string_like: StringLike, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::HardcodedBindAllInterfaces) {
|
||||
flake8_bandit::rules::hardcoded_bind_all_interfaces(checker, string_like);
|
||||
}
|
||||
if checker.enabled(Rule::HardcodedTempFile) {
|
||||
flake8_bandit::rules::hardcoded_tmp_directory(checker, string_like);
|
||||
}
|
||||
if checker.source_type.is_stub() {
|
||||
if checker.enabled(Rule::StringOrBytesTooLong) {
|
||||
flake8_pyi::rules::string_or_bytes_too_long(checker, string_like);
|
||||
}
|
||||
}
|
||||
}
|
||||
66
crates/ruff_linter/src/checkers/ast/annotation.rs
Normal file
66
crates/ruff_linter/src/checkers/ast/annotation.rs
Normal file
@@ -0,0 +1,66 @@
|
||||
use ruff_python_semantic::{ScopeKind, SemanticModel};
|
||||
|
||||
use crate::rules::flake8_type_checking;
|
||||
use crate::settings::LinterSettings;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(super) enum AnnotationContext {
|
||||
/// Python will evaluate the annotation at runtime, but it's not _required_ and, as such, could
|
||||
/// be quoted to convert it into a typing-only annotation.
|
||||
///
|
||||
/// For example:
|
||||
/// ```python
|
||||
/// from pandas import DataFrame
|
||||
///
|
||||
/// def foo() -> DataFrame:
|
||||
/// ...
|
||||
/// ```
|
||||
///
|
||||
/// Above, Python will evaluate `DataFrame` at runtime in order to add it to `__annotations__`.
|
||||
RuntimeEvaluated,
|
||||
/// Python will evaluate the annotation at runtime, and it's required to be available at
|
||||
/// runtime, as a library (like Pydantic) needs access to it.
|
||||
RuntimeRequired,
|
||||
/// The annotation is only evaluated at type-checking time.
|
||||
TypingOnly,
|
||||
}
|
||||
|
||||
impl AnnotationContext {
|
||||
pub(super) fn from_model(semantic: &SemanticModel, settings: &LinterSettings) -> Self {
|
||||
// If the annotation is in a class scope (e.g., an annotated assignment for a
|
||||
// class field), and that class is marked as annotation as runtime-required.
|
||||
if semantic
|
||||
.current_scope()
|
||||
.kind
|
||||
.as_class()
|
||||
.is_some_and(|class_def| {
|
||||
flake8_type_checking::helpers::runtime_required_class(
|
||||
class_def,
|
||||
&settings.flake8_type_checking.runtime_required_base_classes,
|
||||
&settings.flake8_type_checking.runtime_required_decorators,
|
||||
semantic,
|
||||
)
|
||||
})
|
||||
{
|
||||
return Self::RuntimeRequired;
|
||||
}
|
||||
|
||||
// If `__future__` annotations are enabled, then annotations are never evaluated
|
||||
// at runtime, so we can treat them as typing-only.
|
||||
if semantic.future_annotations() {
|
||||
return Self::TypingOnly;
|
||||
}
|
||||
|
||||
// Otherwise, if we're in a class or module scope, then the annotation needs to
|
||||
// be available at runtime.
|
||||
// See: https://docs.python.org/3/reference/simple_stmts.html#annotated-assignment-statements
|
||||
if matches!(
|
||||
semantic.current_scope().kind,
|
||||
ScopeKind::Class(_) | ScopeKind::Module
|
||||
) {
|
||||
return Self::RuntimeEvaluated;
|
||||
}
|
||||
|
||||
Self::TypingOnly
|
||||
}
|
||||
}
|
||||
@@ -44,12 +44,12 @@ use ruff_python_ast::helpers::{
|
||||
};
|
||||
use ruff_python_ast::identifier::Identifier;
|
||||
use ruff_python_ast::str::trailing_quote;
|
||||
use ruff_python_ast::visitor::{walk_except_handler, walk_pattern, Visitor};
|
||||
use ruff_python_ast::visitor::{walk_except_handler, walk_f_string_element, walk_pattern, Visitor};
|
||||
use ruff_python_ast::{helpers, str, visitor, PySourceType};
|
||||
use ruff_python_codegen::{Generator, Quote, Stylist};
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_python_parser::typing::{parse_type_annotation, AnnotationKind};
|
||||
use ruff_python_semantic::analyze::{typing, visibility};
|
||||
use ruff_python_semantic::analyze::{imports, typing, visibility};
|
||||
use ruff_python_semantic::{
|
||||
BindingFlags, BindingId, BindingKind, Exceptions, Export, FromImport, Globals, Import, Module,
|
||||
ModuleKind, NodeId, ScopeId, ScopeKind, SemanticModel, SemanticModelFlags, Snapshot,
|
||||
@@ -58,6 +58,7 @@ use ruff_python_semantic::{
|
||||
use ruff_python_stdlib::builtins::{IPYTHON_BUILTINS, MAGIC_GLOBALS, PYTHON_BUILTINS};
|
||||
use ruff_source_file::Locator;
|
||||
|
||||
use crate::checkers::ast::annotation::AnnotationContext;
|
||||
use crate::checkers::ast::deferred::Deferred;
|
||||
use crate::docstrings::extraction::ExtractionTarget;
|
||||
use crate::importer::Importer;
|
||||
@@ -68,6 +69,7 @@ use crate::settings::{flags, LinterSettings};
|
||||
use crate::{docstrings, noqa};
|
||||
|
||||
mod analyze;
|
||||
mod annotation;
|
||||
mod deferred;
|
||||
|
||||
pub(crate) struct Checker<'a> {
|
||||
@@ -107,6 +109,8 @@ pub(crate) struct Checker<'a> {
|
||||
pub(crate) diagnostics: Vec<Diagnostic>,
|
||||
/// The list of names already seen by flake8-bugbear diagnostics, to avoid duplicate violations..
|
||||
pub(crate) flake8_bugbear_seen: Vec<TextRange>,
|
||||
/// The end offset of the last visited statement.
|
||||
last_stmt_end: TextSize,
|
||||
}
|
||||
|
||||
impl<'a> Checker<'a> {
|
||||
@@ -142,6 +146,7 @@ impl<'a> Checker<'a> {
|
||||
diagnostics: Vec::default(),
|
||||
flake8_bugbear_seen: Vec::default(),
|
||||
cell_offsets,
|
||||
last_stmt_end: TextSize::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -268,6 +273,18 @@ where
|
||||
// Step 0: Pre-processing
|
||||
self.semantic.push_node(stmt);
|
||||
|
||||
// For Jupyter Notebooks, we'll reset the `IMPORT_BOUNDARY` flag when
|
||||
// we encounter a cell boundary.
|
||||
if self.source_type.is_ipynb()
|
||||
&& self.semantic.at_top_level()
|
||||
&& self.semantic.seen_import_boundary()
|
||||
&& self.cell_offsets.is_some_and(|cell_offsets| {
|
||||
cell_offsets.has_cell_boundary(TextRange::new(self.last_stmt_end, stmt.start()))
|
||||
})
|
||||
{
|
||||
self.semantic.flags -= SemanticModelFlags::IMPORT_BOUNDARY;
|
||||
}
|
||||
|
||||
// Track whether we've seen docstrings, non-imports, etc.
|
||||
match stmt {
|
||||
Stmt::ImportFrom(ast::StmtImportFrom { module, names, .. }) => {
|
||||
@@ -288,9 +305,12 @@ where
|
||||
}
|
||||
_ => {
|
||||
self.semantic.flags |= SemanticModelFlags::FUTURES_BOUNDARY;
|
||||
if !self.semantic.seen_import_boundary()
|
||||
&& !helpers::is_assignment_to_a_dunder(stmt)
|
||||
&& !helpers::in_nested_block(self.semantic.current_statements())
|
||||
if !(self.semantic.seen_import_boundary()
|
||||
|| helpers::is_assignment_to_a_dunder(stmt)
|
||||
|| helpers::in_nested_block(self.semantic.current_statements())
|
||||
|| imports::is_matplotlib_activation(stmt, self.semantic())
|
||||
|| self.settings.preview.is_enabled()
|
||||
&& imports::is_sys_path_modification(stmt, self.semantic()))
|
||||
{
|
||||
self.semantic.flags |= SemanticModelFlags::IMPORT_BOUNDARY;
|
||||
}
|
||||
@@ -477,6 +497,13 @@ where
|
||||
// are enabled.
|
||||
let runtime_annotation = !self.semantic.future_annotations();
|
||||
|
||||
// The first parameter may be a single dispatch.
|
||||
let mut singledispatch =
|
||||
flake8_type_checking::helpers::is_singledispatch_implementation(
|
||||
function_def,
|
||||
self.semantic(),
|
||||
);
|
||||
|
||||
self.semantic.push_scope(ScopeKind::Type);
|
||||
|
||||
if let Some(type_params) = type_params {
|
||||
@@ -490,8 +517,10 @@ where
|
||||
.chain(¶meters.kwonlyargs)
|
||||
{
|
||||
if let Some(expr) = ¶meter_with_default.parameter.annotation {
|
||||
if runtime_annotation {
|
||||
self.visit_runtime_annotation(expr);
|
||||
if singledispatch {
|
||||
self.visit_runtime_required_annotation(expr);
|
||||
} else if runtime_annotation {
|
||||
self.visit_runtime_evaluated_annotation(expr);
|
||||
} else {
|
||||
self.visit_annotation(expr);
|
||||
};
|
||||
@@ -499,11 +528,12 @@ where
|
||||
if let Some(expr) = ¶meter_with_default.default {
|
||||
self.visit_expr(expr);
|
||||
}
|
||||
singledispatch = false;
|
||||
}
|
||||
if let Some(arg) = ¶meters.vararg {
|
||||
if let Some(expr) = &arg.annotation {
|
||||
if runtime_annotation {
|
||||
self.visit_runtime_annotation(expr);
|
||||
self.visit_runtime_evaluated_annotation(expr);
|
||||
} else {
|
||||
self.visit_annotation(expr);
|
||||
};
|
||||
@@ -512,7 +542,7 @@ where
|
||||
if let Some(arg) = ¶meters.kwarg {
|
||||
if let Some(expr) = &arg.annotation {
|
||||
if runtime_annotation {
|
||||
self.visit_runtime_annotation(expr);
|
||||
self.visit_runtime_evaluated_annotation(expr);
|
||||
} else {
|
||||
self.visit_annotation(expr);
|
||||
};
|
||||
@@ -520,7 +550,7 @@ where
|
||||
}
|
||||
for expr in returns {
|
||||
if runtime_annotation {
|
||||
self.visit_runtime_annotation(expr);
|
||||
self.visit_runtime_evaluated_annotation(expr);
|
||||
} else {
|
||||
self.visit_annotation(expr);
|
||||
};
|
||||
@@ -651,39 +681,16 @@ where
|
||||
value,
|
||||
..
|
||||
}) => {
|
||||
// If we're in a class or module scope, then the annotation needs to be
|
||||
// available at runtime.
|
||||
// See: https://docs.python.org/3/reference/simple_stmts.html#annotated-assignment-statements
|
||||
let runtime_annotation = if self.semantic.future_annotations() {
|
||||
if self.semantic.current_scope().kind.is_class() {
|
||||
let baseclasses = &self
|
||||
.settings
|
||||
.flake8_type_checking
|
||||
.runtime_evaluated_base_classes;
|
||||
let decorators = &self
|
||||
.settings
|
||||
.flake8_type_checking
|
||||
.runtime_evaluated_decorators;
|
||||
flake8_type_checking::helpers::runtime_evaluated(
|
||||
baseclasses,
|
||||
decorators,
|
||||
&self.semantic,
|
||||
)
|
||||
} else {
|
||||
false
|
||||
match AnnotationContext::from_model(&self.semantic, self.settings) {
|
||||
AnnotationContext::RuntimeRequired => {
|
||||
self.visit_runtime_required_annotation(annotation);
|
||||
}
|
||||
} else {
|
||||
matches!(
|
||||
self.semantic.current_scope().kind,
|
||||
ScopeKind::Class(_) | ScopeKind::Module
|
||||
)
|
||||
};
|
||||
|
||||
if runtime_annotation {
|
||||
self.visit_runtime_annotation(annotation);
|
||||
} else {
|
||||
self.visit_annotation(annotation);
|
||||
AnnotationContext::RuntimeEvaluated => {
|
||||
self.visit_runtime_evaluated_annotation(annotation);
|
||||
}
|
||||
AnnotationContext::TypingOnly => self.visit_annotation(annotation),
|
||||
}
|
||||
|
||||
if let Some(expr) = value {
|
||||
if self.semantic.match_typing_expr(annotation, "TypeAlias") {
|
||||
self.visit_type_definition(expr);
|
||||
@@ -779,6 +786,7 @@ where
|
||||
|
||||
self.semantic.flags = flags_snapshot;
|
||||
self.semantic.pop_node();
|
||||
self.last_stmt_end = stmt.end();
|
||||
}
|
||||
|
||||
fn visit_annotation(&mut self, expr: &'b Expr) {
|
||||
@@ -790,8 +798,7 @@ where
|
||||
|
||||
fn visit_expr(&mut self, expr: &'b Expr) {
|
||||
// Step 0: Pre-processing
|
||||
if !self.semantic.in_f_string()
|
||||
&& !self.semantic.in_literal()
|
||||
if !self.semantic.in_typing_literal()
|
||||
&& !self.semantic.in_deferred_type_definition()
|
||||
&& self.semantic.in_type_definition()
|
||||
&& self.semantic.future_annotations()
|
||||
@@ -799,7 +806,7 @@ where
|
||||
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = expr {
|
||||
self.deferred.string_type_definitions.push((
|
||||
expr.range(),
|
||||
value.as_str(),
|
||||
value.to_str(),
|
||||
self.semantic.snapshot(),
|
||||
));
|
||||
} else {
|
||||
@@ -1173,7 +1180,7 @@ where
|
||||
) {
|
||||
// Ex) Literal["Class"]
|
||||
Some(typing::SubscriptKind::Literal) => {
|
||||
self.semantic.flags |= SemanticModelFlags::LITERAL;
|
||||
self.semantic.flags |= SemanticModelFlags::TYPING_LITERAL;
|
||||
|
||||
self.visit_expr(slice);
|
||||
self.visit_expr_context(ctx);
|
||||
@@ -1213,13 +1220,10 @@ where
|
||||
}
|
||||
}
|
||||
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
|
||||
if self.semantic.in_type_definition()
|
||||
&& !self.semantic.in_literal()
|
||||
&& !self.semantic.in_f_string()
|
||||
{
|
||||
if self.semantic.in_type_definition() && !self.semantic.in_typing_literal() {
|
||||
self.deferred.string_type_definitions.push((
|
||||
expr.range(),
|
||||
value.as_str(),
|
||||
value.to_str(),
|
||||
self.semantic.snapshot(),
|
||||
));
|
||||
}
|
||||
@@ -1246,6 +1250,13 @@ where
|
||||
|
||||
// Step 4: Analysis
|
||||
analyze::expression(expr, self);
|
||||
match expr {
|
||||
Expr::StringLiteral(string_literal) => {
|
||||
analyze::string_like(string_literal.into(), self);
|
||||
}
|
||||
Expr::BytesLiteral(bytes_literal) => analyze::string_like(bytes_literal.into(), self),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
self.semantic.flags = flags_snapshot;
|
||||
self.semantic.pop_node();
|
||||
@@ -1301,17 +1312,6 @@ where
|
||||
self.semantic.flags = flags_snapshot;
|
||||
}
|
||||
|
||||
fn visit_format_spec(&mut self, format_spec: &'b Expr) {
|
||||
match format_spec {
|
||||
Expr::FString(ast::ExprFString { value, .. }) => {
|
||||
for expr in value.elements() {
|
||||
self.visit_expr(expr);
|
||||
}
|
||||
}
|
||||
_ => unreachable!("Unexpected expression for format_spec"),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_parameters(&mut self, parameters: &'b Parameters) {
|
||||
// Step 1: Binding.
|
||||
// Bind, but intentionally avoid walking default expressions, as we handle them
|
||||
@@ -1421,6 +1421,16 @@ where
|
||||
.push((bound, self.semantic.snapshot()));
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_f_string_element(&mut self, f_string_element: &'b ast::FStringElement) {
|
||||
// Step 2: Traversal
|
||||
walk_f_string_element(self, f_string_element);
|
||||
|
||||
// Step 4: Analysis
|
||||
if let Some(literal) = f_string_element.as_literal() {
|
||||
analyze::string_like(literal.into(), self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Checker<'a> {
|
||||
@@ -1497,10 +1507,18 @@ impl<'a> Checker<'a> {
|
||||
self.semantic.flags = snapshot;
|
||||
}
|
||||
|
||||
/// Visit an [`Expr`], and treat it as a runtime-required type annotation.
|
||||
fn visit_runtime_annotation(&mut self, expr: &'a Expr) {
|
||||
/// Visit an [`Expr`], and treat it as a runtime-evaluated type annotation.
|
||||
fn visit_runtime_evaluated_annotation(&mut self, expr: &'a Expr) {
|
||||
let snapshot = self.semantic.flags;
|
||||
self.semantic.flags |= SemanticModelFlags::RUNTIME_ANNOTATION;
|
||||
self.semantic.flags |= SemanticModelFlags::RUNTIME_EVALUATED_ANNOTATION;
|
||||
self.visit_type_definition(expr);
|
||||
self.semantic.flags = snapshot;
|
||||
}
|
||||
|
||||
/// Visit an [`Expr`], and treat it as a runtime-required type annotation.
|
||||
fn visit_runtime_required_annotation(&mut self, expr: &'a Expr) {
|
||||
let snapshot = self.semantic.flags;
|
||||
self.semantic.flags |= SemanticModelFlags::RUNTIME_REQUIRED_ANNOTATION;
|
||||
self.visit_type_definition(expr);
|
||||
self.semantic.flags = snapshot;
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
use std::path::Path;
|
||||
|
||||
use itertools::Itertools;
|
||||
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
||||
use ruff_text_size::{Ranged, TextLen, TextRange};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix};
|
||||
use ruff_python_trivia::CommentRanges;
|
||||
use ruff_python_trivia::{CommentRanges, PythonWhitespace};
|
||||
use ruff_source_file::Locator;
|
||||
|
||||
use crate::noqa;
|
||||
@@ -200,17 +200,11 @@ fn delete_noqa(range: TextRange, locator: &Locator) -> Edit {
|
||||
|
||||
// Compute the leading space.
|
||||
let prefix = locator.slice(TextRange::new(line_range.start(), range.start()));
|
||||
let leading_space = prefix
|
||||
.rfind(|c: char| !c.is_whitespace())
|
||||
.map_or(prefix.len(), |i| prefix.len() - i - 1);
|
||||
let leading_space_len = TextSize::try_from(leading_space).unwrap();
|
||||
let leading_space_len = prefix.text_len() - prefix.trim_whitespace_end().text_len();
|
||||
|
||||
// Compute the trailing space.
|
||||
let suffix = locator.slice(TextRange::new(range.end(), line_range.end()));
|
||||
let trailing_space = suffix
|
||||
.find(|c: char| !c.is_whitespace())
|
||||
.map_or(suffix.len(), |i| i);
|
||||
let trailing_space_len = TextSize::try_from(trailing_space).unwrap();
|
||||
let trailing_space_len = suffix.text_len() - suffix.trim_whitespace_start().text_len();
|
||||
|
||||
// Ex) `# noqa`
|
||||
if line_range
|
||||
|
||||
@@ -245,6 +245,8 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Pylint, "E2515") => (RuleGroup::Stable, rules::pylint::rules::InvalidCharacterZeroWidthSpace),
|
||||
(Pylint, "R0124") => (RuleGroup::Stable, rules::pylint::rules::ComparisonWithItself),
|
||||
(Pylint, "R0133") => (RuleGroup::Stable, rules::pylint::rules::ComparisonOfConstant),
|
||||
(Pylint, "R0202") => (RuleGroup::Preview, rules::pylint::rules::NoClassmethodDecorator),
|
||||
(Pylint, "R0203") => (RuleGroup::Preview, rules::pylint::rules::NoStaticmethodDecorator),
|
||||
(Pylint, "R0206") => (RuleGroup::Stable, rules::pylint::rules::PropertyWithParameters),
|
||||
(Pylint, "R0402") => (RuleGroup::Stable, rules::pylint::rules::ManualFromImport),
|
||||
(Pylint, "R0911") => (RuleGroup::Stable, rules::pylint::rules::TooManyReturnStatements),
|
||||
@@ -252,12 +254,15 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Pylint, "R0913") => (RuleGroup::Stable, rules::pylint::rules::TooManyArguments),
|
||||
(Pylint, "R0915") => (RuleGroup::Stable, rules::pylint::rules::TooManyStatements),
|
||||
(Pylint, "R0916") => (RuleGroup::Preview, rules::pylint::rules::TooManyBooleanExpressions),
|
||||
(Pylint, "R0917") => (RuleGroup::Preview, rules::pylint::rules::TooManyPositional),
|
||||
(Pylint, "R1701") => (RuleGroup::Stable, rules::pylint::rules::RepeatedIsinstanceCalls),
|
||||
(Pylint, "R1704") => (RuleGroup::Preview, rules::pylint::rules::RedefinedArgumentFromLocal),
|
||||
(Pylint, "R1711") => (RuleGroup::Stable, rules::pylint::rules::UselessReturn),
|
||||
(Pylint, "R1714") => (RuleGroup::Stable, rules::pylint::rules::RepeatedEqualityComparison),
|
||||
(Pylint, "R1706") => (RuleGroup::Preview, rules::pylint::rules::AndOrTernary),
|
||||
(Pylint, "R1722") => (RuleGroup::Stable, rules::pylint::rules::SysExitAlias),
|
||||
(Pylint, "R1733") => (RuleGroup::Preview, rules::pylint::rules::UnnecessaryDictIndexLookup),
|
||||
(Pylint, "R1736") => (RuleGroup::Preview, rules::pylint::rules::UnnecessaryListIndexLookup),
|
||||
(Pylint, "R2004") => (RuleGroup::Stable, rules::pylint::rules::MagicValueComparison),
|
||||
(Pylint, "R5501") => (RuleGroup::Stable, rules::pylint::rules::CollapsibleElseIf),
|
||||
(Pylint, "R6201") => (RuleGroup::Preview, rules::pylint::rules::LiteralMembership),
|
||||
@@ -955,10 +960,12 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Refurb, "145") => (RuleGroup::Preview, rules::refurb::rules::SliceCopy),
|
||||
(Refurb, "148") => (RuleGroup::Preview, rules::refurb::rules::UnnecessaryEnumerate),
|
||||
(Refurb, "152") => (RuleGroup::Preview, rules::refurb::rules::MathConstant),
|
||||
(Refurb, "163") => (RuleGroup::Preview, rules::refurb::rules::RedundantLogBase),
|
||||
(Refurb, "168") => (RuleGroup::Preview, rules::refurb::rules::IsinstanceTypeNone),
|
||||
(Refurb, "169") => (RuleGroup::Preview, rules::refurb::rules::TypeNoneComparison),
|
||||
(Refurb, "171") => (RuleGroup::Preview, rules::refurb::rules::SingleItemMembershipTest),
|
||||
(Refurb, "177") => (RuleGroup::Preview, rules::refurb::rules::ImplicitCwd),
|
||||
(Refurb, "181") => (RuleGroup::Preview, rules::refurb::rules::HashlibDigestHex),
|
||||
|
||||
// flake8-logging
|
||||
(Flake8Logging, "001") => (RuleGroup::Preview, rules::flake8_logging::rules::DirectLoggerInstantiation),
|
||||
|
||||
@@ -3,12 +3,14 @@
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
use ruff_diagnostics::Edit;
|
||||
use ruff_python_ast::AnyNodeRef;
|
||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
||||
use ruff_python_ast::{self as ast, Arguments, ExceptHandler, Stmt};
|
||||
use ruff_python_ast::{AnyNodeRef, ArgOrKeyword};
|
||||
use ruff_python_codegen::Stylist;
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_python_trivia::{
|
||||
has_leading_content, is_python_whitespace, PythonWhitespace, SimpleTokenKind, SimpleTokenizer,
|
||||
has_leading_content, is_python_whitespace, CommentRanges, PythonWhitespace, SimpleTokenKind,
|
||||
SimpleTokenizer,
|
||||
};
|
||||
use ruff_source_file::{Locator, NewlineWithTrailingNewline, UniversalNewlines};
|
||||
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
||||
@@ -138,6 +140,32 @@ pub(crate) fn remove_argument<T: Ranged>(
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic function to add arguments or keyword arguments to function calls.
|
||||
pub(crate) fn add_argument(
|
||||
argument: &str,
|
||||
arguments: &Arguments,
|
||||
comment_ranges: &CommentRanges,
|
||||
source: &str,
|
||||
) -> Edit {
|
||||
if let Some(last) = arguments.arguments_source_order().last() {
|
||||
// Case 1: existing arguments, so append after the last argument.
|
||||
let last = parenthesized_range(
|
||||
match last {
|
||||
ArgOrKeyword::Arg(arg) => arg.into(),
|
||||
ArgOrKeyword::Keyword(keyword) => (&keyword.value).into(),
|
||||
},
|
||||
arguments.into(),
|
||||
comment_ranges,
|
||||
source,
|
||||
)
|
||||
.unwrap_or(last.range());
|
||||
Edit::insertion(format!(", {argument}"), last.end())
|
||||
} else {
|
||||
// Case 2: no arguments. Add argument, without any trailing comma.
|
||||
Edit::insertion(argument.to_string(), arguments.start() + TextSize::from(1))
|
||||
}
|
||||
}
|
||||
|
||||
/// Determine if a vector contains only one, specific element.
|
||||
fn is_only<T: PartialEq>(vec: &[T], value: &T) -> bool {
|
||||
vec.len() == 1 && vec[0] == *value
|
||||
|
||||
@@ -17,6 +17,7 @@ use ruff_diagnostics::{Diagnostic, DiagnosticKind, Fix};
|
||||
use ruff_notebook::NotebookIndex;
|
||||
use ruff_source_file::{SourceFile, SourceLocation};
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
pub use sarif::SarifEmitter;
|
||||
pub use text::TextEmitter;
|
||||
|
||||
mod azure;
|
||||
@@ -28,6 +29,7 @@ mod json;
|
||||
mod json_lines;
|
||||
mod junit;
|
||||
mod pylint;
|
||||
mod sarif;
|
||||
mod text;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
|
||||
212
crates/ruff_linter/src/message/sarif.rs
Normal file
212
crates/ruff_linter/src/message/sarif.rs
Normal file
@@ -0,0 +1,212 @@
|
||||
use std::io::Write;
|
||||
|
||||
use anyhow::Result;
|
||||
use serde::{Serialize, Serializer};
|
||||
use serde_json::json;
|
||||
|
||||
use ruff_source_file::OneIndexed;
|
||||
|
||||
use crate::codes::Rule;
|
||||
use crate::fs::normalize_path;
|
||||
use crate::message::{Emitter, EmitterContext, Message};
|
||||
use crate::registry::{AsRule, Linter, RuleNamespace};
|
||||
use crate::VERSION;
|
||||
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
pub struct SarifEmitter;
|
||||
|
||||
impl Emitter for SarifEmitter {
|
||||
fn emit(
|
||||
&mut self,
|
||||
writer: &mut dyn Write,
|
||||
messages: &[Message],
|
||||
_context: &EmitterContext,
|
||||
) -> Result<()> {
|
||||
let results = messages
|
||||
.iter()
|
||||
.map(SarifResult::from_message)
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
let output = json!({
|
||||
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
|
||||
"version": "2.1.0",
|
||||
"runs": [{
|
||||
"tool": {
|
||||
"driver": {
|
||||
"name": "ruff",
|
||||
"informationUri": "https://github.com/astral-sh/ruff",
|
||||
"rules": Rule::iter().map(SarifRule::from).collect::<Vec<_>>(),
|
||||
"version": VERSION.to_string(),
|
||||
}
|
||||
},
|
||||
"results": results,
|
||||
}],
|
||||
});
|
||||
serde_json::to_writer_pretty(writer, &output)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct SarifRule<'a> {
|
||||
name: &'a str,
|
||||
code: String,
|
||||
linter: &'a str,
|
||||
summary: &'a str,
|
||||
explanation: Option<&'a str>,
|
||||
url: Option<String>,
|
||||
}
|
||||
|
||||
impl From<Rule> for SarifRule<'_> {
|
||||
fn from(rule: Rule) -> Self {
|
||||
let code = rule.noqa_code().to_string();
|
||||
let (linter, _) = Linter::parse_code(&code).unwrap();
|
||||
Self {
|
||||
name: rule.into(),
|
||||
code,
|
||||
linter: linter.name(),
|
||||
summary: rule.message_formats()[0],
|
||||
explanation: rule.explanation(),
|
||||
url: rule.url(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for SarifRule<'_> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
json!({
|
||||
"id": self.code,
|
||||
"shortDescription": {
|
||||
"text": self.summary,
|
||||
},
|
||||
"fullDescription": {
|
||||
"text": self.explanation,
|
||||
},
|
||||
"help": {
|
||||
"text": self.summary,
|
||||
},
|
||||
"helpUri": self.url,
|
||||
"properties": {
|
||||
"id": self.code,
|
||||
"kind": self.linter,
|
||||
"name": self.name,
|
||||
"problem.severity": "error".to_string(),
|
||||
},
|
||||
})
|
||||
.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SarifResult {
|
||||
rule: Rule,
|
||||
level: String,
|
||||
message: String,
|
||||
uri: String,
|
||||
start_line: OneIndexed,
|
||||
start_column: OneIndexed,
|
||||
end_line: OneIndexed,
|
||||
end_column: OneIndexed,
|
||||
}
|
||||
|
||||
impl SarifResult {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn from_message(message: &Message) -> Result<Self> {
|
||||
let start_location = message.compute_start_location();
|
||||
let end_location = message.compute_end_location();
|
||||
let path = normalize_path(message.filename());
|
||||
Ok(Self {
|
||||
rule: message.kind.rule(),
|
||||
level: "error".to_string(),
|
||||
message: message.kind.name.clone(),
|
||||
uri: url::Url::from_file_path(&path)
|
||||
.map_err(|()| anyhow::anyhow!("Failed to convert path to URL: {}", path.display()))?
|
||||
.to_string(),
|
||||
start_line: start_location.row,
|
||||
start_column: start_location.column,
|
||||
end_line: end_location.row,
|
||||
end_column: end_location.column,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
fn from_message(message: &Message) -> Result<Self> {
|
||||
let start_location = message.compute_start_location();
|
||||
let end_location = message.compute_end_location();
|
||||
let path = normalize_path(message.filename());
|
||||
Ok(Self {
|
||||
rule: message.kind.rule(),
|
||||
level: "error".to_string(),
|
||||
message: message.kind.name.clone(),
|
||||
uri: path.display().to_string(),
|
||||
start_line: start_location.row,
|
||||
start_column: start_location.column,
|
||||
end_line: end_location.row,
|
||||
end_column: end_location.column,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for SarifResult {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
json!({
|
||||
"level": self.level,
|
||||
"message": {
|
||||
"text": self.message,
|
||||
},
|
||||
"locations": [{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": self.uri,
|
||||
},
|
||||
"region": {
|
||||
"startLine": self.start_line,
|
||||
"startColumn": self.start_column,
|
||||
"endLine": self.end_line,
|
||||
"endColumn": self.end_column,
|
||||
}
|
||||
}
|
||||
}],
|
||||
"ruleId": self.rule.noqa_code().to_string(),
|
||||
})
|
||||
.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use crate::message::tests::{capture_emitter_output, create_messages};
|
||||
use crate::message::SarifEmitter;
|
||||
|
||||
fn get_output() -> String {
|
||||
let mut emitter = SarifEmitter {};
|
||||
capture_emitter_output(&mut emitter, &create_messages())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn valid_json() {
|
||||
let content = get_output();
|
||||
serde_json::from_str::<serde_json::Value>(&content).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_results() {
|
||||
let content = get_output();
|
||||
let sarif = serde_json::from_str::<serde_json::Value>(content.as_str()).unwrap();
|
||||
let rules = sarif["runs"][0]["tool"]["driver"]["rules"]
|
||||
.as_array()
|
||||
.unwrap();
|
||||
let results = sarif["runs"][0]["results"].as_array().unwrap();
|
||||
assert_eq!(results.len(), 3);
|
||||
assert!(rules.len() > 3);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,18 @@
|
||||
use itertools::Itertools;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use ruff_python_ast::helpers::{pep_604_union, ReturnStatementVisitor};
|
||||
use ruff_diagnostics::Edit;
|
||||
use ruff_python_ast::helpers::{
|
||||
implicit_return, pep_604_union, typing_optional, typing_union, ReturnStatementVisitor,
|
||||
};
|
||||
use ruff_python_ast::visitor::Visitor;
|
||||
use ruff_python_ast::{self as ast, Expr, ExprContext};
|
||||
use ruff_python_semantic::analyze::type_inference::{NumberLike, PythonType, ResolvedPythonType};
|
||||
use ruff_python_semantic::analyze::visibility;
|
||||
use ruff_python_semantic::{Definition, SemanticModel};
|
||||
use ruff_text_size::TextRange;
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
|
||||
use crate::importer::{ImportRequest, Importer};
|
||||
use crate::settings::types::PythonVersion;
|
||||
|
||||
/// Return the name of the function, if it's overloaded.
|
||||
@@ -38,22 +43,24 @@ pub(crate) fn is_overload_impl(
|
||||
}
|
||||
|
||||
/// Given a function, guess its return type.
|
||||
pub(crate) fn auto_return_type(
|
||||
function: &ast::StmtFunctionDef,
|
||||
target_version: PythonVersion,
|
||||
) -> Option<Expr> {
|
||||
pub(crate) fn auto_return_type(function: &ast::StmtFunctionDef) -> Option<AutoPythonType> {
|
||||
// Collect all the `return` statements.
|
||||
let returns = {
|
||||
let mut visitor = ReturnStatementVisitor::default();
|
||||
visitor.visit_body(&function.body);
|
||||
|
||||
// Ignore generators.
|
||||
if visitor.is_generator {
|
||||
return None;
|
||||
}
|
||||
|
||||
visitor.returns
|
||||
};
|
||||
|
||||
// Determine the return type of the first `return` statement.
|
||||
let (return_statement, returns) = returns.split_first()?;
|
||||
let Some((return_statement, returns)) = returns.split_first() else {
|
||||
return Some(AutoPythonType::Atom(PythonType::None));
|
||||
};
|
||||
let mut return_type = return_statement.value.as_deref().map_or(
|
||||
ResolvedPythonType::Atom(PythonType::None),
|
||||
ResolvedPythonType::from,
|
||||
@@ -67,25 +74,105 @@ pub(crate) fn auto_return_type(
|
||||
));
|
||||
}
|
||||
|
||||
match return_type {
|
||||
ResolvedPythonType::Atom(python_type) => type_expr(python_type),
|
||||
ResolvedPythonType::Union(python_types) if target_version >= PythonVersion::Py310 => {
|
||||
// Aggregate all the individual types (e.g., `int`, `float`).
|
||||
let names = python_types
|
||||
.iter()
|
||||
.sorted_unstable()
|
||||
.filter_map(|python_type| type_expr(*python_type))
|
||||
.collect::<Vec<_>>();
|
||||
// If the function has an implicit return, union with `None`, as in:
|
||||
// ```python
|
||||
// def func(x: int):
|
||||
// if x > 0:
|
||||
// return 1
|
||||
// ```
|
||||
if implicit_return(function) {
|
||||
return_type = return_type.union(ResolvedPythonType::Atom(PythonType::None));
|
||||
}
|
||||
|
||||
// Wrap in a bitwise union (e.g., `int | float`).
|
||||
Some(pep_604_union(&names))
|
||||
}
|
||||
ResolvedPythonType::Union(_) => None,
|
||||
match return_type {
|
||||
ResolvedPythonType::Atom(python_type) => Some(AutoPythonType::Atom(python_type)),
|
||||
ResolvedPythonType::Union(python_types) => Some(AutoPythonType::Union(python_types)),
|
||||
ResolvedPythonType::Unknown => None,
|
||||
ResolvedPythonType::TypeError => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum AutoPythonType {
|
||||
Atom(PythonType),
|
||||
Union(FxHashSet<PythonType>),
|
||||
}
|
||||
|
||||
impl AutoPythonType {
|
||||
/// Convert an [`AutoPythonType`] into an [`Expr`].
|
||||
///
|
||||
/// If the [`Expr`] relies on importing any external symbols, those imports will be returned as
|
||||
/// additional edits.
|
||||
pub(crate) fn into_expression(
|
||||
self,
|
||||
importer: &Importer,
|
||||
at: TextSize,
|
||||
semantic: &SemanticModel,
|
||||
target_version: PythonVersion,
|
||||
) -> Option<(Expr, Vec<Edit>)> {
|
||||
match self {
|
||||
AutoPythonType::Atom(python_type) => {
|
||||
let expr = type_expr(python_type)?;
|
||||
Some((expr, vec![]))
|
||||
}
|
||||
AutoPythonType::Union(python_types) => {
|
||||
if target_version >= PythonVersion::Py310 {
|
||||
// Aggregate all the individual types (e.g., `int`, `float`).
|
||||
let names = python_types
|
||||
.iter()
|
||||
.sorted_unstable()
|
||||
.map(|python_type| type_expr(*python_type))
|
||||
.collect::<Option<Vec<_>>>()?;
|
||||
|
||||
// Wrap in a bitwise union (e.g., `int | float`).
|
||||
let expr = pep_604_union(&names);
|
||||
|
||||
Some((expr, vec![]))
|
||||
} else {
|
||||
let python_types = python_types
|
||||
.into_iter()
|
||||
.sorted_unstable()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
match python_types.as_slice() {
|
||||
[python_type, PythonType::None] | [PythonType::None, python_type] => {
|
||||
let element = type_expr(*python_type)?;
|
||||
|
||||
// Ex) `Optional[int]`
|
||||
let (optional_edit, binding) = importer
|
||||
.get_or_import_symbol(
|
||||
&ImportRequest::import_from("typing", "Optional"),
|
||||
at,
|
||||
semantic,
|
||||
)
|
||||
.ok()?;
|
||||
let expr = typing_optional(element, binding);
|
||||
Some((expr, vec![optional_edit]))
|
||||
}
|
||||
_ => {
|
||||
let elements = python_types
|
||||
.into_iter()
|
||||
.map(type_expr)
|
||||
.collect::<Option<Vec<_>>>()?;
|
||||
|
||||
// Ex) `Union[int, str]`
|
||||
let (union_edit, binding) = importer
|
||||
.get_or_import_symbol(
|
||||
&ImportRequest::import_from("typing", "Union"),
|
||||
at,
|
||||
semantic,
|
||||
)
|
||||
.ok()?;
|
||||
let expr = typing_union(&elements, binding);
|
||||
Some((expr, vec![union_edit]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a [`PythonType`], return an [`Expr`] that resolves to that type.
|
||||
fn type_expr(python_type: PythonType) -> Option<Expr> {
|
||||
fn name(name: &str) -> Expr {
|
||||
|
||||
@@ -11,6 +11,7 @@ mod tests {
|
||||
|
||||
use crate::assert_messages;
|
||||
use crate::registry::Rule;
|
||||
use crate::settings::types::PythonVersion;
|
||||
use crate::settings::LinterSettings;
|
||||
use crate::test::test_path;
|
||||
|
||||
@@ -128,6 +129,25 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auto_return_type_py38() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
Path::new("flake8_annotations/auto_return_type.py"),
|
||||
&LinterSettings {
|
||||
target_version: PythonVersion::Py38,
|
||||
..LinterSettings::for_rules(vec![
|
||||
Rule::MissingReturnTypeUndocumentedPublicFunction,
|
||||
Rule::MissingReturnTypePrivateFunction,
|
||||
Rule::MissingReturnTypeSpecialMethod,
|
||||
Rule::MissingReturnTypeStaticMethod,
|
||||
Rule::MissingReturnTypeClassMethod,
|
||||
])
|
||||
},
|
||||
)?;
|
||||
assert_messages!(diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn suppress_none_returning() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
|
||||
@@ -508,7 +508,7 @@ fn check_dynamically_typed<F>(
|
||||
if let Expr::StringLiteral(ast::ExprStringLiteral { range, value }) = annotation {
|
||||
// Quoted annotations
|
||||
if let Ok((parsed_annotation, _)) =
|
||||
parse_type_annotation(value.as_str(), *range, checker.locator().contents())
|
||||
parse_type_annotation(value.to_str(), *range, checker.locator().contents())
|
||||
{
|
||||
if type_hint_resolves_to_any(
|
||||
&parsed_annotation,
|
||||
@@ -537,6 +537,19 @@ fn check_dynamically_typed<F>(
|
||||
}
|
||||
}
|
||||
|
||||
fn is_empty_body(body: &[Stmt]) -> bool {
|
||||
body.iter().all(|stmt| match stmt {
|
||||
Stmt::Pass(_) => true,
|
||||
Stmt::Expr(ast::StmtExpr { value, range: _ }) => {
|
||||
matches!(
|
||||
value.as_ref(),
|
||||
Expr::StringLiteral(_) | Expr::EllipsisLiteral(_)
|
||||
)
|
||||
}
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
|
||||
/// Generate flake8-annotation checks for a given `Definition`.
|
||||
pub(crate) fn definition(
|
||||
checker: &Checker,
|
||||
@@ -725,39 +738,67 @@ pub(crate) fn definition(
|
||||
) {
|
||||
if is_method && visibility::is_classmethod(decorator_list, checker.semantic()) {
|
||||
if checker.enabled(Rule::MissingReturnTypeClassMethod) {
|
||||
let return_type = auto_return_type(function, checker.settings.target_version)
|
||||
.map(|return_type| checker.generator().expr(&return_type));
|
||||
let return_type = if visibility::is_abstract(decorator_list, checker.semantic())
|
||||
&& is_empty_body(body)
|
||||
{
|
||||
None
|
||||
} else {
|
||||
auto_return_type(function)
|
||||
.and_then(|return_type| {
|
||||
return_type.into_expression(
|
||||
checker.importer(),
|
||||
function.parameters.start(),
|
||||
checker.semantic(),
|
||||
checker.settings.target_version,
|
||||
)
|
||||
})
|
||||
.map(|(return_type, edits)| (checker.generator().expr(&return_type), edits))
|
||||
};
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
MissingReturnTypeClassMethod {
|
||||
name: name.to_string(),
|
||||
annotation: return_type.clone(),
|
||||
annotation: return_type.clone().map(|(return_type, ..)| return_type),
|
||||
},
|
||||
function.identifier(),
|
||||
);
|
||||
if let Some(return_type) = return_type {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
|
||||
format!(" -> {return_type}"),
|
||||
function.parameters.range().end(),
|
||||
)));
|
||||
if let Some((return_type, edits)) = return_type {
|
||||
diagnostic.set_fix(Fix::unsafe_edits(
|
||||
Edit::insertion(format!(" -> {return_type}"), function.parameters.end()),
|
||||
edits,
|
||||
));
|
||||
}
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
} else if is_method && visibility::is_staticmethod(decorator_list, checker.semantic()) {
|
||||
if checker.enabled(Rule::MissingReturnTypeStaticMethod) {
|
||||
let return_type = auto_return_type(function, checker.settings.target_version)
|
||||
.map(|return_type| checker.generator().expr(&return_type));
|
||||
let return_type = if visibility::is_abstract(decorator_list, checker.semantic())
|
||||
&& is_empty_body(body)
|
||||
{
|
||||
None
|
||||
} else {
|
||||
auto_return_type(function)
|
||||
.and_then(|return_type| {
|
||||
return_type.into_expression(
|
||||
checker.importer(),
|
||||
function.parameters.start(),
|
||||
checker.semantic(),
|
||||
checker.settings.target_version,
|
||||
)
|
||||
})
|
||||
.map(|(return_type, edits)| (checker.generator().expr(&return_type), edits))
|
||||
};
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
MissingReturnTypeStaticMethod {
|
||||
name: name.to_string(),
|
||||
annotation: return_type.clone(),
|
||||
annotation: return_type.clone().map(|(return_type, ..)| return_type),
|
||||
},
|
||||
function.identifier(),
|
||||
);
|
||||
if let Some(return_type) = return_type {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
|
||||
format!(" -> {return_type}"),
|
||||
function.parameters.range().end(),
|
||||
)));
|
||||
if let Some((return_type, edits)) = return_type {
|
||||
diagnostic.set_fix(Fix::unsafe_edits(
|
||||
Edit::insertion(format!(" -> {return_type}"), function.parameters.end()),
|
||||
edits,
|
||||
));
|
||||
}
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
@@ -775,7 +816,7 @@ pub(crate) fn definition(
|
||||
);
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
|
||||
" -> None".to_string(),
|
||||
function.parameters.range().end(),
|
||||
function.parameters.end(),
|
||||
)));
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
@@ -793,7 +834,7 @@ pub(crate) fn definition(
|
||||
if let Some(return_type) = return_type {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
|
||||
format!(" -> {return_type}"),
|
||||
function.parameters.range().end(),
|
||||
function.parameters.end(),
|
||||
)));
|
||||
}
|
||||
diagnostics.push(diagnostic);
|
||||
@@ -803,20 +844,41 @@ pub(crate) fn definition(
|
||||
visibility::Visibility::Public => {
|
||||
if checker.enabled(Rule::MissingReturnTypeUndocumentedPublicFunction) {
|
||||
let return_type =
|
||||
auto_return_type(function, checker.settings.target_version)
|
||||
.map(|return_type| checker.generator().expr(&return_type));
|
||||
if visibility::is_abstract(decorator_list, checker.semantic())
|
||||
&& is_empty_body(body)
|
||||
{
|
||||
None
|
||||
} else {
|
||||
auto_return_type(function)
|
||||
.and_then(|return_type| {
|
||||
return_type.into_expression(
|
||||
checker.importer(),
|
||||
function.parameters.start(),
|
||||
checker.semantic(),
|
||||
checker.settings.target_version,
|
||||
)
|
||||
})
|
||||
.map(|(return_type, edits)| {
|
||||
(checker.generator().expr(&return_type), edits)
|
||||
})
|
||||
};
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
MissingReturnTypeUndocumentedPublicFunction {
|
||||
name: name.to_string(),
|
||||
annotation: return_type.clone(),
|
||||
annotation: return_type
|
||||
.clone()
|
||||
.map(|(return_type, ..)| return_type),
|
||||
},
|
||||
function.identifier(),
|
||||
);
|
||||
if let Some(return_type) = return_type {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
|
||||
format!(" -> {return_type}"),
|
||||
function.parameters.range().end(),
|
||||
)));
|
||||
if let Some((return_type, edits)) = return_type {
|
||||
diagnostic.set_fix(Fix::unsafe_edits(
|
||||
Edit::insertion(
|
||||
format!(" -> {return_type}"),
|
||||
function.parameters.end(),
|
||||
),
|
||||
edits,
|
||||
));
|
||||
}
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
@@ -824,20 +886,41 @@ pub(crate) fn definition(
|
||||
visibility::Visibility::Private => {
|
||||
if checker.enabled(Rule::MissingReturnTypePrivateFunction) {
|
||||
let return_type =
|
||||
auto_return_type(function, checker.settings.target_version)
|
||||
.map(|return_type| checker.generator().expr(&return_type));
|
||||
if visibility::is_abstract(decorator_list, checker.semantic())
|
||||
&& is_empty_body(body)
|
||||
{
|
||||
None
|
||||
} else {
|
||||
auto_return_type(function)
|
||||
.and_then(|return_type| {
|
||||
return_type.into_expression(
|
||||
checker.importer(),
|
||||
function.parameters.start(),
|
||||
checker.semantic(),
|
||||
checker.settings.target_version,
|
||||
)
|
||||
})
|
||||
.map(|(return_type, edits)| {
|
||||
(checker.generator().expr(&return_type), edits)
|
||||
})
|
||||
};
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
MissingReturnTypePrivateFunction {
|
||||
name: name.to_string(),
|
||||
annotation: return_type.clone(),
|
||||
annotation: return_type
|
||||
.clone()
|
||||
.map(|(return_type, ..)| return_type),
|
||||
},
|
||||
function.identifier(),
|
||||
);
|
||||
if let Some(return_type) = return_type {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::insertion(
|
||||
format!(" -> {return_type}"),
|
||||
function.parameters.range().end(),
|
||||
)));
|
||||
if let Some((return_type, edits)) = return_type {
|
||||
diagnostic.set_fix(Fix::unsafe_edits(
|
||||
Edit::insertion(
|
||||
format!(" -> {return_type}"),
|
||||
function.parameters.end(),
|
||||
),
|
||||
edits,
|
||||
));
|
||||
}
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -123,5 +123,376 @@ auto_return_type.py:31:5: ANN201 [*] Missing return type annotation for public f
|
||||
31 |-def func(x: int):
|
||||
31 |+def func(x: int) -> str | float:
|
||||
32 32 | return 1 + 2.5 if x > 0 else 1.5 or "str"
|
||||
33 33 |
|
||||
34 34 |
|
||||
|
||||
auto_return_type.py:35:5: ANN201 Missing return type annotation for public function `func`
|
||||
|
|
||||
35 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
36 | if not x:
|
||||
37 | return None
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
auto_return_type.py:41:5: ANN201 Missing return type annotation for public function `func`
|
||||
|
|
||||
41 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
42 | return {"foo": 1}
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
auto_return_type.py:45:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
45 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
46 | if not x:
|
||||
47 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
42 42 | return {"foo": 1}
|
||||
43 43 |
|
||||
44 44 |
|
||||
45 |-def func(x: int):
|
||||
45 |+def func(x: int) -> int:
|
||||
46 46 | if not x:
|
||||
47 47 | return 1
|
||||
48 48 | else:
|
||||
|
||||
auto_return_type.py:52:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
52 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
53 | if not x:
|
||||
54 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `int | None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
49 49 | return True
|
||||
50 50 |
|
||||
51 51 |
|
||||
52 |-def func(x: int):
|
||||
52 |+def func(x: int) -> int | None:
|
||||
53 53 | if not x:
|
||||
54 54 | return 1
|
||||
55 55 | else:
|
||||
|
||||
auto_return_type.py:59:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
59 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
60 | if not x:
|
||||
61 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `str | int | None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
56 56 | return None
|
||||
57 57 |
|
||||
58 58 |
|
||||
59 |-def func(x: int):
|
||||
59 |+def func(x: int) -> str | int | None:
|
||||
60 60 | if not x:
|
||||
61 61 | return 1
|
||||
62 62 | elif x > 5:
|
||||
|
||||
auto_return_type.py:68:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
68 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
69 | if x:
|
||||
70 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `int | None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
65 65 | return None
|
||||
66 66 |
|
||||
67 67 |
|
||||
68 |-def func(x: int):
|
||||
68 |+def func(x: int) -> int | None:
|
||||
69 69 | if x:
|
||||
70 70 | return 1
|
||||
71 71 |
|
||||
|
||||
auto_return_type.py:73:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
73 | def func():
|
||||
| ^^^^ ANN201
|
||||
74 | x = 1
|
||||
|
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
70 70 | return 1
|
||||
71 71 |
|
||||
72 72 |
|
||||
73 |-def func():
|
||||
73 |+def func() -> None:
|
||||
74 74 | x = 1
|
||||
75 75 |
|
||||
76 76 |
|
||||
|
||||
auto_return_type.py:77:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
77 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
78 | if x > 0:
|
||||
79 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `int | None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
74 74 | x = 1
|
||||
75 75 |
|
||||
76 76 |
|
||||
77 |-def func(x: int):
|
||||
77 |+def func(x: int) -> int | None:
|
||||
78 78 | if x > 0:
|
||||
79 79 | return 1
|
||||
80 80 |
|
||||
|
||||
auto_return_type.py:82:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
82 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
83 | match x:
|
||||
84 | case [1, 2, 3]:
|
||||
|
|
||||
= help: Add return type annotation: `str | int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
79 79 | return 1
|
||||
80 80 |
|
||||
81 81 |
|
||||
82 |-def func(x: int):
|
||||
82 |+def func(x: int) -> str | int:
|
||||
83 83 | match x:
|
||||
84 84 | case [1, 2, 3]:
|
||||
85 85 | return 1
|
||||
|
||||
auto_return_type.py:90:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
90 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
91 | for i in range(5):
|
||||
92 | if i > 0:
|
||||
|
|
||||
= help: Add return type annotation: `int | None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
87 87 | return "foo"
|
||||
88 88 |
|
||||
89 89 |
|
||||
90 |-def func(x: int):
|
||||
90 |+def func(x: int) -> int | None:
|
||||
91 91 | for i in range(5):
|
||||
92 92 | if i > 0:
|
||||
93 93 | return 1
|
||||
|
||||
auto_return_type.py:96:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
96 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
97 | for i in range(5):
|
||||
98 | if i > 0:
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
93 93 | return 1
|
||||
94 94 |
|
||||
95 95 |
|
||||
96 |-def func(x: int):
|
||||
96 |+def func(x: int) -> int:
|
||||
97 97 | for i in range(5):
|
||||
98 98 | if i > 0:
|
||||
99 99 | return 1
|
||||
|
||||
auto_return_type.py:104:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
104 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
105 | for i in range(5):
|
||||
106 | if i > 0:
|
||||
|
|
||||
= help: Add return type annotation: `int | None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
101 101 | return 4
|
||||
102 102 |
|
||||
103 103 |
|
||||
104 |-def func(x: int):
|
||||
104 |+def func(x: int) -> int | None:
|
||||
105 105 | for i in range(5):
|
||||
106 106 | if i > 0:
|
||||
107 107 | break
|
||||
|
||||
auto_return_type.py:112:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
112 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
113 | try:
|
||||
114 | pass
|
||||
|
|
||||
= help: Add return type annotation: `int | None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
109 109 | return 4
|
||||
110 110 |
|
||||
111 111 |
|
||||
112 |-def func(x: int):
|
||||
112 |+def func(x: int) -> int | None:
|
||||
113 113 | try:
|
||||
114 114 | pass
|
||||
115 115 | except:
|
||||
|
||||
auto_return_type.py:119:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
119 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
120 | try:
|
||||
121 | pass
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
116 116 | return 1
|
||||
117 117 |
|
||||
118 118 |
|
||||
119 |-def func(x: int):
|
||||
119 |+def func(x: int) -> int:
|
||||
120 120 | try:
|
||||
121 121 | pass
|
||||
122 122 | except:
|
||||
|
||||
auto_return_type.py:128:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
128 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
129 | try:
|
||||
130 | pass
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
125 125 | return 2
|
||||
126 126 |
|
||||
127 127 |
|
||||
128 |-def func(x: int):
|
||||
128 |+def func(x: int) -> int:
|
||||
129 129 | try:
|
||||
130 130 | pass
|
||||
131 131 | except:
|
||||
|
||||
auto_return_type.py:137:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
137 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
138 | try:
|
||||
139 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
134 134 | return 2
|
||||
135 135 |
|
||||
136 136 |
|
||||
137 |-def func(x: int):
|
||||
137 |+def func(x: int) -> int:
|
||||
138 138 | try:
|
||||
139 139 | return 1
|
||||
140 140 | except:
|
||||
|
||||
auto_return_type.py:146:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
146 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
147 | while x > 0:
|
||||
148 | break
|
||||
|
|
||||
= help: Add return type annotation: `int | None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
143 143 | pass
|
||||
144 144 |
|
||||
145 145 |
|
||||
146 |-def func(x: int):
|
||||
146 |+def func(x: int) -> int | None:
|
||||
147 147 | while x > 0:
|
||||
148 148 | break
|
||||
149 149 | return 1
|
||||
|
||||
auto_return_type.py:158:9: ANN201 Missing return type annotation for public function `method`
|
||||
|
|
||||
156 | class Foo(abc.ABC):
|
||||
157 | @abstractmethod
|
||||
158 | def method(self):
|
||||
| ^^^^^^ ANN201
|
||||
159 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
auto_return_type.py:162:9: ANN201 Missing return type annotation for public function `method`
|
||||
|
|
||||
161 | @abc.abstractmethod
|
||||
162 | def method(self):
|
||||
| ^^^^^^ ANN201
|
||||
163 | """Docstring."""
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
auto_return_type.py:166:9: ANN201 Missing return type annotation for public function `method`
|
||||
|
|
||||
165 | @abc.abstractmethod
|
||||
166 | def method(self):
|
||||
| ^^^^^^ ANN201
|
||||
167 | ...
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
auto_return_type.py:171:9: ANN205 Missing return type annotation for staticmethod `method`
|
||||
|
|
||||
169 | @staticmethod
|
||||
170 | @abstractmethod
|
||||
171 | def method():
|
||||
| ^^^^^^ ANN205
|
||||
172 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
auto_return_type.py:176:9: ANN206 Missing return type annotation for classmethod `method`
|
||||
|
|
||||
174 | @classmethod
|
||||
175 | @abstractmethod
|
||||
176 | def method(cls):
|
||||
| ^^^^^^ ANN206
|
||||
177 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
auto_return_type.py:180:9: ANN201 [*] Missing return type annotation for public function `method`
|
||||
|
|
||||
179 | @abstractmethod
|
||||
180 | def method(self):
|
||||
| ^^^^^^ ANN201
|
||||
181 | if self.x > 0:
|
||||
182 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `float`
|
||||
|
||||
ℹ Unsafe fix
|
||||
177 177 | pass
|
||||
178 178 |
|
||||
179 179 | @abstractmethod
|
||||
180 |- def method(self):
|
||||
180 |+ def method(self) -> float:
|
||||
181 181 | if self.x > 0:
|
||||
182 182 | return 1
|
||||
183 183 | else:
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,553 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_annotations/mod.rs
|
||||
---
|
||||
auto_return_type.py:1:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
1 | def func():
|
||||
| ^^^^ ANN201
|
||||
2 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |-def func():
|
||||
1 |+def func() -> int:
|
||||
2 2 | return 1
|
||||
3 3 |
|
||||
4 4 |
|
||||
|
||||
auto_return_type.py:5:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
5 | def func():
|
||||
| ^^^^ ANN201
|
||||
6 | return 1.5
|
||||
|
|
||||
= help: Add return type annotation: `float`
|
||||
|
||||
ℹ Unsafe fix
|
||||
2 2 | return 1
|
||||
3 3 |
|
||||
4 4 |
|
||||
5 |-def func():
|
||||
5 |+def func() -> float:
|
||||
6 6 | return 1.5
|
||||
7 7 |
|
||||
8 8 |
|
||||
|
||||
auto_return_type.py:9:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
9 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
10 | if x > 0:
|
||||
11 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `float`
|
||||
|
||||
ℹ Unsafe fix
|
||||
6 6 | return 1.5
|
||||
7 7 |
|
||||
8 8 |
|
||||
9 |-def func(x: int):
|
||||
9 |+def func(x: int) -> float:
|
||||
10 10 | if x > 0:
|
||||
11 11 | return 1
|
||||
12 12 | else:
|
||||
|
||||
auto_return_type.py:16:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
16 | def func():
|
||||
| ^^^^ ANN201
|
||||
17 | return True
|
||||
|
|
||||
= help: Add return type annotation: `bool`
|
||||
|
||||
ℹ Unsafe fix
|
||||
13 13 | return 1.5
|
||||
14 14 |
|
||||
15 15 |
|
||||
16 |-def func():
|
||||
16 |+def func() -> bool:
|
||||
17 17 | return True
|
||||
18 18 |
|
||||
19 19 |
|
||||
|
||||
auto_return_type.py:20:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
20 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
21 | if x > 0:
|
||||
22 | return None
|
||||
|
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
17 17 | return True
|
||||
18 18 |
|
||||
19 19 |
|
||||
20 |-def func(x: int):
|
||||
20 |+def func(x: int) -> None:
|
||||
21 21 | if x > 0:
|
||||
22 22 | return None
|
||||
23 23 | else:
|
||||
|
||||
auto_return_type.py:27:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
27 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
28 | return 1 or 2.5 if x > 0 else 1.5 or "str"
|
||||
|
|
||||
= help: Add return type annotation: `Union[str | float]`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from typing import Union
|
||||
1 2 | def func():
|
||||
2 3 | return 1
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
24 25 | return
|
||||
25 26 |
|
||||
26 27 |
|
||||
27 |-def func(x: int):
|
||||
28 |+def func(x: int) -> Union[str | float]:
|
||||
28 29 | return 1 or 2.5 if x > 0 else 1.5 or "str"
|
||||
29 30 |
|
||||
30 31 |
|
||||
|
||||
auto_return_type.py:31:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
31 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
32 | return 1 + 2.5 if x > 0 else 1.5 or "str"
|
||||
|
|
||||
= help: Add return type annotation: `Union[str | float]`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from typing import Union
|
||||
1 2 | def func():
|
||||
2 3 | return 1
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
28 29 | return 1 or 2.5 if x > 0 else 1.5 or "str"
|
||||
29 30 |
|
||||
30 31 |
|
||||
31 |-def func(x: int):
|
||||
32 |+def func(x: int) -> Union[str | float]:
|
||||
32 33 | return 1 + 2.5 if x > 0 else 1.5 or "str"
|
||||
33 34 |
|
||||
34 35 |
|
||||
|
||||
auto_return_type.py:35:5: ANN201 Missing return type annotation for public function `func`
|
||||
|
|
||||
35 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
36 | if not x:
|
||||
37 | return None
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
auto_return_type.py:41:5: ANN201 Missing return type annotation for public function `func`
|
||||
|
|
||||
41 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
42 | return {"foo": 1}
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
auto_return_type.py:45:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
45 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
46 | if not x:
|
||||
47 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
42 42 | return {"foo": 1}
|
||||
43 43 |
|
||||
44 44 |
|
||||
45 |-def func(x: int):
|
||||
45 |+def func(x: int) -> int:
|
||||
46 46 | if not x:
|
||||
47 47 | return 1
|
||||
48 48 | else:
|
||||
|
||||
auto_return_type.py:52:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
52 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
53 | if not x:
|
||||
54 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `Optional[int]`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from typing import Optional
|
||||
1 2 | def func():
|
||||
2 3 | return 1
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
49 50 | return True
|
||||
50 51 |
|
||||
51 52 |
|
||||
52 |-def func(x: int):
|
||||
53 |+def func(x: int) -> Optional[int]:
|
||||
53 54 | if not x:
|
||||
54 55 | return 1
|
||||
55 56 | else:
|
||||
|
||||
auto_return_type.py:59:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
59 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
60 | if not x:
|
||||
61 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `Union[str | int | None]`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from typing import Union
|
||||
1 2 | def func():
|
||||
2 3 | return 1
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
56 57 | return None
|
||||
57 58 |
|
||||
58 59 |
|
||||
59 |-def func(x: int):
|
||||
60 |+def func(x: int) -> Union[str | int | None]:
|
||||
60 61 | if not x:
|
||||
61 62 | return 1
|
||||
62 63 | elif x > 5:
|
||||
|
||||
auto_return_type.py:68:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
68 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
69 | if x:
|
||||
70 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `Optional[int]`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from typing import Optional
|
||||
1 2 | def func():
|
||||
2 3 | return 1
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
65 66 | return None
|
||||
66 67 |
|
||||
67 68 |
|
||||
68 |-def func(x: int):
|
||||
69 |+def func(x: int) -> Optional[int]:
|
||||
69 70 | if x:
|
||||
70 71 | return 1
|
||||
71 72 |
|
||||
|
||||
auto_return_type.py:73:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
73 | def func():
|
||||
| ^^^^ ANN201
|
||||
74 | x = 1
|
||||
|
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
70 70 | return 1
|
||||
71 71 |
|
||||
72 72 |
|
||||
73 |-def func():
|
||||
73 |+def func() -> None:
|
||||
74 74 | x = 1
|
||||
75 75 |
|
||||
76 76 |
|
||||
|
||||
auto_return_type.py:77:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
77 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
78 | if x > 0:
|
||||
79 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `Optional[int]`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from typing import Optional
|
||||
1 2 | def func():
|
||||
2 3 | return 1
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
74 75 | x = 1
|
||||
75 76 |
|
||||
76 77 |
|
||||
77 |-def func(x: int):
|
||||
78 |+def func(x: int) -> Optional[int]:
|
||||
78 79 | if x > 0:
|
||||
79 80 | return 1
|
||||
80 81 |
|
||||
|
||||
auto_return_type.py:82:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
82 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
83 | match x:
|
||||
84 | case [1, 2, 3]:
|
||||
|
|
||||
= help: Add return type annotation: `Union[str | int]`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from typing import Union
|
||||
1 2 | def func():
|
||||
2 3 | return 1
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
79 80 | return 1
|
||||
80 81 |
|
||||
81 82 |
|
||||
82 |-def func(x: int):
|
||||
83 |+def func(x: int) -> Union[str | int]:
|
||||
83 84 | match x:
|
||||
84 85 | case [1, 2, 3]:
|
||||
85 86 | return 1
|
||||
|
||||
auto_return_type.py:90:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
90 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
91 | for i in range(5):
|
||||
92 | if i > 0:
|
||||
|
|
||||
= help: Add return type annotation: `Optional[int]`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from typing import Optional
|
||||
1 2 | def func():
|
||||
2 3 | return 1
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
87 88 | return "foo"
|
||||
88 89 |
|
||||
89 90 |
|
||||
90 |-def func(x: int):
|
||||
91 |+def func(x: int) -> Optional[int]:
|
||||
91 92 | for i in range(5):
|
||||
92 93 | if i > 0:
|
||||
93 94 | return 1
|
||||
|
||||
auto_return_type.py:96:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
96 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
97 | for i in range(5):
|
||||
98 | if i > 0:
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
93 93 | return 1
|
||||
94 94 |
|
||||
95 95 |
|
||||
96 |-def func(x: int):
|
||||
96 |+def func(x: int) -> int:
|
||||
97 97 | for i in range(5):
|
||||
98 98 | if i > 0:
|
||||
99 99 | return 1
|
||||
|
||||
auto_return_type.py:104:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
104 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
105 | for i in range(5):
|
||||
106 | if i > 0:
|
||||
|
|
||||
= help: Add return type annotation: `Optional[int]`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from typing import Optional
|
||||
1 2 | def func():
|
||||
2 3 | return 1
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
101 102 | return 4
|
||||
102 103 |
|
||||
103 104 |
|
||||
104 |-def func(x: int):
|
||||
105 |+def func(x: int) -> Optional[int]:
|
||||
105 106 | for i in range(5):
|
||||
106 107 | if i > 0:
|
||||
107 108 | break
|
||||
|
||||
auto_return_type.py:112:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
112 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
113 | try:
|
||||
114 | pass
|
||||
|
|
||||
= help: Add return type annotation: `Optional[int]`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from typing import Optional
|
||||
1 2 | def func():
|
||||
2 3 | return 1
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
109 110 | return 4
|
||||
110 111 |
|
||||
111 112 |
|
||||
112 |-def func(x: int):
|
||||
113 |+def func(x: int) -> Optional[int]:
|
||||
113 114 | try:
|
||||
114 115 | pass
|
||||
115 116 | except:
|
||||
|
||||
auto_return_type.py:119:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
119 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
120 | try:
|
||||
121 | pass
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
116 116 | return 1
|
||||
117 117 |
|
||||
118 118 |
|
||||
119 |-def func(x: int):
|
||||
119 |+def func(x: int) -> int:
|
||||
120 120 | try:
|
||||
121 121 | pass
|
||||
122 122 | except:
|
||||
|
||||
auto_return_type.py:128:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
128 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
129 | try:
|
||||
130 | pass
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
125 125 | return 2
|
||||
126 126 |
|
||||
127 127 |
|
||||
128 |-def func(x: int):
|
||||
128 |+def func(x: int) -> int:
|
||||
129 129 | try:
|
||||
130 130 | pass
|
||||
131 131 | except:
|
||||
|
||||
auto_return_type.py:137:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
137 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
138 | try:
|
||||
139 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `int`
|
||||
|
||||
ℹ Unsafe fix
|
||||
134 134 | return 2
|
||||
135 135 |
|
||||
136 136 |
|
||||
137 |-def func(x: int):
|
||||
137 |+def func(x: int) -> int:
|
||||
138 138 | try:
|
||||
139 139 | return 1
|
||||
140 140 | except:
|
||||
|
||||
auto_return_type.py:146:5: ANN201 [*] Missing return type annotation for public function `func`
|
||||
|
|
||||
146 | def func(x: int):
|
||||
| ^^^^ ANN201
|
||||
147 | while x > 0:
|
||||
148 | break
|
||||
|
|
||||
= help: Add return type annotation: `Optional[int]`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from typing import Optional
|
||||
1 2 | def func():
|
||||
2 3 | return 1
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
143 144 | pass
|
||||
144 145 |
|
||||
145 146 |
|
||||
146 |-def func(x: int):
|
||||
147 |+def func(x: int) -> Optional[int]:
|
||||
147 148 | while x > 0:
|
||||
148 149 | break
|
||||
149 150 | return 1
|
||||
|
||||
auto_return_type.py:158:9: ANN201 Missing return type annotation for public function `method`
|
||||
|
|
||||
156 | class Foo(abc.ABC):
|
||||
157 | @abstractmethod
|
||||
158 | def method(self):
|
||||
| ^^^^^^ ANN201
|
||||
159 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
auto_return_type.py:162:9: ANN201 Missing return type annotation for public function `method`
|
||||
|
|
||||
161 | @abc.abstractmethod
|
||||
162 | def method(self):
|
||||
| ^^^^^^ ANN201
|
||||
163 | """Docstring."""
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
auto_return_type.py:166:9: ANN201 Missing return type annotation for public function `method`
|
||||
|
|
||||
165 | @abc.abstractmethod
|
||||
166 | def method(self):
|
||||
| ^^^^^^ ANN201
|
||||
167 | ...
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
auto_return_type.py:171:9: ANN205 Missing return type annotation for staticmethod `method`
|
||||
|
|
||||
169 | @staticmethod
|
||||
170 | @abstractmethod
|
||||
171 | def method():
|
||||
| ^^^^^^ ANN205
|
||||
172 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
auto_return_type.py:176:9: ANN206 Missing return type annotation for classmethod `method`
|
||||
|
|
||||
174 | @classmethod
|
||||
175 | @abstractmethod
|
||||
176 | def method(cls):
|
||||
| ^^^^^^ ANN206
|
||||
177 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
|
||||
auto_return_type.py:180:9: ANN201 [*] Missing return type annotation for public function `method`
|
||||
|
|
||||
179 | @abstractmethod
|
||||
180 | def method(self):
|
||||
| ^^^^^^ ANN201
|
||||
181 | if self.x > 0:
|
||||
182 | return 1
|
||||
|
|
||||
= help: Add return type annotation: `float`
|
||||
|
||||
ℹ Unsafe fix
|
||||
177 177 | pass
|
||||
178 178 |
|
||||
179 179 | @abstractmethod
|
||||
180 |- def method(self):
|
||||
180 |+ def method(self) -> float:
|
||||
181 181 | if self.x > 0:
|
||||
182 182 | return 1
|
||||
183 183 | else:
|
||||
|
||||
|
||||
@@ -1,14 +1,24 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_annotations/mod.rs
|
||||
---
|
||||
annotation_presence.py:5:5: ANN201 Missing return type annotation for public function `foo`
|
||||
annotation_presence.py:5:5: ANN201 [*] Missing return type annotation for public function `foo`
|
||||
|
|
||||
4 | # Error
|
||||
5 | def foo(a, b):
|
||||
| ^^^ ANN201
|
||||
6 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
2 2 | from typing_extensions import override
|
||||
3 3 |
|
||||
4 4 | # Error
|
||||
5 |-def foo(a, b):
|
||||
5 |+def foo(a, b) -> None:
|
||||
6 6 | pass
|
||||
7 7 |
|
||||
8 8 |
|
||||
|
||||
annotation_presence.py:5:9: ANN001 Missing type annotation for function argument `a`
|
||||
|
|
||||
@@ -26,14 +36,24 @@ annotation_presence.py:5:12: ANN001 Missing type annotation for function argumen
|
||||
6 | pass
|
||||
|
|
||||
|
||||
annotation_presence.py:10:5: ANN201 Missing return type annotation for public function `foo`
|
||||
annotation_presence.py:10:5: ANN201 [*] Missing return type annotation for public function `foo`
|
||||
|
|
||||
9 | # Error
|
||||
10 | def foo(a: int, b):
|
||||
| ^^^ ANN201
|
||||
11 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
7 7 |
|
||||
8 8 |
|
||||
9 9 | # Error
|
||||
10 |-def foo(a: int, b):
|
||||
10 |+def foo(a: int, b) -> None:
|
||||
11 11 | pass
|
||||
12 12 |
|
||||
13 13 |
|
||||
|
||||
annotation_presence.py:10:17: ANN001 Missing type annotation for function argument `b`
|
||||
|
|
||||
@@ -51,23 +71,43 @@ annotation_presence.py:15:17: ANN001 Missing type annotation for function argume
|
||||
16 | pass
|
||||
|
|
||||
|
||||
annotation_presence.py:20:5: ANN201 Missing return type annotation for public function `foo`
|
||||
annotation_presence.py:20:5: ANN201 [*] Missing return type annotation for public function `foo`
|
||||
|
|
||||
19 | # Error
|
||||
20 | def foo(a: int, b: int):
|
||||
| ^^^ ANN201
|
||||
21 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
annotation_presence.py:25:5: ANN201 Missing return type annotation for public function `foo`
|
||||
ℹ Unsafe fix
|
||||
17 17 |
|
||||
18 18 |
|
||||
19 19 | # Error
|
||||
20 |-def foo(a: int, b: int):
|
||||
20 |+def foo(a: int, b: int) -> None:
|
||||
21 21 | pass
|
||||
22 22 |
|
||||
23 23 |
|
||||
|
||||
annotation_presence.py:25:5: ANN201 [*] Missing return type annotation for public function `foo`
|
||||
|
|
||||
24 | # Error
|
||||
25 | def foo():
|
||||
| ^^^ ANN201
|
||||
26 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
22 22 |
|
||||
23 23 |
|
||||
24 24 | # Error
|
||||
25 |-def foo():
|
||||
25 |+def foo() -> None:
|
||||
26 26 | pass
|
||||
27 27 |
|
||||
28 28 |
|
||||
|
||||
annotation_presence.py:45:12: ANN401 Dynamically typed expressions (typing.Any) are disallowed in `a`
|
||||
|
|
||||
|
||||
@@ -1,13 +1,23 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_annotations/mod.rs
|
||||
---
|
||||
ignore_fully_untyped.py:24:5: ANN201 Missing return type annotation for public function `error_partially_typed_1`
|
||||
ignore_fully_untyped.py:24:5: ANN201 [*] Missing return type annotation for public function `error_partially_typed_1`
|
||||
|
|
||||
24 | def error_partially_typed_1(a: int, b):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ ANN201
|
||||
25 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
21 21 | pass
|
||||
22 22 |
|
||||
23 23 |
|
||||
24 |-def error_partially_typed_1(a: int, b):
|
||||
24 |+def error_partially_typed_1(a: int, b) -> None:
|
||||
25 25 | pass
|
||||
26 26 |
|
||||
27 27 |
|
||||
|
||||
ignore_fully_untyped.py:24:37: ANN001 Missing type annotation for function argument `b`
|
||||
|
|
||||
@@ -23,15 +33,25 @@ ignore_fully_untyped.py:28:37: ANN001 Missing type annotation for function argum
|
||||
29 | pass
|
||||
|
|
||||
|
||||
ignore_fully_untyped.py:32:5: ANN201 Missing return type annotation for public function `error_partially_typed_3`
|
||||
ignore_fully_untyped.py:32:5: ANN201 [*] Missing return type annotation for public function `error_partially_typed_3`
|
||||
|
|
||||
32 | def error_partially_typed_3(a: int, b: int):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ ANN201
|
||||
33 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ignore_fully_untyped.py:43:9: ANN201 Missing return type annotation for public function `error_typed_self`
|
||||
ℹ Unsafe fix
|
||||
29 29 | pass
|
||||
30 30 |
|
||||
31 31 |
|
||||
32 |-def error_partially_typed_3(a: int, b: int):
|
||||
32 |+def error_partially_typed_3(a: int, b: int) -> None:
|
||||
33 33 | pass
|
||||
34 34 |
|
||||
35 35 |
|
||||
|
||||
ignore_fully_untyped.py:43:9: ANN201 [*] Missing return type annotation for public function `error_typed_self`
|
||||
|
|
||||
41 | pass
|
||||
42 |
|
||||
@@ -39,6 +59,14 @@ ignore_fully_untyped.py:43:9: ANN201 Missing return type annotation for public f
|
||||
| ^^^^^^^^^^^^^^^^ ANN201
|
||||
44 | pass
|
||||
|
|
||||
= help: Add return type annotation
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
40 40 | def ok_untyped_method(self):
|
||||
41 41 | pass
|
||||
42 42 |
|
||||
43 |- def error_typed_self(self: X):
|
||||
43 |+ def error_typed_self(self: X) -> None:
|
||||
44 44 | pass
|
||||
|
||||
|
||||
|
||||
@@ -41,14 +41,24 @@ mypy_init_return.py:11:9: ANN204 [*] Missing return type annotation for special
|
||||
13 13 |
|
||||
14 14 |
|
||||
|
||||
mypy_init_return.py:40:5: ANN202 Missing return type annotation for private function `__init__`
|
||||
mypy_init_return.py:40:5: ANN202 [*] Missing return type annotation for private function `__init__`
|
||||
|
|
||||
39 | # Error
|
||||
40 | def __init__(self, foo: int):
|
||||
| ^^^^^^^^ ANN202
|
||||
41 | ...
|
||||
|
|
||||
= help: Add return type annotation
|
||||
= help: Add return type annotation: `None`
|
||||
|
||||
ℹ Unsafe fix
|
||||
37 37 |
|
||||
38 38 |
|
||||
39 39 | # Error
|
||||
40 |-def __init__(self, foo: int):
|
||||
40 |+def __init__(self, foo: int) -> None:
|
||||
41 41 | ...
|
||||
42 42 |
|
||||
43 43 |
|
||||
|
||||
mypy_init_return.py:47:9: ANN204 [*] Missing return type annotation for special method `__init__`
|
||||
|
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user