Compare commits
400 Commits
charlie/br
...
main-backu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e87032d57d | ||
|
|
0c2abf8316 | ||
|
|
6e85e0010f | ||
|
|
2ea0a0b28a | ||
|
|
10abc8fd92 | ||
|
|
61195bc7b1 | ||
|
|
9040554be5 | ||
|
|
06ba1cf0ad | ||
|
|
7a63a8f45a | ||
|
|
c1bc0dc83d | ||
|
|
90573975f3 | ||
|
|
802df97d55 | ||
|
|
6f4ee32807 | ||
|
|
7ec979d022 | ||
|
|
fab9cc5294 | ||
|
|
ff7bab589e | ||
|
|
593b46be5e | ||
|
|
13196fc500 | ||
|
|
5ef4ccd632 | ||
|
|
e363fb860e | ||
|
|
4888d800fb | ||
|
|
4d03b9b5b2 | ||
|
|
fb365c642b | ||
|
|
db04fd4157 | ||
|
|
ff3e8ead36 | ||
|
|
1bbec2b967 | ||
|
|
510be51cfa | ||
|
|
3b4c8fffe5 | ||
|
|
126652b684 | ||
|
|
57e8712d76 | ||
|
|
78c6ede1c9 | ||
|
|
a843a00f6b | ||
|
|
f846f1ea42 | ||
|
|
1d4b7a395f | ||
|
|
ce3ce0734b | ||
|
|
b0e119f049 | ||
|
|
1f5e707829 | ||
|
|
ed7acfe477 | ||
|
|
c31b58eb39 | ||
|
|
c0a3a20c63 | ||
|
|
05ae26b935 | ||
|
|
b996b21ffc | ||
|
|
2d1f69cbb9 | ||
|
|
0f2e295f3a | ||
|
|
c174bbf1f2 | ||
|
|
8078663b6c | ||
|
|
f60e204b73 | ||
|
|
08ebbe40d7 | ||
|
|
ed3b4eb72b | ||
|
|
f0d200c8a1 | ||
|
|
8d74eee750 | ||
|
|
21aa0b8d84 | ||
|
|
9cb00518e5 | ||
|
|
6f65c5cba7 | ||
|
|
8a415fa61e | ||
|
|
5054cbe84f | ||
|
|
69d27d924c | ||
|
|
5270020423 | ||
|
|
edcfcb4a74 | ||
|
|
40a603208f | ||
|
|
b2f95e2848 | ||
|
|
a2e3209c42 | ||
|
|
5e9e8a7589 | ||
|
|
fdec727f80 | ||
|
|
3fbf4f6804 | ||
|
|
fe25708d89 | ||
|
|
342cd19f50 | ||
|
|
4a2c4aad0b | ||
|
|
ae3a477c97 | ||
|
|
531aeb3511 | ||
|
|
4de0cb1827 | ||
|
|
5493c9f4e3 | ||
|
|
d23611db65 | ||
|
|
7d384d88d0 | ||
|
|
b81273e9bc | ||
|
|
335780aeea | ||
|
|
e1f02fced7 | ||
|
|
d4084fb17a | ||
|
|
41a0ef8740 | ||
|
|
25cc1da319 | ||
|
|
33a3c407a9 | ||
|
|
fac0c25343 | ||
|
|
4ff779c298 | ||
|
|
726884b287 | ||
|
|
851f23668f | ||
|
|
3654cf0bdf | ||
|
|
6c5c311bab | ||
|
|
531e41ae2c | ||
|
|
b48834fe2d | ||
|
|
205ee80033 | ||
|
|
fc301ab1b0 | ||
|
|
e820928f11 | ||
|
|
ff17f6e178 | ||
|
|
3bdf8a940a | ||
|
|
9d47d3d212 | ||
|
|
611dcc2e9b | ||
|
|
53de75efc3 | ||
|
|
0c7d16b61a | ||
|
|
735c06d5f4 | ||
|
|
02f13abf50 | ||
|
|
10dda125ff | ||
|
|
27e3873dc2 | ||
|
|
dd4cc25227 | ||
|
|
b78001c953 | ||
|
|
718354673e | ||
|
|
192379cede | ||
|
|
a983f4383f | ||
|
|
947fb53d0b | ||
|
|
2baad9ead6 | ||
|
|
2af9805662 | ||
|
|
5b2af304a2 | ||
|
|
cbe4e8c5f3 | ||
|
|
aabc96dde9 | ||
|
|
99e108dd53 | ||
|
|
d6b6df5d1c | ||
|
|
822cac5aa0 | ||
|
|
aa101e4f26 | ||
|
|
cbc4cb286b | ||
|
|
6fa3d0f90a | ||
|
|
455bcc01a0 | ||
|
|
d495cd9129 | ||
|
|
d8822d1091 | ||
|
|
75f6ce1ae5 | ||
|
|
41e9e7280a | ||
|
|
17c8abcec1 | ||
|
|
e000b1c304 | ||
|
|
243ca16b34 | ||
|
|
4de9580b92 | ||
|
|
5cf85f0b9d | ||
|
|
4dc030ba9d | ||
|
|
1d366d52ab | ||
|
|
a3d9d8cb14 | ||
|
|
09a6afdd04 | ||
|
|
a14e43e03a | ||
|
|
f47dfca4e3 | ||
|
|
58c35ab458 | ||
|
|
7b8844bd3e | ||
|
|
6d7358090b | ||
|
|
9f1a538eba | ||
|
|
48920a034e | ||
|
|
13d6e275ef | ||
|
|
96eb80f5cf | ||
|
|
a73bac9ed1 | ||
|
|
6b60f85cc4 | ||
|
|
39b2dbe04d | ||
|
|
e1f70100ac | ||
|
|
d66d935879 | ||
|
|
e28f333f23 | ||
|
|
0adcdd995d | ||
|
|
e6eb49ffb0 | ||
|
|
80109b1fe0 | ||
|
|
bd64603950 | ||
|
|
5b0e92d725 | ||
|
|
ae9d3c3193 | ||
|
|
3873414b30 | ||
|
|
a9f4b59f40 | ||
|
|
6d6155413b | ||
|
|
f9b5469642 | ||
|
|
ef38eb6b1a | ||
|
|
0ea53825db | ||
|
|
9d6ae774f8 | ||
|
|
55fc0e83f3 | ||
|
|
713dd2b91e | ||
|
|
969ea23d67 | ||
|
|
b80bbec8e6 | ||
|
|
8f425e9ce2 | ||
|
|
d7e2e7361e | ||
|
|
c37e0c7f03 | ||
|
|
0d7b94817d | ||
|
|
f43e5b72e2 | ||
|
|
a19f294b0c | ||
|
|
7ebef61c47 | ||
|
|
b61f4d7b69 | ||
|
|
e26369a34e | ||
|
|
97a08ee77b | ||
|
|
cb8c6fb78d | ||
|
|
8580e4ebb5 | ||
|
|
39fc23cf92 | ||
|
|
66e3080173 | ||
|
|
1511b6631b | ||
|
|
2a8aa6f308 | ||
|
|
dc628cab8f | ||
|
|
c137bc9d77 | ||
|
|
60180fd54c | ||
|
|
c7ed645cc6 | ||
|
|
8caa28f0f8 | ||
|
|
8aa3bc93f3 | ||
|
|
f39ffef370 | ||
|
|
e093d2bee6 | ||
|
|
ca5b474d45 | ||
|
|
2b43d45bd5 | ||
|
|
4bdc2d47c1 | ||
|
|
09b82e41d5 | ||
|
|
cc6d8a1c58 | ||
|
|
07918f0a9a | ||
|
|
a0786ea872 | ||
|
|
4713b2b3ab | ||
|
|
e7f14ab9b8 | ||
|
|
56c73cc63d | ||
|
|
659f4dd8bf | ||
|
|
bd158089e0 | ||
|
|
a73bee7aae | ||
|
|
1468fe46ab | ||
|
|
c9364718b4 | ||
|
|
38cf933bcb | ||
|
|
838990ae15 | ||
|
|
aa0290bbfc | ||
|
|
f74e44d1e8 | ||
|
|
d9df131720 | ||
|
|
f2ffe12f8c | ||
|
|
6dba8430be | ||
|
|
b26365b215 | ||
|
|
2d019930e9 | ||
|
|
d5fc7c4c87 | ||
|
|
393869c47c | ||
|
|
4edd2bf78a | ||
|
|
d3c4551629 | ||
|
|
581f6e176c | ||
|
|
ac4d3c076c | ||
|
|
e5fe037e38 | ||
|
|
872b9d4765 | ||
|
|
674eeec29c | ||
|
|
4f1e7c6291 | ||
|
|
884a7bdb15 | ||
|
|
7885344bcf | ||
|
|
e8fef39861 | ||
|
|
509cf7ed0d | ||
|
|
a4a5366504 | ||
|
|
2dfd053bed | ||
|
|
7f552e4594 | ||
|
|
9efa872023 | ||
|
|
fd8468c5eb | ||
|
|
958c7e33ad | ||
|
|
0f311cd5e5 | ||
|
|
0365752bf3 | ||
|
|
6d140426c1 | ||
|
|
33a62789f7 | ||
|
|
84dff79ddc | ||
|
|
300710f7db | ||
|
|
515dceb07b | ||
|
|
2858c315bf | ||
|
|
3b0fd61b3b | ||
|
|
9030679193 | ||
|
|
71ba4226c1 | ||
|
|
3d03439618 | ||
|
|
39f410c909 | ||
|
|
4222b13e6c | ||
|
|
d691527ead | ||
|
|
40e2150f7e | ||
|
|
92bf96608c | ||
|
|
fac6a857f6 | ||
|
|
68586f8e3c | ||
|
|
f8787a9377 | ||
|
|
530a20cc96 | ||
|
|
6cb57b2075 | ||
|
|
92b2574d52 | ||
|
|
95fb938bd6 | ||
|
|
9683314264 | ||
|
|
d9bbeeb9b3 | ||
|
|
68116d5c11 | ||
|
|
e164a41723 | ||
|
|
aa32a73c5b | ||
|
|
9d7d629cef | ||
|
|
439298e735 | ||
|
|
16173bf581 | ||
|
|
7e7b2eadee | ||
|
|
80116c768d | ||
|
|
200390c1ab | ||
|
|
24d2ab8b0a | ||
|
|
41f21a7b5d | ||
|
|
63e4a36e27 | ||
|
|
15ab44384c | ||
|
|
5380b85579 | ||
|
|
b707f53f23 | ||
|
|
6439867f78 | ||
|
|
1904d095f9 | ||
|
|
7e8f683808 | ||
|
|
37b1894834 | ||
|
|
201d08583a | ||
|
|
107b2e11ae | ||
|
|
661b210391 | ||
|
|
9aed9143fe | ||
|
|
c21d0d9283 | ||
|
|
313fd7d28c | ||
|
|
3aa0096212 | ||
|
|
53dec88029 | ||
|
|
bd0c15d34e | ||
|
|
ce0be73841 | ||
|
|
b2ac4f60f1 | ||
|
|
1fdfa5fe1b | ||
|
|
c0f390ebc6 | ||
|
|
c387b5d523 | ||
|
|
ea95e1a715 | ||
|
|
c16e08d59b | ||
|
|
ff00460ff4 | ||
|
|
f4672e4256 | ||
|
|
6e89b3ab1a | ||
|
|
6a174dae45 | ||
|
|
8936ab2f8d | ||
|
|
165b979733 | ||
|
|
06c92bb899 | ||
|
|
1a657731dd | ||
|
|
d8cfc7e84f | ||
|
|
de2e88656e | ||
|
|
052dee72b8 | ||
|
|
751f9d304f | ||
|
|
d6f9dd0763 | ||
|
|
5fc8c4d0b1 | ||
|
|
1b7a272b77 | ||
|
|
3abdc87076 | ||
|
|
99b02be35a | ||
|
|
f99167d4ed | ||
|
|
816e1e711c | ||
|
|
e2b28d07c8 | ||
|
|
28785784b2 | ||
|
|
6f6b7b2312 | ||
|
|
93cb05ebda | ||
|
|
e8200ab674 | ||
|
|
492f09298f | ||
|
|
35eea0b8ec | ||
|
|
1eeddb521e | ||
|
|
96a50810a6 | ||
|
|
51b7dbb89c | ||
|
|
28a8c3a062 | ||
|
|
4e5626dfd5 | ||
|
|
f979d8dbc3 | ||
|
|
cc084b4fec | ||
|
|
bbeec36fdb | ||
|
|
b6c230f3ca | ||
|
|
a5b59f3c9d | ||
|
|
d5a208ca9d | ||
|
|
fa41a1e2f6 | ||
|
|
952d70b9d1 | ||
|
|
02953b9fe6 | ||
|
|
8adc74fe26 | ||
|
|
48c0cb5599 | ||
|
|
2e33a3d0e9 | ||
|
|
1cc342e4ed | ||
|
|
519718e65d | ||
|
|
5da5490b19 | ||
|
|
518cf728c3 | ||
|
|
3397737a76 | ||
|
|
6211a3a3a8 | ||
|
|
e64d7d196d | ||
|
|
f8b45e48e1 | ||
|
|
f99e4789ed | ||
|
|
86b847204e | ||
|
|
26b529f9dc | ||
|
|
b21ed24025 | ||
|
|
a414677892 | ||
|
|
bfe4795b6c | ||
|
|
40690b9761 | ||
|
|
1fd898c14c | ||
|
|
42b95a9a95 | ||
|
|
e8230efe1a | ||
|
|
7fcc18daea | ||
|
|
2b7bf79d29 | ||
|
|
904fc477f1 | ||
|
|
bfac0355dc | ||
|
|
c16e650071 | ||
|
|
acde8bb625 | ||
|
|
7f99404618 | ||
|
|
3742f9117b | ||
|
|
a66902406f | ||
|
|
51b6571ee1 | ||
|
|
2345bc895d | ||
|
|
53c48bf6b9 | ||
|
|
ffacac05bb | ||
|
|
3bae0823f7 | ||
|
|
29644a30d7 | ||
|
|
07712eda58 | ||
|
|
76728bb69d | ||
|
|
721c2709c8 | ||
|
|
7865cace06 | ||
|
|
a9330765fa | ||
|
|
9941eef853 | ||
|
|
e494175cd2 | ||
|
|
dd7560c494 | ||
|
|
6603b4a124 | ||
|
|
dab7e20a33 | ||
|
|
13f3ec5b2b | ||
|
|
452cd308d5 | ||
|
|
3c469eaa26 | ||
|
|
0c5bf8e470 | ||
|
|
3f52fdd50a | ||
|
|
f0c0897f23 | ||
|
|
a35d4dc22a | ||
|
|
5fe1bd3900 | ||
|
|
3737af157f | ||
|
|
ed01cd63ee | ||
|
|
1dd57971a9 | ||
|
|
d1ae5a7448 | ||
|
|
e89d6febea | ||
|
|
4dcad05aa0 | ||
|
|
b0d270b881 | ||
|
|
e3ccef2504 | ||
|
|
dcdd964e0c | ||
|
|
107442cc06 | ||
|
|
10957879db | ||
|
|
f0148f46d0 |
1
.github/release.yml
vendored
1
.github/release.yml
vendored
@@ -4,7 +4,6 @@ changelog:
|
||||
labels:
|
||||
- internal
|
||||
- documentation
|
||||
- formatter
|
||||
categories:
|
||||
- title: Breaking Changes
|
||||
labels:
|
||||
|
||||
14
.github/workflows/benchmark.yaml
vendored
14
.github/workflows/benchmark.yaml
vendored
@@ -3,12 +3,12 @@ name: Benchmark
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "Cargo.toml"
|
||||
- "Cargo.lock"
|
||||
- "rust-toolchain"
|
||||
- "crates/**"
|
||||
- "!crates/ruff_dev"
|
||||
- "!crates/ruff_shrinking"
|
||||
- 'Cargo.toml'
|
||||
- 'Cargo.lock'
|
||||
- 'rust-toolchain'
|
||||
- 'crates/**'
|
||||
- '!crates/ruff_dev'
|
||||
- '!crates/ruff_shrinking'
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
name: "Run | ${{ matrix.os }}"
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
os: [ ubuntu-latest, windows-latest ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
|
||||
26
.github/workflows/ci.yaml
vendored
26
.github/workflows/ci.yaml
vendored
@@ -2,7 +2,7 @@ name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -16,7 +16,7 @@ env:
|
||||
CARGO_TERM_COLOR: always
|
||||
RUSTUP_MAX_RETRIES: 10
|
||||
PACKAGE_NAME: ruff
|
||||
PYTHON_VERSION: "3.11"
|
||||
PYTHON_VERSION: "3.11" # to build abi3 wheels
|
||||
|
||||
jobs:
|
||||
determine_changes:
|
||||
@@ -42,7 +42,6 @@ jobs:
|
||||
- "!crates/ruff_formatter/**"
|
||||
- "!crates/ruff_dev/**"
|
||||
- "!crates/ruff_shrinking/**"
|
||||
- scripts/*
|
||||
|
||||
formatter:
|
||||
- Cargo.toml
|
||||
@@ -51,12 +50,7 @@ jobs:
|
||||
- crates/ruff_formatter/**
|
||||
- crates/ruff_python_trivia/**
|
||||
- crates/ruff_python_ast/**
|
||||
- crates/ruff_source_file/**
|
||||
- crates/ruff_python_index/**
|
||||
- crates/ruff_text_size/**
|
||||
- crates/ruff_python_parser/**
|
||||
- crates/ruff_dev/**
|
||||
- scripts/*
|
||||
|
||||
|
||||
cargo-fmt:
|
||||
name: "cargo fmt"
|
||||
@@ -85,7 +79,7 @@ jobs:
|
||||
cargo-test:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
os: [ ubuntu-latest, windows-latest ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: "cargo test | ${{ matrix.os }}"
|
||||
steps:
|
||||
@@ -237,6 +231,7 @@ jobs:
|
||||
- name: "Run cargo-udeps"
|
||||
run: cargo +nightly-2023-06-08 udeps
|
||||
|
||||
|
||||
python-package:
|
||||
name: "python package"
|
||||
runs-on: ubuntu-latest
|
||||
@@ -336,8 +331,11 @@ jobs:
|
||||
- name: "Cache rust"
|
||||
uses: Swatinem/rust-cache@v2
|
||||
- name: "Formatter progress"
|
||||
run: scripts/formatter_ecosystem_checks.sh
|
||||
run: scripts/formatter_progress.sh
|
||||
- name: "Github step summary"
|
||||
run: grep "similarity index" target/progress_projects_log.txt | sort > $GITHUB_STEP_SUMMARY
|
||||
- name: "Remove checkouts from cache"
|
||||
run: rm -r target/progress_projects
|
||||
run: grep "similarity index" target/progress_projects_report.txt | sort > $GITHUB_STEP_SUMMARY
|
||||
# CPython is not black formatted, so we run only the stability check
|
||||
- name: "Clone CPython 3.10"
|
||||
run: git clone --branch 3.10 --depth 1 https://github.com/python/cpython.git crates/ruff/resources/test/cpython
|
||||
- name: "Check CPython stability"
|
||||
run: cargo run --bin ruff_dev -- format-dev --stability-check crates/ruff/resources/test/cpython
|
||||
|
||||
4
.github/workflows/docs.yaml
vendored
4
.github/workflows/docs.yaml
vendored
@@ -3,7 +3,7 @@ name: mkdocs
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [published]
|
||||
types: [ published ]
|
||||
|
||||
jobs:
|
||||
mkdocs:
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
run: mkdocs build --strict -f mkdocs.generated.yml
|
||||
- name: "Deploy to Cloudflare Pages"
|
||||
if: ${{ env.CF_API_TOKEN_EXISTS == 'true' }}
|
||||
uses: cloudflare/wrangler-action@3.0.0
|
||||
uses: cloudflare/wrangler-action@2.0.0
|
||||
with:
|
||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||
accountId: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
|
||||
6
.github/workflows/flake8-to-ruff.yaml
vendored
6
.github/workflows/flake8-to-ruff.yaml
vendored
@@ -66,7 +66,7 @@ jobs:
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target: [x64, x86]
|
||||
target: [ x64, x86 ]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
@@ -94,7 +94,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target: [x86_64, i686]
|
||||
target: [ x86_64, i686 ]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
@@ -121,7 +121,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target: [aarch64, armv7, s390x, ppc64le, ppc64]
|
||||
target: [ aarch64, armv7, s390x, ppc64le, ppc64 ]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
|
||||
4
.github/workflows/playground.yaml
vendored
4
.github/workflows/playground.yaml
vendored
@@ -3,7 +3,7 @@ name: "[Playground] Release"
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [published]
|
||||
types: [ published ]
|
||||
|
||||
env:
|
||||
CARGO_INCREMENTAL: 0
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
working-directory: playground
|
||||
- name: "Deploy to Cloudflare Pages"
|
||||
if: ${{ env.CF_API_TOKEN_EXISTS == 'true' }}
|
||||
uses: cloudflare/wrangler-action@3.0.0
|
||||
uses: cloudflare/wrangler-action@2.0.0
|
||||
with:
|
||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||
accountId: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
|
||||
4
.github/workflows/pr-comment.yaml
vendored
4
.github/workflows/pr-comment.yaml
vendored
@@ -2,8 +2,8 @@ name: PR Check Comment
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [CI, Benchmark]
|
||||
types: [completed]
|
||||
workflows: [ CI, Benchmark ]
|
||||
types: [ completed ]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
workflow_run_id:
|
||||
|
||||
@@ -42,13 +42,13 @@ repos:
|
||||
name: cargo fmt
|
||||
entry: cargo fmt --
|
||||
language: system
|
||||
types: [rust]
|
||||
types: [ rust ]
|
||||
pass_filenames: false # This makes it a lot faster
|
||||
- id: ruff
|
||||
name: ruff
|
||||
entry: cargo run --bin ruff -- check --no-cache --force-exclude --fix --exit-non-zero-on-fix
|
||||
language: system
|
||||
types_or: [python, pyi]
|
||||
types_or: [ python, pyi ]
|
||||
require_serial: true
|
||||
exclude: |
|
||||
(?x)^(
|
||||
@@ -62,12 +62,5 @@ repos:
|
||||
hooks:
|
||||
- id: black
|
||||
|
||||
# Prettier
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: v3.0.0
|
||||
hooks:
|
||||
- id: prettier
|
||||
types: [yaml]
|
||||
|
||||
ci:
|
||||
skip: [cargo-fmt, dev-generate-all]
|
||||
skip: [ cargo-fmt, dev-generate-all ]
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
# Breaking Changes
|
||||
|
||||
## 0.0.283 / 0.284
|
||||
|
||||
### The target Python version now defaults to 3.8 instead of 3.10 ([#6397](https://github.com/astral-sh/ruff/pull/6397))
|
||||
|
||||
Previously, when a target Python version was not specified, Ruff would use a default of Python 3.10. However, it is safer to default to an _older_ Python version to avoid assuming the availability of new features. We now default to the oldest supported Python version which is currently Python 3.8.
|
||||
|
||||
(We still support Python 3.7 but since [it has reached EOL](https://devguide.python.org/versions/#unsupported-versions) we've decided not to make it the default here.)
|
||||
|
||||
Note this change was announced in 0.0.283 but not active until 0.0.284.
|
||||
|
||||
## 0.0.277
|
||||
|
||||
### `.ipynb_checkpoints`, `.pyenv`, `.pytest_cache`, and `.vscode` are now excluded by default ([#5513](https://github.com/astral-sh/ruff/pull/5513))
|
||||
|
||||
@@ -69,13 +69,6 @@ and pre-commit to run some validation checks:
|
||||
pipx install pre-commit # or `pip install pre-commit` if you have a virtualenv
|
||||
```
|
||||
|
||||
You can optionally install pre-commit hooks to automatically run the validation checks
|
||||
when making a commit:
|
||||
|
||||
```shell
|
||||
pre-commit install
|
||||
```
|
||||
|
||||
### Development
|
||||
|
||||
After cloning the repository, run Ruff locally from the repository root with:
|
||||
@@ -131,6 +124,7 @@ At time of writing, the repository includes the following crates:
|
||||
- `crates/ruff_macros`: proc macro crate containing macros used by Ruff.
|
||||
- `crates/ruff_python_ast`: library crate containing Python-specific AST types and utilities.
|
||||
- `crates/ruff_python_codegen`: library crate containing utilities for generating Python source code.
|
||||
- `crates/ruff_python_codegen`: library crate containing utilities for generating Python source code.
|
||||
- `crates/ruff_python_formatter`: library crate implementing the Python formatter. Emits an
|
||||
intermediate representation for each node, which `ruff_formatter` prints based on the configured
|
||||
line length.
|
||||
|
||||
63
Cargo.lock
generated
63
Cargo.lock
generated
@@ -133,12 +133,6 @@ dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
|
||||
|
||||
[[package]]
|
||||
name = "ascii-canvas"
|
||||
version = "3.0.0"
|
||||
@@ -800,7 +794,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.284"
|
||||
version = "0.0.280"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -1033,7 +1027,6 @@ dependencies = [
|
||||
"number_prefix",
|
||||
"portable-atomic",
|
||||
"unicode-width",
|
||||
"vt100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2042,7 +2035,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.284"
|
||||
version = "0.0.280"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
@@ -2141,7 +2134,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_cli"
|
||||
version = "0.0.284"
|
||||
version = "0.0.280"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
@@ -2201,6 +2194,7 @@ dependencies = [
|
||||
"indoc",
|
||||
"itertools",
|
||||
"libcst",
|
||||
"log",
|
||||
"once_cell",
|
||||
"pretty_assertions",
|
||||
"rayon",
|
||||
@@ -2224,9 +2218,6 @@ dependencies = [
|
||||
"strum_macros",
|
||||
"tempfile",
|
||||
"toml",
|
||||
"tracing",
|
||||
"tracing-indicatif",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2291,7 +2282,6 @@ dependencies = [
|
||||
"rustc-hash",
|
||||
"serde",
|
||||
"smallvec",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3125,18 +3115,6 @@ dependencies = [
|
||||
"valuable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-indicatif"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b38ed3722d27705c3bd7ca0ccf29acc3d8e1c717b4cd87f97891a2c1834ea1af"
|
||||
dependencies = [
|
||||
"indicatif",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-log"
|
||||
version = "0.1.3"
|
||||
@@ -3335,39 +3313,6 @@ version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "vt100"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84cd863bf0db7e392ba3bd04994be3473491b31e66340672af5d11943c6274de"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"log",
|
||||
"unicode-width",
|
||||
"vte",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vte"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"utf8parse",
|
||||
"vte_generate_state_changes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vte_generate_state_changes"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wait-timeout"
|
||||
version = "0.2.0"
|
||||
|
||||
@@ -4,7 +4,7 @@ resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
edition = "2021"
|
||||
rust-version = "1.71"
|
||||
rust-version = "1.70"
|
||||
homepage = "https://beta.ruff.rs/docs"
|
||||
documentation = "https://beta.ruff.rs/docs"
|
||||
repository = "https://github.com/astral-sh/ruff"
|
||||
@@ -46,9 +46,6 @@ syn = { version = "2.0.15" }
|
||||
test-case = { version = "3.0.0" }
|
||||
thiserror = { version = "1.0.43" }
|
||||
toml = { version = "0.7.2" }
|
||||
tracing = "0.1.37"
|
||||
tracing-indicatif = "0.3.4"
|
||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
||||
wsl = { version = "0.1.0" }
|
||||
|
||||
# v1.0.1
|
||||
|
||||
@@ -140,7 +140,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com) hook:
|
||||
```yaml
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.0.284
|
||||
rev: v0.0.280
|
||||
hooks:
|
||||
- id: ruff
|
||||
```
|
||||
@@ -211,8 +211,8 @@ line-length = 88
|
||||
# Allow unused variables when underscore-prefixed.
|
||||
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||
|
||||
# Assume Python 3.8
|
||||
target-version = "py38"
|
||||
# Assume Python 3.10.
|
||||
target-version = "py310"
|
||||
|
||||
[tool.ruff.mccabe]
|
||||
# Unlike Flake8, default to a complexity level of 10.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.284"
|
||||
version = "0.0.280"
|
||||
description = """
|
||||
Convert Flake8 configuration files to Ruff configuration files.
|
||||
"""
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.284"
|
||||
version = "0.0.280"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
|
||||
@@ -14,19 +14,3 @@ with open("/dev/shm/unit/test", "w") as f:
|
||||
# not ok by config
|
||||
with open("/foo/bar", "w") as f:
|
||||
f.write("def")
|
||||
|
||||
# Using `tempfile` module should be ok
|
||||
import tempfile
|
||||
from tempfile import TemporaryDirectory
|
||||
|
||||
with tempfile.NamedTemporaryFile(dir="/tmp") as f:
|
||||
f.write(b"def")
|
||||
|
||||
with tempfile.NamedTemporaryFile(dir="/var/tmp") as f:
|
||||
f.write(b"def")
|
||||
|
||||
with tempfile.TemporaryDirectory(dir="/dev/shm") as d:
|
||||
pass
|
||||
|
||||
with TemporaryDirectory(dir="/tmp") as d:
|
||||
pass
|
||||
|
||||
@@ -67,8 +67,7 @@ cfg.getboolean("hello", True)
|
||||
os.set_blocking(0, False)
|
||||
g_action.set_enabled(True)
|
||||
settings.set_enable_developer_extras(True)
|
||||
foo.is_(True)
|
||||
bar.is_not(False)
|
||||
|
||||
|
||||
class Registry:
|
||||
def __init__(self) -> None:
|
||||
|
||||
@@ -240,16 +240,12 @@ def foo(f=lambda x: print(x)):
|
||||
|
||||
from collections import abc
|
||||
from typing import Annotated, Dict, Optional, Sequence, Union, Set
|
||||
import typing_extensions
|
||||
|
||||
|
||||
def immutable_annotations(
|
||||
a: Sequence[int] | None = [],
|
||||
b: Optional[abc.Mapping[int, int]] = {},
|
||||
c: Annotated[Union[abc.Set[str], abc.Sized], "annotation"] = set(),
|
||||
d: typing_extensions.Annotated[
|
||||
Union[abc.Set[str], abc.Sized], "annotation"
|
||||
] = set(),
|
||||
):
|
||||
pass
|
||||
|
||||
@@ -258,6 +254,5 @@ def mutable_annotations(
|
||||
a: list[int] | None = [],
|
||||
b: Optional[Dict[int, int]] = {},
|
||||
c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||
d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
|
||||
):
|
||||
pass
|
||||
|
||||
@@ -74,10 +74,3 @@ try:
|
||||
except (ValueError, binascii.Error):
|
||||
# binascii.Error is a subclass of ValueError.
|
||||
pass
|
||||
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/6412
|
||||
try:
|
||||
pass
|
||||
except (ValueError, ValueError, TypeError):
|
||||
pass
|
||||
|
||||
@@ -3,6 +3,3 @@ import logging
|
||||
name = "world"
|
||||
logging.info(f"Hello {name}")
|
||||
logging.log(logging.INFO, f"Hello {name}")
|
||||
|
||||
_LOGGER = logging.getLogger()
|
||||
_LOGGER.info(f"{__name__}")
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import typing
|
||||
|
||||
# Shouldn't affect non-union field types.
|
||||
field1: str
|
||||
|
||||
# Should emit for duplicate field types.
|
||||
field2: str | str # PYI016: Duplicate union member `str`
|
||||
|
||||
|
||||
# Should emit for union types in arguments.
|
||||
def func1(arg1: int | int): # PYI016: Duplicate union member `int`
|
||||
print(arg1)
|
||||
|
||||
|
||||
# Should emit for unions in return types.
|
||||
def func2() -> str | str: # PYI016: Duplicate union member `str`
|
||||
return "my string"
|
||||
|
||||
|
||||
# Should emit in longer unions, even if not directly adjacent.
|
||||
field3: str | str | int # PYI016: Duplicate union member `str`
|
||||
field4: int | int | str # PYI016: Duplicate union member `int`
|
||||
@@ -32,55 +33,3 @@ field10: (str | int) | str # PYI016: Duplicate union member `str`
|
||||
|
||||
# Should emit for nested unions.
|
||||
field11: dict[int | int, str]
|
||||
|
||||
# Should emit for unions with more than two cases
|
||||
field12: int | int | int # Error
|
||||
field13: int | int | int | int # Error
|
||||
|
||||
# Should emit for unions with more than two cases, even if not directly adjacent
|
||||
field14: int | int | str | int # Error
|
||||
|
||||
# Should emit for duplicate literal types; also covered by PYI030
|
||||
field15: typing.Literal[1] | typing.Literal[1] # Error
|
||||
|
||||
# Shouldn't emit if in new parent type
|
||||
field16: int | dict[int, str] # OK
|
||||
|
||||
# Shouldn't emit if not in a union parent
|
||||
field17: dict[int, int] # OK
|
||||
|
||||
# Should emit in cases with newlines
|
||||
field18: typing.Union[
|
||||
set[
|
||||
int # foo
|
||||
],
|
||||
set[
|
||||
int # bar
|
||||
],
|
||||
] # Error, newline and comment will not be emitted in message
|
||||
|
||||
# Should emit in cases with `typing.Union` instead of `|`
|
||||
field19: typing.Union[int, int] # Error
|
||||
|
||||
# Should emit in cases with nested `typing.Union`
|
||||
field20: typing.Union[int, typing.Union[int, str]] # Error
|
||||
|
||||
# Should emit in cases with mixed `typing.Union` and `|`
|
||||
field21: typing.Union[int, int | str] # Error
|
||||
|
||||
# Should emit only once in cases with multiple nested `typing.Union`
|
||||
field22: typing.Union[int, typing.Union[int, typing.Union[int, int]]] # Error
|
||||
|
||||
# Should emit in cases with newlines
|
||||
field23: set[ # foo
|
||||
int] | set[int]
|
||||
|
||||
# Should emit twice (once for each `int` in the nested union, both of which are
|
||||
# duplicates of the outer `int`), but not three times (which would indicate that
|
||||
# we incorrectly re-checked the nested union).
|
||||
field24: typing.Union[int, typing.Union[int, int]] # PYI016: Duplicate union member `int`
|
||||
|
||||
# Should emit twice (once for each `int` in the nested union, both of which are
|
||||
# duplicates of the outer `int`), but not three times (which would indicate that
|
||||
# we incorrectly re-checked the nested union).
|
||||
field25: typing.Union[int, int | int] # PYI016: Duplicate union member `int`
|
||||
|
||||
@@ -74,13 +74,3 @@ field22: typing.Union[int, typing.Union[int, typing.Union[int, int]]] # Error
|
||||
# Should emit in cases with newlines
|
||||
field23: set[ # foo
|
||||
int] | set[int]
|
||||
|
||||
# Should emit twice (once for each `int` in the nested union, both of which are
|
||||
# duplicates of the outer `int`), but not three times (which would indicate that
|
||||
# we incorrectly re-checked the nested union).
|
||||
field24: typing.Union[int, typing.Union[int, int]] # PYI016: Duplicate union member `int`
|
||||
|
||||
# Should emit twice (once for each `int` in the nested union, both of which are
|
||||
# duplicates of the outer `int`), but not three times (which would indicate that
|
||||
# we incorrectly re-checked the nested union).
|
||||
field25: typing.Union[int, int | int] # PYI016: Duplicate union member `int`
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
from typing import TypeVar, Self, Type
|
||||
|
||||
_S = TypeVar("_S", bound=BadClass)
|
||||
_S2 = TypeVar("_S2", BadClass, GoodClass)
|
||||
|
||||
class BadClass:
|
||||
def __new__(cls: type[_S], *args: str, **kwargs: int) -> _S: ... # PYI019
|
||||
|
||||
|
||||
def bad_instance_method(self: _S, arg: bytes) -> _S: ... # PYI019
|
||||
|
||||
|
||||
@classmethod
|
||||
def bad_class_method(cls: type[_S], arg: int) -> _S: ... # PYI019
|
||||
|
||||
|
||||
@classmethod
|
||||
def bad_posonly_class_method(cls: type[_S], /) -> _S: ... # PYI019
|
||||
|
||||
|
||||
@classmethod
|
||||
def excluded_edge_case(cls: Type[_S], arg: int) -> _S: ... # Ok
|
||||
|
||||
|
||||
class GoodClass:
|
||||
def __new__(cls: type[Self], *args: list[int], **kwargs: set[str]) -> Self: ...
|
||||
def good_instance_method_1(self: Self, arg: bytes) -> Self: ...
|
||||
def good_instance_method_2(self, arg1: _S2, arg2: _S2) -> _S2: ...
|
||||
@classmethod
|
||||
def good_cls_method_1(cls: type[Self], arg: int) -> Self: ...
|
||||
@classmethod
|
||||
def good_cls_method_2(cls, arg1: _S, arg2: _S) -> _S: ...
|
||||
@staticmethod
|
||||
def static_method(arg1: _S) -> _S: ...
|
||||
|
||||
|
||||
# Python > 3.12
|
||||
class PEP695BadDunderNew[T]:
|
||||
def __new__[S](cls: type[S], *args: Any, ** kwargs: Any) -> S: ... # PYI019
|
||||
|
||||
|
||||
def generic_instance_method[S](self: S) -> S: ... # PYI019
|
||||
|
||||
|
||||
class PEP695GoodDunderNew[T]:
|
||||
def __new__(cls, *args: Any, **kwargs: Any) -> Self: ...
|
||||
@@ -1,46 +0,0 @@
|
||||
from typing import TypeVar, Self, Type
|
||||
|
||||
_S = TypeVar("_S", bound=BadClass)
|
||||
_S2 = TypeVar("_S2", BadClass, GoodClass)
|
||||
|
||||
class BadClass:
|
||||
def __new__(cls: type[_S], *args: str, **kwargs: int) -> _S: ... # PYI019
|
||||
|
||||
|
||||
def bad_instance_method(self: _S, arg: bytes) -> _S: ... # PYI019
|
||||
|
||||
|
||||
@classmethod
|
||||
def bad_class_method(cls: type[_S], arg: int) -> _S: ... # PYI019
|
||||
|
||||
|
||||
@classmethod
|
||||
def bad_posonly_class_method(cls: type[_S], /) -> _S: ... # PYI019
|
||||
|
||||
|
||||
@classmethod
|
||||
def excluded_edge_case(cls: Type[_S], arg: int) -> _S: ... # Ok
|
||||
|
||||
|
||||
class GoodClass:
|
||||
def __new__(cls: type[Self], *args: list[int], **kwargs: set[str]) -> Self: ...
|
||||
def good_instance_method_1(self: Self, arg: bytes) -> Self: ...
|
||||
def good_instance_method_2(self, arg1: _S2, arg2: _S2) -> _S2: ...
|
||||
@classmethod
|
||||
def good_cls_method_1(cls: type[Self], arg: int) -> Self: ...
|
||||
@classmethod
|
||||
def good_cls_method_2(cls, arg1: _S, arg2: _S) -> _S: ...
|
||||
@staticmethod
|
||||
def static_method(arg1: _S) -> _S: ...
|
||||
|
||||
|
||||
# Python > 3.12
|
||||
class PEP695BadDunderNew[T]:
|
||||
def __new__[S](cls: type[S], *args: Any, ** kwargs: Any) -> S: ... # PYI019
|
||||
|
||||
|
||||
def generic_instance_method[S](self: S) -> S: ... # PYI019
|
||||
|
||||
|
||||
class PEP695GoodDunderNew[T]:
|
||||
def __new__(cls, *args: Any, **kwargs: Any) -> Self: ...
|
||||
@@ -1,11 +1,9 @@
|
||||
import collections
|
||||
|
||||
person: collections.namedtuple # Y024 Use "typing.NamedTuple" instead of "collections.namedtuple"
|
||||
person: collections.namedtuple # OK
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
person: namedtuple # Y024 Use "typing.NamedTuple" instead of "collections.namedtuple"
|
||||
person: namedtuple # OK
|
||||
|
||||
person = namedtuple(
|
||||
"Person", ["name", "age"]
|
||||
) # Y024 Use "typing.NamedTuple" instead of "collections.namedtuple"
|
||||
person = namedtuple("Person", ["name", "age"]) # OK
|
||||
|
||||
@@ -1,38 +1,24 @@
|
||||
import typing
|
||||
import typing_extensions
|
||||
from typing import Literal
|
||||
# Shouldn't emit for any cases in the non-stub file for compatibility with flake8-pyi.
|
||||
# Note that this rule could be applied here in the future.
|
||||
|
||||
# Shouldn't affect non-union field types.
|
||||
field1: Literal[1] # OK
|
||||
field2: Literal[1] | Literal[2] # OK
|
||||
|
||||
# Should emit for duplicate field types.
|
||||
field2: Literal[1] | Literal[2] # Error
|
||||
|
||||
# Should emit for union types in arguments.
|
||||
def func1(arg1: Literal[1] | Literal[2]): # Error
|
||||
def func1(arg1: Literal[1] | Literal[2]): # OK
|
||||
print(arg1)
|
||||
|
||||
|
||||
# Should emit for unions in return types.
|
||||
def func2() -> Literal[1] | Literal[2]: # Error
|
||||
def func2() -> Literal[1] | Literal[2]: # OK
|
||||
return "my Literal[1]ing"
|
||||
|
||||
|
||||
# Should emit in longer unions, even if not directly adjacent.
|
||||
field3: Literal[1] | Literal[2] | str # Error
|
||||
field4: str | Literal[1] | Literal[2] # Error
|
||||
field5: Literal[1] | str | Literal[2] # Error
|
||||
field6: Literal[1] | bool | Literal[2] | str # Error
|
||||
|
||||
# Should emit for non-type unions.
|
||||
field7 = Literal[1] | Literal[2] # Error
|
||||
|
||||
# Should emit for parenthesized unions.
|
||||
field8: Literal[1] | (Literal[2] | str) # Error
|
||||
|
||||
# Should handle user parentheses when fixing.
|
||||
field9: Literal[1] | (Literal[2] | str) # Error
|
||||
field10: (Literal[1] | str) | Literal[2] # Error
|
||||
|
||||
# Should emit for union in generic parent type.
|
||||
field11: dict[Literal[1] | Literal[2], str] # Error
|
||||
field3: Literal[1] | Literal[2] | str # OK
|
||||
field4: str | Literal[1] | Literal[2] # OK
|
||||
field5: Literal[1] | str | Literal[2] # OK
|
||||
field6: Literal[1] | bool | Literal[2] | str # OK
|
||||
field7 = Literal[1] | Literal[2] # OK
|
||||
field8: Literal[1] | (Literal[2] | str) # OK
|
||||
field9: Literal[1] | (Literal[2] | str) # OK
|
||||
field10: (Literal[1] | str) | Literal[2] # OK
|
||||
field11: dict[Literal[1] | Literal[2], str] # OK
|
||||
|
||||
@@ -3,8 +3,8 @@ import typing
|
||||
|
||||
|
||||
class Bad:
|
||||
def __eq__(self, other: Any) -> bool: ... # Y032
|
||||
def __ne__(self, other: typing.Any) -> typing.Any: ... # Y032
|
||||
def __eq__(self, other: Any) -> bool: ... # Fine because not a stub file
|
||||
def __ne__(self, other: typing.Any) -> typing.Any: ... # Fine because not a stub file
|
||||
|
||||
|
||||
class Good:
|
||||
|
||||
@@ -9,16 +9,16 @@ from typing import (
|
||||
|
||||
just_literals_pipe_union: TypeAlias = (
|
||||
Literal[True] | Literal["idk"]
|
||||
) # PYI042, since not camel case
|
||||
) # not PYI042 (not a stubfile)
|
||||
PublicAliasT: TypeAlias = str | int
|
||||
PublicAliasT2: TypeAlias = Union[str, bytes]
|
||||
_ABCDEFGHIJKLMNOPQRST: TypeAlias = typing.Any
|
||||
_PrivateAliasS: TypeAlias = Literal["I", "guess", "this", "is", "okay"]
|
||||
_PrivateAliasS2: TypeAlias = Annotated[str, "also okay"]
|
||||
|
||||
snake_case_alias1: TypeAlias = str | int # PYI042, since not camel case
|
||||
_snake_case_alias2: TypeAlias = Literal["whatever"] # PYI042, since not camel case
|
||||
Snake_case_alias: TypeAlias = int | float # PYI042, since not camel case
|
||||
snake_case_alias1: TypeAlias = str | int # not PYI042 (not a stubfile)
|
||||
_snake_case_alias2: TypeAlias = Literal["whatever"] # not PYI042 (not a stubfile)
|
||||
Snake_case_alias: TypeAlias = int | float # not PYI042 (not a stubfile)
|
||||
|
||||
# check that this edge case doesn't crash
|
||||
_: TypeAlias = str | int
|
||||
|
||||
@@ -7,11 +7,11 @@ from typing import (
|
||||
Literal,
|
||||
)
|
||||
|
||||
_PrivateAliasT: TypeAlias = str | int # PYI043, since this ends in a T
|
||||
_PrivateAliasT2: TypeAlias = typing.Any # PYI043, since this ends in a T
|
||||
_PrivateAliasT: TypeAlias = str | int # not PYI043 (not a stubfile)
|
||||
_PrivateAliasT2: TypeAlias = typing.Any # not PYI043 (not a stubfile)
|
||||
_PrivateAliasT3: TypeAlias = Literal[
|
||||
"not", "a", "chance"
|
||||
] # PYI043, since this ends in a T
|
||||
] # not PYI043 (not a stubfile)
|
||||
just_literals_pipe_union: TypeAlias = Literal[True] | Literal["idk"]
|
||||
PublicAliasT: TypeAlias = str | int
|
||||
PublicAliasT2: TypeAlias = Union[str, bytes]
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import typing
|
||||
import sys
|
||||
from typing import TypeAlias
|
||||
|
||||
|
||||
_UnusedPrivateTypeAlias: TypeAlias = int | None
|
||||
_T: typing.TypeAlias = str
|
||||
|
||||
# OK
|
||||
_UsedPrivateTypeAlias: TypeAlias = int | None
|
||||
|
||||
def func(arg: _UsedPrivateTypeAlias) -> _UsedPrivateTypeAlias:
|
||||
...
|
||||
|
||||
|
||||
if sys.version_info > (3, 9):
|
||||
_PrivateTypeAlias: TypeAlias = str | None
|
||||
else:
|
||||
_PrivateTypeAlias: TypeAlias = float | None
|
||||
|
||||
|
||||
def func2(arg: _PrivateTypeAlias) -> None: ...
|
||||
@@ -1,22 +0,0 @@
|
||||
import typing
|
||||
import sys
|
||||
from typing import TypeAlias
|
||||
|
||||
|
||||
_UnusedPrivateTypeAlias: TypeAlias = int | None
|
||||
_T: typing.TypeAlias = str
|
||||
|
||||
# OK
|
||||
_UsedPrivateTypeAlias: TypeAlias = int | None
|
||||
|
||||
def func(arg: _UsedPrivateTypeAlias) -> _UsedPrivateTypeAlias:
|
||||
...
|
||||
|
||||
|
||||
if sys.version_info > (3, 9):
|
||||
_PrivateTypeAlias: TypeAlias = str | None
|
||||
else:
|
||||
_PrivateTypeAlias: TypeAlias = float | None
|
||||
|
||||
|
||||
def func2(arg: _PrivateTypeAlias) -> None: ...
|
||||
@@ -1,18 +0,0 @@
|
||||
import typing
|
||||
from typing import TypedDict
|
||||
|
||||
|
||||
class _UnusedTypedDict(TypedDict):
|
||||
foo: str
|
||||
|
||||
|
||||
class _UnusedTypedDict2(typing.TypedDict):
|
||||
bar: int
|
||||
|
||||
|
||||
class _UsedTypedDict(TypedDict):
|
||||
foo: bytes
|
||||
|
||||
|
||||
class _CustomClass(_UsedTypedDict):
|
||||
bar: list[int]
|
||||
@@ -1,32 +0,0 @@
|
||||
import sys
|
||||
import typing
|
||||
from typing import TypedDict
|
||||
|
||||
|
||||
class _UnusedTypedDict(TypedDict):
|
||||
foo: str
|
||||
|
||||
|
||||
class _UnusedTypedDict2(typing.TypedDict):
|
||||
bar: int
|
||||
|
||||
|
||||
# OK
|
||||
class _UsedTypedDict(TypedDict):
|
||||
foo: bytes
|
||||
|
||||
|
||||
class _CustomClass(_UsedTypedDict):
|
||||
bar: list[int]
|
||||
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
class _UsedTypedDict2(TypedDict):
|
||||
foo: int
|
||||
else:
|
||||
class _UsedTypedDict2(TypedDict):
|
||||
foo: float
|
||||
|
||||
|
||||
class _CustomClass2(_UsedTypedDict2):
|
||||
bar: list[int]
|
||||
@@ -1,17 +0,0 @@
|
||||
import typing
|
||||
from typing import Literal, TypeAlias, Union
|
||||
|
||||
A: str | Literal["foo"]
|
||||
B: TypeAlias = typing.Union[Literal[b"bar", b"foo"], bytes, str]
|
||||
C: TypeAlias = typing.Union[Literal[5], int, typing.Union[Literal["foo"], str]]
|
||||
D: TypeAlias = typing.Union[Literal[b"str_bytes", 42], bytes, int]
|
||||
|
||||
def func(x: complex | Literal[1J], y: Union[Literal[3.14], float]): ...
|
||||
|
||||
# OK
|
||||
A: Literal["foo"]
|
||||
B: TypeAlias = Literal[b"bar", b"foo"]
|
||||
C: TypeAlias = typing.Union[Literal[5], Literal["foo"]]
|
||||
D: TypeAlias = Literal[b"str_bytes", 42]
|
||||
|
||||
def func(x: Literal[1J], y: Literal[3.14]): ...
|
||||
@@ -1,17 +0,0 @@
|
||||
import typing
|
||||
from typing import Literal, TypeAlias, Union
|
||||
|
||||
A: str | Literal["foo"]
|
||||
B: TypeAlias = typing.Union[Literal[b"bar", b"foo"], bytes, str]
|
||||
C: TypeAlias = typing.Union[Literal[5], int, typing.Union[Literal["foo"], str]]
|
||||
D: TypeAlias = typing.Union[Literal[b"str_bytes", 42], bytes, int]
|
||||
|
||||
def func(x: complex | Literal[1J], y: Union[Literal[3.14], float]): ...
|
||||
|
||||
# OK
|
||||
A: Literal["foo"]
|
||||
B: TypeAlias = Literal[b"bar", b"foo"]
|
||||
C: TypeAlias = typing.Union[Literal[5], Literal["foo"]]
|
||||
D: TypeAlias = Literal[b"str_bytes", 42]
|
||||
|
||||
def func(x: Literal[1J], y: Literal[3.14]): ...
|
||||
@@ -1,31 +0,0 @@
|
||||
import builtins
|
||||
from typing import Union
|
||||
|
||||
w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
|
||||
x: type[int] | type[str] | type[float]
|
||||
y: builtins.type[int] | type[str] | builtins.type[complex]
|
||||
z: Union[type[float], type[complex]]
|
||||
z: Union[type[float, int], type[complex]]
|
||||
|
||||
|
||||
def func(arg: type[int] | str | type[float]) -> None:
|
||||
...
|
||||
|
||||
|
||||
# OK
|
||||
x: type[int, str, float]
|
||||
y: builtins.type[int, str, complex]
|
||||
z: Union[float, complex]
|
||||
|
||||
|
||||
def func(arg: type[int, float] | str) -> None:
|
||||
...
|
||||
|
||||
|
||||
# OK
|
||||
item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
|
||||
|
||||
|
||||
def func():
|
||||
# PYI055
|
||||
item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
|
||||
@@ -1,24 +0,0 @@
|
||||
import builtins
|
||||
from typing import Union
|
||||
|
||||
w: builtins.type[int] | builtins.type[str] | builtins.type[complex]
|
||||
x: type[int] | type[str] | type[float]
|
||||
y: builtins.type[int] | type[str] | builtins.type[complex]
|
||||
z: Union[type[float], type[complex]]
|
||||
z: Union[type[float, int], type[complex]]
|
||||
|
||||
def func(arg: type[int] | str | type[float]) -> None: ...
|
||||
|
||||
# OK
|
||||
x: type[int, str, float]
|
||||
y: builtins.type[int, str, complex]
|
||||
z: Union[float, complex]
|
||||
|
||||
def func(arg: type[int, float] | str) -> None: ...
|
||||
|
||||
# OK
|
||||
item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
|
||||
|
||||
def func():
|
||||
# PYI055
|
||||
item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker
|
||||
@@ -1,15 +1,7 @@
|
||||
import contextlib
|
||||
import pathlib
|
||||
import pathlib as pl
|
||||
from pathlib import Path
|
||||
from pathlib import Path as P
|
||||
|
||||
# SIM115
|
||||
f = open("foo.txt")
|
||||
f = Path("foo.txt").open()
|
||||
f = pathlib.Path("foo.txt").open()
|
||||
f = pl.Path("foo.txt").open()
|
||||
f = P("foo.txt").open()
|
||||
data = f.read()
|
||||
f.close()
|
||||
|
||||
|
||||
@@ -30,13 +30,3 @@ for key in list(obj.keys()):
|
||||
(k for k in obj.keys()) # SIM118
|
||||
|
||||
key in (obj or {}).keys() # SIM118
|
||||
|
||||
from typing import KeysView
|
||||
|
||||
|
||||
class Foo:
|
||||
def keys(self) -> KeysView[object]:
|
||||
...
|
||||
|
||||
def __contains__(self, key: object) -> bool:
|
||||
return key in self.keys() # OK
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
"""Regression test: ensure that we don't treat the export entry as a typing-only reference."""
|
||||
from __future__ import annotations
|
||||
|
||||
from logging import getLogger
|
||||
|
||||
__all__ = ("getLogger",)
|
||||
|
||||
|
||||
def foo() -> None:
|
||||
pass
|
||||
@@ -18,5 +18,3 @@ file_name.split(os.sep)
|
||||
|
||||
# OK
|
||||
"foo/bar/".split("/")
|
||||
"foo/bar/".split(os.sep, 1)
|
||||
"foo/bar/".split(1, sep=os.sep)
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
import bar
|
||||
import foo
|
||||
@@ -1,2 +0,0 @@
|
||||
import foo
|
||||
import bar
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"execution_count": null,
|
||||
"cell_type": "code",
|
||||
"id": "1",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": ["%%timeit\n", "print('hello world')"]
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "eab4754a-d6df-4b41-8ee8-7e23aef440f9",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import math\n",
|
||||
"\n",
|
||||
"%matplotlib inline\n",
|
||||
"\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"_ = math.pi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "2b0e2986-1b87-4bb6-9b1d-c11ca1decd87",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%%timeit\n",
|
||||
"import sys"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python (ruff)",
|
||||
"language": "python",
|
||||
"name": "ruff"
|
||||
},
|
||||
"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
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "cad32845-44f9-4a53-8b8c-a6b1bb3f3378",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import math\n",
|
||||
"\n",
|
||||
"%matplotlib inline\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"_ = math.pi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "d7b8e967-8b4a-493b-b6f7-d5cecfb3a5c3",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%%timeit\n",
|
||||
"import sys"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python (ruff)",
|
||||
"language": "python",
|
||||
"name": "ruff"
|
||||
},
|
||||
"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
|
||||
}
|
||||
@@ -25,23 +25,6 @@
|
||||
"def foo():\n",
|
||||
" pass"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "16214f6f-bb32-4594-81be-79fb27c6ec92",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from pathlib import Path\n",
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"%matplotlib \\\n",
|
||||
" --inline\n",
|
||||
"\n",
|
||||
"import math\n",
|
||||
"import abc"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
|
||||
@@ -27,23 +27,6 @@
|
||||
"def foo():\n",
|
||||
" pass"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "6d6c55c6-4a34-4662-914b-4ee11c9c24a5",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sys\n",
|
||||
"from pathlib import Path\n",
|
||||
"\n",
|
||||
"%matplotlib \\\n",
|
||||
" --inline\n",
|
||||
"\n",
|
||||
"import abc\n",
|
||||
"import math"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "a0efffbc-85f1-4513-bf49-5387ec3a2a4e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def f():\n",
|
||||
" foo1 = %matplotlib --list\n",
|
||||
" foo2: list[str] = %matplotlib --list"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "6e0b2b50-43f2-4f59-951d-9404dd560ae4",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def f():\n",
|
||||
" bar1 = !pwd\n",
|
||||
" bar2: str = !pwd"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python (ruff)",
|
||||
"language": "python",
|
||||
"name": "ruff"
|
||||
},
|
||||
"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
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "24426ef2-046c-453e-b809-05b56e7355e0",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def f():\n",
|
||||
" %matplotlib --list\n",
|
||||
" %matplotlib --list"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "3d98fdae-b86b-476e-b4db-9d3ce5562682",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def f():\n",
|
||||
" !pwd\n",
|
||||
" !pwd"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python (ruff)",
|
||||
"language": "python",
|
||||
"name": "ruff"
|
||||
},
|
||||
"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
|
||||
}
|
||||
@@ -1,23 +1,28 @@
|
||||
# PERF203
|
||||
for i in range(10):
|
||||
try:
|
||||
try: # PERF203
|
||||
print(f"{i}")
|
||||
except:
|
||||
print("error")
|
||||
|
||||
# OK
|
||||
try:
|
||||
for i in range(10):
|
||||
print(f"{i}")
|
||||
except:
|
||||
print("error")
|
||||
|
||||
# OK
|
||||
i = 0
|
||||
while i < 10:
|
||||
while i < 10: # PERF203
|
||||
try:
|
||||
print(f"{i}")
|
||||
except:
|
||||
print("error")
|
||||
|
||||
i += 1
|
||||
|
||||
try:
|
||||
i = 0
|
||||
while i < 10:
|
||||
print(f"{i}")
|
||||
i += 1
|
||||
except:
|
||||
print("error")
|
||||
|
||||
@@ -45,18 +45,3 @@ def f():
|
||||
for i in items:
|
||||
if i not in result:
|
||||
result.append(i) # OK
|
||||
|
||||
|
||||
def f():
|
||||
fibonacci = [0, 1]
|
||||
for i in range(20):
|
||||
fibonacci.append(sum(fibonacci[-2:])) # OK
|
||||
print(fibonacci)
|
||||
|
||||
|
||||
def f():
|
||||
foo = object()
|
||||
foo.fibonacci = [0, 1]
|
||||
for i in range(20):
|
||||
foo.fibonacci.append(sum(foo.fibonacci[-2:])) # OK
|
||||
print(foo.fibonacci)
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
#: E241
|
||||
a = (1, 2)
|
||||
#: Okay
|
||||
b = (1, 20)
|
||||
#: E242
|
||||
a = (1, 2) # tab before 2
|
||||
#: Okay
|
||||
b = (1, 20) # space before 20
|
||||
#: E241 E241 E241
|
||||
# issue 135
|
||||
more_spaces = [a, b,
|
||||
ef, +h,
|
||||
c, -d]
|
||||
@@ -66,6 +66,3 @@ while 1:
|
||||
#: E703:2:1
|
||||
0\
|
||||
;
|
||||
#: E701:2:3
|
||||
a = \
|
||||
5;
|
||||
|
||||
@@ -58,6 +58,3 @@ assert type(res) == type(None)
|
||||
types = StrEnum
|
||||
if x == types.X:
|
||||
pass
|
||||
|
||||
#: E721
|
||||
assert type(res) is int
|
||||
|
||||
@@ -634,8 +634,3 @@ def starts_with_this():
|
||||
@expect('D404: First word of the docstring should not be "This"')
|
||||
def starts_with_space_then_this():
|
||||
""" This is a docstring that starts with a space.""" # noqa: D210
|
||||
|
||||
|
||||
class SameLine: """This is a docstring on the same line"""
|
||||
|
||||
def same_line(): """This is a docstring on the same line"""
|
||||
|
||||
@@ -92,10 +92,3 @@ match *0, 1, *2:
|
||||
case 0,:
|
||||
import x
|
||||
import y
|
||||
|
||||
|
||||
# Test: access a sub-importation via an alias.
|
||||
import foo.bar as bop
|
||||
import foo.bar.baz
|
||||
|
||||
print(bop.baz.read_csv("test.csv"))
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
"""Test type parameters and aliases"""
|
||||
|
||||
# Type parameters in type alias statements
|
||||
|
||||
from some_module import Bar
|
||||
|
||||
type Foo[T] = T # OK
|
||||
type Foo[T] = list[T] # OK
|
||||
type Foo[T: ForwardA] = T # OK
|
||||
type Foo[*Ts] = Bar[Ts] # OK
|
||||
type Foo[**P] = Bar[P] # OK
|
||||
class ForwardA: ...
|
||||
|
||||
# Types used in aliased assignment must exist
|
||||
|
||||
type Foo = DoesNotExist # F821: Undefined name `DoesNotExist`
|
||||
type Foo = list[DoesNotExist] # F821: Undefined name `DoesNotExist`
|
||||
|
||||
# Type parameters do not escape alias scopes
|
||||
|
||||
type Foo[T] = T
|
||||
T # F821: Undefined name `T` - not accessible afterward alias scope
|
||||
|
||||
# Type parameters in functions
|
||||
|
||||
def foo[T](t: T) -> T: return t # OK
|
||||
async def afoo[T](t: T) -> T: return t # OK
|
||||
def with_forward_ref[T: ForwardB](t: T) -> T: return t # OK
|
||||
def can_access_inside[T](t: T) -> T: # OK
|
||||
print(T) # OK
|
||||
return t # OK
|
||||
class ForwardB: ...
|
||||
|
||||
|
||||
# Type parameters do not escape function scopes
|
||||
|
||||
from some_library import some_decorator
|
||||
|
||||
@some_decorator(T) # F821: Undefined name `T` - not accessible in decorators
|
||||
|
||||
def foo[T](t: T) -> None: ...
|
||||
T # F821: Undefined name `T` - not accessible afterward function scope
|
||||
|
||||
|
||||
# Type parameters in classes
|
||||
|
||||
class Foo[T](list[T]): ... # OK
|
||||
class UsesForward[T: ForwardC](list[T]): ... # OK
|
||||
class ForwardC: ...
|
||||
class WithinBody[T](list[T]): # OK
|
||||
t = T # OK
|
||||
x: T # OK
|
||||
|
||||
def foo(self, x: T) -> T: # OK
|
||||
return x
|
||||
|
||||
def foo(self):
|
||||
T # OK
|
||||
|
||||
|
||||
# Type parameters do not escape class scopes
|
||||
|
||||
from some_library import some_decorator
|
||||
@some_decorator(T) # F821: Undefined name `T` - not accessible in decorators
|
||||
|
||||
class Foo[T](list[T]): ...
|
||||
T # F821: Undefined name `T` - not accessible after class scope
|
||||
|
||||
# Types specified in bounds should exist
|
||||
|
||||
type Foo[T: DoesNotExist] = T # F821: Undefined name `DoesNotExist`
|
||||
def foo[T: DoesNotExist](t: T) -> T: return t # F821: Undefined name `DoesNotExist`
|
||||
class Foo[T: DoesNotExist](list[T]): ... # F821: Undefined name `DoesNotExist`
|
||||
|
||||
type Foo[T: (DoesNotExist1, DoesNotExist2)] = T # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
||||
def foo[T: (DoesNotExist1, DoesNotExist2)](t: T) -> T: return t # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
||||
class Foo[T: (DoesNotExist1, DoesNotExist2)](list[T]): ... # F821: Undefined name `DoesNotExist1`, Undefined name `DoesNotExist2`
|
||||
|
||||
# Type parameters in nested classes
|
||||
|
||||
class Parent[T]:
|
||||
t = T # OK
|
||||
|
||||
def can_use_class_variable(self, x: t) -> t: # OK
|
||||
return x
|
||||
|
||||
class Child:
|
||||
def can_access_parent_type_parameter(self, x: T) -> T: # OK
|
||||
T # OK
|
||||
return x
|
||||
|
||||
def cannot_access_parent_variable(self, x: t) -> t: # F821: Undefined name `T`
|
||||
t # F821: Undefined name `t`
|
||||
return x
|
||||
|
||||
# Type parameters in nested functions
|
||||
|
||||
def can_access_inside_nested[T](t: T) -> T: # OK
|
||||
def bar(x: T) -> T: # OK
|
||||
T # OK
|
||||
return x
|
||||
|
||||
bar(t)
|
||||
@@ -63,20 +63,3 @@ def main():
|
||||
|
||||
for sys in range(5):
|
||||
pass
|
||||
|
||||
|
||||
import requests_mock as rm
|
||||
|
||||
|
||||
def requests_mock(requests_mock: rm.Mocker):
|
||||
print(rm.ANY)
|
||||
|
||||
|
||||
import sklearn.base
|
||||
import mlflow.sklearn
|
||||
|
||||
|
||||
def f():
|
||||
import sklearn
|
||||
|
||||
mlflow
|
||||
|
||||
@@ -147,10 +147,3 @@ def f() -> None:
|
||||
global CONSTANT
|
||||
CONSTANT = 1
|
||||
CONSTANT = 2
|
||||
|
||||
|
||||
def f() -> None:
|
||||
try:
|
||||
print("hello")
|
||||
except A as e :
|
||||
print("oh no!")
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
# pylint: disable=missing-docstring,consider-using-f-string, pointless-statement
|
||||
|
||||
## Old style formatting
|
||||
|
||||
"%s %z" % ("hello", "world") # [bad-format-character]
|
||||
|
||||
"%s" "%z" % ("hello", "world") # [bad-format-character]
|
||||
|
||||
"""%s %z""" % ("hello", "world") # [bad-format-character]
|
||||
|
||||
"""%s""" """%z""" % ("hello", "world") # [bad-format-character]
|
||||
|
||||
## New style formatting
|
||||
|
||||
"{:s} {:y}".format("hello", "world") # [bad-format-character]
|
||||
|
||||
"{:*^30s}".format("centered")
|
||||
|
||||
## f-strings
|
||||
|
||||
H, W = "hello", "world"
|
||||
f"{H} {W}"
|
||||
f"{H:s} {W:z}" # [bad-format-character]
|
||||
|
||||
f"{1:z}" # [bad-format-character]
|
||||
|
||||
## False negatives
|
||||
|
||||
print(("%" "z") % 1)
|
||||
@@ -19,10 +19,6 @@ foo in foo
|
||||
|
||||
foo not in foo
|
||||
|
||||
id(foo) == id(foo)
|
||||
|
||||
len(foo) == len(foo)
|
||||
|
||||
# Non-errors.
|
||||
"foo" == "foo" # This is flagged by `comparison-of-constant` instead.
|
||||
|
||||
@@ -47,11 +43,3 @@ foo is not bar
|
||||
foo in bar
|
||||
|
||||
foo not in bar
|
||||
|
||||
x(foo) == y(foo)
|
||||
|
||||
id(foo) == id(bar)
|
||||
|
||||
id(foo, bar) == id(foo, bar)
|
||||
|
||||
id(foo, bar=1) == id(foo, bar=1)
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
class Person:
|
||||
def __init__(self):
|
||||
self.name = "monty"
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, Person) and other.name == self.name
|
||||
|
||||
class Language:
|
||||
def __init__(self):
|
||||
self.name = "python"
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, Language) and other.name == self.name
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.name)
|
||||
@@ -40,4 +40,3 @@ __all__ = __all__ + ["Hello"]
|
||||
|
||||
__all__ = __all__ + multiprocessing.__all__
|
||||
|
||||
__all__ = list[str](["Hello", "world"])
|
||||
|
||||
@@ -19,10 +19,6 @@ logging.error("Example log %s, %s", "foo", "bar", "baz", **kwargs)
|
||||
# do not handle keyword arguments
|
||||
logging.error("%(objects)d modifications: %(modifications)d errors: %(errors)d")
|
||||
|
||||
logging.info(msg="Hello %s")
|
||||
|
||||
logging.info(msg="Hello %s %s")
|
||||
|
||||
import warning
|
||||
|
||||
warning.warning("Hello %s %s", "World!")
|
||||
|
||||
@@ -15,10 +15,6 @@ logging.error("Example log %s, %s", "foo", "bar", "baz", **kwargs)
|
||||
# do not handle keyword arguments
|
||||
logging.error("%(objects)d modifications: %(modifications)d errors: %(errors)d", {"objects": 1, "modifications": 1, "errors": 1})
|
||||
|
||||
logging.info(msg="Hello")
|
||||
|
||||
logging.info(msg="Hello", something="else")
|
||||
|
||||
import warning
|
||||
|
||||
warning.warning("Hello %s", "World!", "again")
|
||||
|
||||
@@ -32,30 +32,3 @@ print(
|
||||
)
|
||||
|
||||
'{' '0}'.format(1)
|
||||
|
||||
args = list(range(10))
|
||||
kwargs = {x: x for x in range(10)}
|
||||
|
||||
"{0}".format(*args)
|
||||
|
||||
"{0}".format(**kwargs)
|
||||
|
||||
"{0}_{1}".format(*args)
|
||||
|
||||
"{0}_{1}".format(1, *args)
|
||||
|
||||
"{0}_{1}".format(1, 2, *args)
|
||||
|
||||
"{0}_{1}".format(*args, 1, 2)
|
||||
|
||||
"{0}_{1}_{2}".format(1, **kwargs)
|
||||
|
||||
"{0}_{1}_{2}".format(1, 2, **kwargs)
|
||||
|
||||
"{0}_{1}_{2}".format(1, 2, 3, **kwargs)
|
||||
|
||||
"{0}_{1}_{2}".format(1, 2, 3, *args, **kwargs)
|
||||
|
||||
"{1}_{0}".format(1, 2, *args)
|
||||
|
||||
"{1}_{0}".format(1, 2)
|
||||
|
||||
@@ -15,17 +15,3 @@ f"{0}".format(1)
|
||||
print(f"{0}".format(1))
|
||||
|
||||
''.format(1)
|
||||
|
||||
'{1} {0}'.format(*args)
|
||||
|
||||
"{1}_{0}".format(*args, 1)
|
||||
|
||||
"{1}_{0}".format(*args, 1, 2)
|
||||
|
||||
"{1}_{0}".format(1, **kwargs)
|
||||
|
||||
"{1}_{0}".format(1, foo=2)
|
||||
|
||||
"{1}_{0}".format(1, 2, **kwargs)
|
||||
|
||||
"{1}_{0}".format(1, 2, foo=3, bar=4)
|
||||
|
||||
28
crates/ruff/resources/test/fixtures/pyupgrade/UP030_2.py
vendored
Normal file
28
crates/ruff/resources/test/fixtures/pyupgrade/UP030_2.py
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# These SHOULD change
|
||||
|
||||
args = list(range(10))
|
||||
kwargs = {x: x for x in range(10)}
|
||||
|
||||
"{0}".format(*args)
|
||||
|
||||
"{0}".format(**kwargs)
|
||||
|
||||
"{0}_{1}".format(*args)
|
||||
|
||||
"{0}_{1}".format(1, *args)
|
||||
|
||||
"{1}_{0}".format(*args)
|
||||
|
||||
"{1}_{0}".format(1, *args)
|
||||
|
||||
"{0}_{1}".format(1, 2, *args)
|
||||
|
||||
"{0}_{1}".format(*args, 1, 2)
|
||||
|
||||
"{0}_{1}_{2}".format(1, **kwargs)
|
||||
|
||||
"{0}_{1}_{2}".format(1, 2, **kwargs)
|
||||
|
||||
"{0}_{1}_{2}".format(1, 2, 3, **kwargs)
|
||||
|
||||
"{0}_{1}_{2}".format(1, 2, 3, *args, **kwargs)
|
||||
@@ -106,7 +106,3 @@ print('Hello %(arg)s' % bar['bop'])
|
||||
"""
|
||||
% (x,)
|
||||
)
|
||||
|
||||
"%s" % (
|
||||
x, # comment
|
||||
)
|
||||
|
||||
@@ -6,12 +6,8 @@
|
||||
|
||||
"{1} {0}".format(a, b)
|
||||
|
||||
"{0} {1} {0}".format(a, b)
|
||||
|
||||
"{x.y}".format(x=z)
|
||||
|
||||
"{x} {y} {x}".format(x=a, y=b)
|
||||
|
||||
"{.x} {.y}".format(a, b)
|
||||
|
||||
"{} {}".format(a.b, c.d)
|
||||
@@ -76,58 +72,6 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
111111
|
||||
)
|
||||
|
||||
"{a}" "{b}".format(a=1, b=1)
|
||||
|
||||
(
|
||||
"{a}"
|
||||
"{b}"
|
||||
).format(a=1, b=1)
|
||||
|
||||
(
|
||||
"{a}"
|
||||
""
|
||||
"{b}"
|
||||
""
|
||||
).format(a=1, b=1)
|
||||
|
||||
(
|
||||
(
|
||||
# comment
|
||||
"{a}"
|
||||
# comment
|
||||
"{b}"
|
||||
)
|
||||
# comment
|
||||
.format(a=1, b=1)
|
||||
)
|
||||
|
||||
(
|
||||
"{a}"
|
||||
"b"
|
||||
).format(a=1)
|
||||
|
||||
|
||||
def d(osname, version, release):
|
||||
return"{}-{}.{}".format(osname, version, release)
|
||||
|
||||
|
||||
def e():
|
||||
yield"{}".format(1)
|
||||
|
||||
|
||||
assert"{}".format(1)
|
||||
|
||||
|
||||
async def c():
|
||||
return "{}".format(await 3)
|
||||
|
||||
|
||||
async def c():
|
||||
return "{}".format(1 + await 3)
|
||||
|
||||
|
||||
"{}".format(1 * 2)
|
||||
|
||||
###
|
||||
# Non-errors
|
||||
###
|
||||
@@ -141,6 +85,8 @@ async def c():
|
||||
|
||||
"{} {}".format(*a)
|
||||
|
||||
"{0} {0}".format(arg)
|
||||
|
||||
"{x} {x}".format(arg)
|
||||
|
||||
"{x.y} {x.z}".format(arg)
|
||||
@@ -157,6 +103,8 @@ b"{} {}".format(a, b)
|
||||
|
||||
r'"\N{snowman} {}".format(a)'
|
||||
|
||||
"{a}" "{b}".format(a=1, b=1)
|
||||
|
||||
"123456789 {}".format(
|
||||
11111111111111111111111111111111111111111111111111111111111111111111111111,
|
||||
)
|
||||
@@ -192,13 +140,20 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
]
|
||||
)
|
||||
|
||||
(
|
||||
"{a}"
|
||||
"{1 + 2}"
|
||||
).format(a=1)
|
||||
async def c():
|
||||
return "{}".format(await 3)
|
||||
|
||||
"{}".format(**c)
|
||||
|
||||
"{}".format(
|
||||
1 # comment
|
||||
)
|
||||
async def c():
|
||||
return "{}".format(1 + await 3)
|
||||
|
||||
|
||||
def d(osname, version, release):
|
||||
return"{}-{}.{}".format(osname, version, release)
|
||||
|
||||
|
||||
def e():
|
||||
yield"{}".format(1)
|
||||
|
||||
|
||||
assert"{}".format(1)
|
||||
|
||||
@@ -46,35 +46,6 @@ from typing import Callable, Match, Pattern, List, OrderedDict, AbstractSet, Con
|
||||
if True: from collections import (
|
||||
Mapping, Counter)
|
||||
|
||||
# Bad imports from PYI027 that are now handled by PYI022 (UP035)
|
||||
from typing import ContextManager
|
||||
from typing import OrderedDict
|
||||
from typing_extensions import OrderedDict
|
||||
from typing import Callable
|
||||
from typing import ByteString
|
||||
from typing import Container
|
||||
from typing import Hashable
|
||||
from typing import ItemsView
|
||||
from typing import Iterable
|
||||
from typing import Iterator
|
||||
from typing import KeysView
|
||||
from typing import Mapping
|
||||
from typing import MappingView
|
||||
from typing import MutableMapping
|
||||
from typing import MutableSequence
|
||||
from typing import MutableSet
|
||||
from typing import Sequence
|
||||
from typing import Sized
|
||||
from typing import ValuesView
|
||||
from typing import Awaitable
|
||||
from typing import AsyncIterator
|
||||
from typing import AsyncIterable
|
||||
from typing import Coroutine
|
||||
from typing import Collection
|
||||
from typing import AsyncGenerator
|
||||
from typing import Reversible
|
||||
from typing import Generator
|
||||
|
||||
# OK
|
||||
from a import b
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"""A mirror of UP037_1.py, with `from __future__ import annotations`."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import (
|
||||
108
crates/ruff/resources/test/fixtures/pyupgrade/UP037_1.py
vendored
Normal file
108
crates/ruff/resources/test/fixtures/pyupgrade/UP037_1.py
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
"""A mirror of UP037_0.py, without `from __future__ import annotations`."""
|
||||
|
||||
from typing import (
|
||||
Annotated,
|
||||
Callable,
|
||||
List,
|
||||
Literal,
|
||||
NamedTuple,
|
||||
Tuple,
|
||||
TypeVar,
|
||||
TypedDict,
|
||||
cast,
|
||||
)
|
||||
|
||||
from mypy_extensions import Arg, DefaultArg, DefaultNamedArg, NamedArg, VarArg
|
||||
|
||||
|
||||
def foo(var: "MyClass") -> "MyClass":
|
||||
x: "MyClass"
|
||||
|
||||
|
||||
def foo(*, inplace: "bool"):
|
||||
pass
|
||||
|
||||
|
||||
def foo(*args: "str", **kwargs: "int"):
|
||||
pass
|
||||
|
||||
|
||||
x: Tuple["MyClass"]
|
||||
|
||||
x: Callable[["MyClass"], None]
|
||||
|
||||
|
||||
class Foo(NamedTuple):
|
||||
x: "MyClass"
|
||||
|
||||
|
||||
class D(TypedDict):
|
||||
E: TypedDict("E", foo="int", total=False)
|
||||
|
||||
|
||||
class D(TypedDict):
|
||||
E: TypedDict("E", {"foo": "int"})
|
||||
|
||||
|
||||
x: Annotated["str", "metadata"]
|
||||
|
||||
x: Arg("str", "name")
|
||||
|
||||
x: DefaultArg("str", "name")
|
||||
|
||||
x: NamedArg("str", "name")
|
||||
|
||||
x: DefaultNamedArg("str", "name")
|
||||
|
||||
x: DefaultNamedArg("str", name="name")
|
||||
|
||||
x: VarArg("str")
|
||||
|
||||
x: List[List[List["MyClass"]]]
|
||||
|
||||
x: NamedTuple("X", [("foo", "int"), ("bar", "str")])
|
||||
|
||||
x: NamedTuple("X", fields=[("foo", "int"), ("bar", "str")])
|
||||
|
||||
x: NamedTuple(typename="X", fields=[("foo", "int")])
|
||||
|
||||
X: MyCallable("X")
|
||||
|
||||
|
||||
# OK
|
||||
class D(TypedDict):
|
||||
E: TypedDict("E")
|
||||
|
||||
|
||||
x: Annotated[()]
|
||||
|
||||
x: DefaultNamedArg(name="name", quox="str")
|
||||
|
||||
x: DefaultNamedArg(name="name")
|
||||
|
||||
x: NamedTuple("X", [("foo",), ("bar",)])
|
||||
|
||||
x: NamedTuple("X", ["foo", "bar"])
|
||||
|
||||
x: NamedTuple()
|
||||
|
||||
x: Literal["foo", "bar"]
|
||||
|
||||
x = cast(x, "str")
|
||||
|
||||
|
||||
def foo(x, *args, **kwargs):
|
||||
...
|
||||
|
||||
|
||||
def foo(*, inplace):
|
||||
...
|
||||
|
||||
|
||||
x: Annotated[1:2] = ...
|
||||
|
||||
x = TypeVar("x", "str", "int")
|
||||
|
||||
x = cast("str", x)
|
||||
|
||||
X = List["MyClass"]
|
||||
@@ -1,47 +0,0 @@
|
||||
import typing
|
||||
from typing import TypeAlias
|
||||
|
||||
# UP040
|
||||
x: typing.TypeAlias = int
|
||||
x: TypeAlias = int
|
||||
|
||||
# UP040 simple generic
|
||||
T = typing.TypeVar["T"]
|
||||
x: typing.TypeAlias = list[T]
|
||||
|
||||
# UP040 call style generic
|
||||
T = typing.TypeVar("T")
|
||||
x: typing.TypeAlias = list[T]
|
||||
|
||||
# UP040 bounded generic (todo)
|
||||
T = typing.TypeVar("T", bound=int)
|
||||
x: typing.TypeAlias = list[T]
|
||||
|
||||
T = typing.TypeVar("T", int, str)
|
||||
x: typing.TypeAlias = list[T]
|
||||
|
||||
# UP040 contravariant generic (todo)
|
||||
T = typing.TypeVar("T", contravariant=True)
|
||||
x: typing.TypeAlias = list[T]
|
||||
|
||||
# UP040 covariant generic (todo)
|
||||
T = typing.TypeVar("T", covariant=True)
|
||||
x: typing.TypeAlias = list[T]
|
||||
|
||||
# UP040 in class scope
|
||||
T = typing.TypeVar["T"]
|
||||
class Foo:
|
||||
# reference to global variable
|
||||
x: typing.TypeAlias = list[T]
|
||||
|
||||
# reference to class variable
|
||||
TCLS = typing.TypeVar["TCLS"]
|
||||
y: typing.TypeAlias = list[TCLS]
|
||||
|
||||
# UP040 wont add generics in fix
|
||||
T = typing.TypeVar(*args)
|
||||
x: typing.TypeAlias = list[T]
|
||||
|
||||
# OK
|
||||
x: TypeAlias
|
||||
x: int = 1
|
||||
@@ -11,8 +11,6 @@ class A:
|
||||
without_annotation = []
|
||||
class_variable: ClassVar[list[int]] = []
|
||||
final_variable: Final[list[int]] = []
|
||||
class_variable_without_subscript: ClassVar = []
|
||||
final_variable_without_subscript: Final = []
|
||||
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
@@ -2,9 +2,21 @@ x = range(10)
|
||||
|
||||
# RUF015
|
||||
list(x)[0]
|
||||
list(x)[:1]
|
||||
list(x)[:1:1]
|
||||
list(x)[:1:2]
|
||||
tuple(x)[0]
|
||||
tuple(x)[:1]
|
||||
tuple(x)[:1:1]
|
||||
tuple(x)[:1:2]
|
||||
list(i for i in x)[0]
|
||||
list(i for i in x)[:1]
|
||||
list(i for i in x)[:1:1]
|
||||
list(i for i in x)[:1:2]
|
||||
[i for i in x][0]
|
||||
[i for i in x][:1]
|
||||
[i for i in x][:1:1]
|
||||
[i for i in x][:1:2]
|
||||
|
||||
# OK (not indexing (solely) the first element)
|
||||
list(x)
|
||||
@@ -17,9 +29,6 @@ list(x)[::]
|
||||
[i for i in x]
|
||||
[i for i in x][1]
|
||||
[i for i in x][-1]
|
||||
[i for i in x][:1]
|
||||
[i for i in x][:1:1]
|
||||
[i for i in x][:1:2]
|
||||
[i for i in x][1:]
|
||||
[i for i in x][:3:2]
|
||||
[i for i in x][::2]
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# ruff: noqa: RUF100
|
||||
|
||||
import os # noqa: F401
|
||||
|
||||
print(os.sep)
|
||||
@@ -1,5 +0,0 @@
|
||||
import os # ruff: noqa: F401
|
||||
|
||||
|
||||
def f():
|
||||
x = 1
|
||||
@@ -33,7 +33,7 @@ impl<'a, T: Codegen<'a>> CodegenStylist<'a> for T {
|
||||
///
|
||||
/// Returns `Ok(None)` if the statement is empty after removing the imports.
|
||||
pub(crate) fn remove_imports<'a>(
|
||||
member_names: impl Iterator<Item = &'a str>,
|
||||
imports: impl Iterator<Item = &'a str>,
|
||||
stmt: &Stmt,
|
||||
locator: &Locator,
|
||||
stylist: &Stylist,
|
||||
@@ -45,20 +45,27 @@ pub(crate) fn remove_imports<'a>(
|
||||
bail!("Expected Statement::Simple");
|
||||
};
|
||||
|
||||
let aliases = match body.body.first_mut() {
|
||||
Some(SmallStatement::Import(import_body)) => &mut import_body.names,
|
||||
let (aliases, import_module) = match body.body.first_mut() {
|
||||
Some(SmallStatement::Import(import_body)) => (&mut import_body.names, None),
|
||||
Some(SmallStatement::ImportFrom(import_body)) => {
|
||||
if let ImportNames::Aliases(names) = &mut import_body.names {
|
||||
names
|
||||
(
|
||||
names,
|
||||
Some((&import_body.relative, import_body.module.as_ref())),
|
||||
)
|
||||
} else if let ImportNames::Star(..) = &import_body.names {
|
||||
// Special-case: if the import is a `from ... import *`, then we delete the
|
||||
// entire statement.
|
||||
let mut found_star = false;
|
||||
for member in member_names {
|
||||
if member == "*" {
|
||||
for import in imports {
|
||||
let qualified_name = match import_body.module.as_ref() {
|
||||
Some(module_name) => format!("{}.*", compose_module_path(module_name)),
|
||||
None => "*".to_string(),
|
||||
};
|
||||
if import == qualified_name {
|
||||
found_star = true;
|
||||
} else {
|
||||
bail!("Expected \"*\" for unused import (got: \"{}\")", member);
|
||||
bail!("Expected \"*\" for unused import (got: \"{}\")", import);
|
||||
}
|
||||
}
|
||||
if !found_star {
|
||||
@@ -75,10 +82,30 @@ pub(crate) fn remove_imports<'a>(
|
||||
// Preserve the trailing comma (or not) from the last entry.
|
||||
let trailing_comma = aliases.last().and_then(|alias| alias.comma.clone());
|
||||
|
||||
for member in member_names {
|
||||
let alias_index = aliases
|
||||
.iter()
|
||||
.position(|alias| member == compose_module_path(&alias.name));
|
||||
for import in imports {
|
||||
let alias_index = aliases.iter().position(|alias| {
|
||||
let qualified_name = match import_module {
|
||||
Some((relative, module)) => {
|
||||
let module = module.map(compose_module_path);
|
||||
let member = compose_module_path(&alias.name);
|
||||
let mut qualified_name = String::with_capacity(
|
||||
relative.len() + module.as_ref().map_or(0, String::len) + member.len() + 1,
|
||||
);
|
||||
for _ in 0..relative.len() {
|
||||
qualified_name.push('.');
|
||||
}
|
||||
if let Some(module) = module {
|
||||
qualified_name.push_str(&module);
|
||||
qualified_name.push('.');
|
||||
}
|
||||
qualified_name.push_str(&member);
|
||||
qualified_name
|
||||
}
|
||||
None => compose_module_path(&alias.name),
|
||||
};
|
||||
qualified_name == import
|
||||
});
|
||||
|
||||
if let Some(index) = alias_index {
|
||||
aliases.remove(index);
|
||||
}
|
||||
@@ -112,7 +139,7 @@ pub(crate) fn remove_imports<'a>(
|
||||
///
|
||||
/// Returns the modified import statement.
|
||||
pub(crate) fn retain_imports(
|
||||
member_names: &[&str],
|
||||
imports: &[&str],
|
||||
stmt: &Stmt,
|
||||
locator: &Locator,
|
||||
stylist: &Stylist,
|
||||
@@ -124,11 +151,14 @@ pub(crate) fn retain_imports(
|
||||
bail!("Expected Statement::Simple");
|
||||
};
|
||||
|
||||
let aliases = match body.body.first_mut() {
|
||||
Some(SmallStatement::Import(import_body)) => &mut import_body.names,
|
||||
let (aliases, import_module) = match body.body.first_mut() {
|
||||
Some(SmallStatement::Import(import_body)) => (&mut import_body.names, None),
|
||||
Some(SmallStatement::ImportFrom(import_body)) => {
|
||||
if let ImportNames::Aliases(names) = &mut import_body.names {
|
||||
names
|
||||
(
|
||||
names,
|
||||
Some((&import_body.relative, import_body.module.as_ref())),
|
||||
)
|
||||
} else {
|
||||
bail!("Expected: ImportNames::Aliases");
|
||||
}
|
||||
@@ -140,9 +170,28 @@ pub(crate) fn retain_imports(
|
||||
let trailing_comma = aliases.last().and_then(|alias| alias.comma.clone());
|
||||
|
||||
aliases.retain(|alias| {
|
||||
member_names
|
||||
.iter()
|
||||
.any(|member| *member == compose_module_path(&alias.name))
|
||||
imports.iter().any(|import| {
|
||||
let qualified_name = match import_module {
|
||||
Some((relative, module)) => {
|
||||
let module = module.map(compose_module_path);
|
||||
let member = compose_module_path(&alias.name);
|
||||
let mut qualified_name = String::with_capacity(
|
||||
relative.len() + module.as_ref().map_or(0, String::len) + member.len() + 1,
|
||||
);
|
||||
for _ in 0..relative.len() {
|
||||
qualified_name.push('.');
|
||||
}
|
||||
if let Some(module) = module {
|
||||
qualified_name.push_str(&module);
|
||||
qualified_name.push('.');
|
||||
}
|
||||
qualified_name.push_str(&member);
|
||||
qualified_name
|
||||
}
|
||||
None => compose_module_path(&alias.name),
|
||||
};
|
||||
qualified_name == *import
|
||||
})
|
||||
});
|
||||
|
||||
// But avoid destroying any trailing comments.
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
//! Interface for generating autofix edits from higher-level actions (e.g., "remove an argument").
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use ruff_python_ast::{self as ast, ExceptHandler, Expr, Keyword, Ranged, Stmt};
|
||||
use ruff_python_parser::{lexer, Mode};
|
||||
use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||
|
||||
use ruff_diagnostics::Edit;
|
||||
use ruff_python_ast::{
|
||||
self as ast, Arguments, ExceptHandler, Expr, Keyword, PySourceType, Ranged, Stmt,
|
||||
};
|
||||
use ruff_python_codegen::Stylist;
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_python_parser::{lexer, AsMode};
|
||||
use ruff_python_trivia::{has_leading_content, is_python_whitespace, PythonWhitespace};
|
||||
use ruff_source_file::{Locator, NewlineWithTrailingNewline};
|
||||
use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||
|
||||
use crate::autofix::codemods;
|
||||
|
||||
@@ -58,115 +55,118 @@ pub(crate) fn delete_stmt(
|
||||
|
||||
/// Generate a `Fix` to remove the specified imports from an `import` statement.
|
||||
pub(crate) fn remove_unused_imports<'a>(
|
||||
member_names: impl Iterator<Item = &'a str>,
|
||||
unused_imports: impl Iterator<Item = &'a str>,
|
||||
stmt: &Stmt,
|
||||
parent: Option<&Stmt>,
|
||||
locator: &Locator,
|
||||
stylist: &Stylist,
|
||||
indexer: &Indexer,
|
||||
) -> Result<Edit> {
|
||||
match codemods::remove_imports(member_names, stmt, locator, stylist)? {
|
||||
match codemods::remove_imports(unused_imports, stmt, locator, stylist)? {
|
||||
None => Ok(delete_stmt(stmt, parent, locator, indexer)),
|
||||
Some(content) => Ok(Edit::range_replacement(content, stmt.range())),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub(crate) enum Parentheses {
|
||||
/// Remove parentheses, if the removed argument is the only argument left.
|
||||
Remove,
|
||||
/// Preserve parentheses, even if the removed argument is the only argument
|
||||
Preserve,
|
||||
}
|
||||
|
||||
/// Generic function to remove arguments or keyword arguments in function
|
||||
/// calls and class definitions. (For classes `args` should be considered
|
||||
/// `bases`)
|
||||
///
|
||||
/// Supports the removal of parentheses when this is the only (kw)arg left.
|
||||
/// For this behavior, set `remove_parentheses` to `true`.
|
||||
pub(crate) fn remove_argument<T: Ranged>(
|
||||
argument: &T,
|
||||
arguments: &Arguments,
|
||||
parentheses: Parentheses,
|
||||
pub(crate) fn remove_argument(
|
||||
locator: &Locator,
|
||||
source_type: PySourceType,
|
||||
call_at: TextSize,
|
||||
expr_range: TextRange,
|
||||
args: &[Expr],
|
||||
keywords: &[Keyword],
|
||||
remove_parentheses: bool,
|
||||
) -> Result<Edit> {
|
||||
// TODO(sbrugman): Preserve trailing comments.
|
||||
if arguments.keywords.len() + arguments.args.len() > 1 {
|
||||
let mut fix_start = None;
|
||||
let mut fix_end = None;
|
||||
let contents = locator.after(call_at);
|
||||
|
||||
if arguments
|
||||
.args
|
||||
.iter()
|
||||
.map(Expr::start)
|
||||
.chain(arguments.keywords.iter().map(Keyword::start))
|
||||
.any(|location| location > argument.start())
|
||||
{
|
||||
// Case 1: argument or keyword is _not_ the last node, so delete from the start of the
|
||||
// argument to the end of the subsequent comma.
|
||||
let mut seen_comma = false;
|
||||
for (tok, range) in lexer::lex_starts_at(
|
||||
locator.slice(arguments.range()),
|
||||
source_type.as_mode(),
|
||||
arguments.start(),
|
||||
)
|
||||
.flatten()
|
||||
{
|
||||
if seen_comma {
|
||||
if tok.is_non_logical_newline() {
|
||||
// Also delete any non-logical newlines after the comma.
|
||||
continue;
|
||||
}
|
||||
fix_end = Some(if tok.is_newline() {
|
||||
let mut fix_start = None;
|
||||
let mut fix_end = None;
|
||||
|
||||
let n_arguments = keywords.len() + args.len();
|
||||
if n_arguments == 0 {
|
||||
bail!("No arguments or keywords to remove");
|
||||
}
|
||||
|
||||
if n_arguments == 1 {
|
||||
// Case 1: there is only one argument.
|
||||
let mut count = 0u32;
|
||||
for (tok, range) in lexer::lex_starts_at(contents, Mode::Module, call_at).flatten() {
|
||||
if tok.is_lpar() {
|
||||
if count == 0 {
|
||||
fix_start = Some(if remove_parentheses {
|
||||
range.start()
|
||||
} else {
|
||||
range.start() + TextSize::from(1)
|
||||
});
|
||||
}
|
||||
count = count.saturating_add(1);
|
||||
}
|
||||
|
||||
if tok.is_rpar() {
|
||||
count = count.saturating_sub(1);
|
||||
if count == 0 {
|
||||
fix_end = Some(if remove_parentheses {
|
||||
range.end()
|
||||
} else {
|
||||
range.start()
|
||||
range.end() - TextSize::from(1)
|
||||
});
|
||||
break;
|
||||
}
|
||||
if range.start() == argument.start() {
|
||||
fix_start = Some(range.start());
|
||||
}
|
||||
if fix_start.is_some() && tok.is_comma() {
|
||||
seen_comma = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Case 2: argument or keyword is the last node, so delete from the start of the
|
||||
// previous comma to the end of the argument.
|
||||
for (tok, range) in lexer::lex_starts_at(
|
||||
locator.slice(arguments.range()),
|
||||
source_type.as_mode(),
|
||||
arguments.start(),
|
||||
)
|
||||
.flatten()
|
||||
{
|
||||
if range.start() == argument.start() {
|
||||
fix_end = Some(argument.end());
|
||||
break;
|
||||
}
|
||||
if tok.is_comma() {
|
||||
fix_start = Some(range.start());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match (fix_start, fix_end) {
|
||||
(Some(start), Some(end)) => Ok(Edit::deletion(start, end)),
|
||||
_ => {
|
||||
bail!("No fix could be constructed")
|
||||
} else if args
|
||||
.iter()
|
||||
.map(Expr::start)
|
||||
.chain(keywords.iter().map(Keyword::start))
|
||||
.any(|location| location > expr_range.start())
|
||||
{
|
||||
// Case 2: argument or keyword is _not_ the last node.
|
||||
let mut seen_comma = false;
|
||||
for (tok, range) in lexer::lex_starts_at(contents, Mode::Module, call_at).flatten() {
|
||||
if seen_comma {
|
||||
if tok.is_non_logical_newline() {
|
||||
// Also delete any non-logical newlines after the comma.
|
||||
continue;
|
||||
}
|
||||
fix_end = Some(if tok.is_newline() {
|
||||
range.end()
|
||||
} else {
|
||||
range.start()
|
||||
});
|
||||
break;
|
||||
}
|
||||
if range.start() == expr_range.start() {
|
||||
fix_start = Some(range.start());
|
||||
}
|
||||
if fix_start.is_some() && tok.is_comma() {
|
||||
seen_comma = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Only one argument; remove it (but preserve parentheses, if needed).
|
||||
Ok(match parentheses {
|
||||
Parentheses::Remove => Edit::deletion(arguments.start(), arguments.end()),
|
||||
Parentheses::Preserve => {
|
||||
Edit::replacement("()".to_string(), arguments.start(), arguments.end())
|
||||
// Case 3: argument or keyword is the last node, so we have to find the last
|
||||
// comma in the stmt.
|
||||
for (tok, range) in lexer::lex_starts_at(contents, Mode::Module, call_at).flatten() {
|
||||
if range.start() == expr_range.start() {
|
||||
fix_end = Some(expr_range.end());
|
||||
break;
|
||||
}
|
||||
})
|
||||
if tok.is_comma() {
|
||||
fix_start = Some(range.start());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match (fix_start, fix_end) {
|
||||
(Some(start), Some(end)) => Ok(Edit::deletion(start, end)),
|
||||
_ => {
|
||||
bail!("No fix could be constructed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,13 +179,16 @@ fn is_only<T: PartialEq>(vec: &[T], value: &T) -> bool {
|
||||
fn is_lone_child(child: &Stmt, parent: &Stmt) -> bool {
|
||||
match parent {
|
||||
Stmt::FunctionDef(ast::StmtFunctionDef { body, .. })
|
||||
| Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef { body, .. })
|
||||
| Stmt::ClassDef(ast::StmtClassDef { body, .. })
|
||||
| Stmt::With(ast::StmtWith { body, .. }) => {
|
||||
| Stmt::With(ast::StmtWith { body, .. })
|
||||
| Stmt::AsyncWith(ast::StmtAsyncWith { body, .. }) => {
|
||||
if is_only(body, child) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::For(ast::StmtFor { body, orelse, .. })
|
||||
| Stmt::AsyncFor(ast::StmtAsyncFor { body, orelse, .. })
|
||||
| Stmt::While(ast::StmtWhile { body, orelse, .. }) => {
|
||||
if is_only(body, child) || is_only(orelse, child) {
|
||||
return true;
|
||||
@@ -291,24 +294,24 @@ fn next_stmt_break(semicolon: TextSize, locator: &Locator) -> TextSize {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use anyhow::Result;
|
||||
|
||||
use ruff_python_ast::Ranged;
|
||||
use ruff_python_parser::parse_suite;
|
||||
use ruff_source_file::Locator;
|
||||
use ruff_python_ast::{Ranged, Suite};
|
||||
use ruff_python_parser::Parse;
|
||||
use ruff_text_size::TextSize;
|
||||
|
||||
use ruff_source_file::Locator;
|
||||
|
||||
use crate::autofix::edits::{next_stmt_break, trailing_semicolon};
|
||||
|
||||
#[test]
|
||||
fn find_semicolon() -> Result<()> {
|
||||
let contents = "x = 1";
|
||||
let program = parse_suite(contents, "<filename>")?;
|
||||
let program = Suite::parse(contents, "<filename>")?;
|
||||
let stmt = program.first().unwrap();
|
||||
let locator = Locator::new(contents);
|
||||
assert_eq!(trailing_semicolon(stmt.end(), &locator), None);
|
||||
|
||||
let contents = "x = 1; y = 1";
|
||||
let program = parse_suite(contents, "<filename>")?;
|
||||
let program = Suite::parse(contents, "<filename>")?;
|
||||
let stmt = program.first().unwrap();
|
||||
let locator = Locator::new(contents);
|
||||
assert_eq!(
|
||||
@@ -317,7 +320,7 @@ mod tests {
|
||||
);
|
||||
|
||||
let contents = "x = 1 ; y = 1";
|
||||
let program = parse_suite(contents, "<filename>")?;
|
||||
let program = Suite::parse(contents, "<filename>")?;
|
||||
let stmt = program.first().unwrap();
|
||||
let locator = Locator::new(contents);
|
||||
assert_eq!(
|
||||
@@ -330,7 +333,7 @@ x = 1 \
|
||||
; y = 1
|
||||
"
|
||||
.trim();
|
||||
let program = parse_suite(contents, "<filename>")?;
|
||||
let program = Suite::parse(contents, "<filename>")?;
|
||||
let stmt = program.first().unwrap();
|
||||
let locator = Locator::new(contents);
|
||||
assert_eq!(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use itertools::Itertools;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use itertools::Itertools;
|
||||
use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
|
||||
@@ -59,30 +59,35 @@ fn apply_fixes<'a>(
|
||||
})
|
||||
.sorted_by(|(rule1, fix1), (rule2, fix2)| cmp_fix(*rule1, *rule2, fix1, fix2))
|
||||
{
|
||||
let mut edits = fix
|
||||
.edits()
|
||||
.iter()
|
||||
.filter(|edit| !applied.contains(edit))
|
||||
.peekable();
|
||||
// If we already applied an identical fix as part of another correction, skip
|
||||
// any re-application.
|
||||
if fix.edits().iter().all(|edit| applied.contains(edit)) {
|
||||
*fixed.entry(rule).or_default() += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the fix contains at least one new edit, enforce isolation and positional requirements.
|
||||
if let Some(first) = edits.peek() {
|
||||
// If this fix requires isolation, and we've already applied another fix in the
|
||||
// same isolation group, skip it.
|
||||
if let IsolationLevel::Group(id) = fix.isolation() {
|
||||
if !isolated.insert(id) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Best-effort approach: if this fix overlaps with a fix we've already applied,
|
||||
// skip it.
|
||||
if last_pos.map_or(false, |last_pos| {
|
||||
fix.min_start()
|
||||
.map_or(false, |fix_location| last_pos >= fix_location)
|
||||
}) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this fix overlaps with a fix we've already applied, skip it.
|
||||
if last_pos.is_some_and(|last_pos| last_pos >= first.start()) {
|
||||
// If this fix requires isolation, and we've already applied another fix in the
|
||||
// same isolation group, skip it.
|
||||
if let IsolationLevel::Group(id) = fix.isolation() {
|
||||
if !isolated.insert(id) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let mut applied_edits = Vec::with_capacity(fix.edits().len());
|
||||
for edit in edits {
|
||||
for edit in fix
|
||||
.edits()
|
||||
.iter()
|
||||
.sorted_unstable_by_key(|edit| edit.start())
|
||||
{
|
||||
// Add all contents from `last_pos` to `fix.location`.
|
||||
let slice = locator.slice(TextRange::new(last_pos.unwrap_or_default(), edit.start()));
|
||||
output.push_str(slice);
|
||||
@@ -98,10 +103,9 @@ fn apply_fixes<'a>(
|
||||
|
||||
// Track that the edit was applied.
|
||||
last_pos = Some(edit.end());
|
||||
applied_edits.push(edit);
|
||||
applied.insert(edit);
|
||||
}
|
||||
|
||||
applied.extend(applied_edits.drain(..));
|
||||
*fixed.entry(rule).or_default() += 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,28 +1,27 @@
|
||||
use ruff_python_ast::{Parameter, Ranged};
|
||||
use ruff_python_ast::{Arg, Ranged};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::codes::Rule;
|
||||
use crate::rules::{flake8_builtins, pep8_naming, pycodestyle};
|
||||
|
||||
/// Run lint rules over a [`Parameter`] syntax node.
|
||||
pub(crate) fn parameter(parameter: &Parameter, checker: &mut Checker) {
|
||||
/// Run lint rules over an [`Arg`] syntax node.
|
||||
pub(crate) fn argument(arg: &Arg, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::AmbiguousVariableName) {
|
||||
if let Some(diagnostic) =
|
||||
pycodestyle::rules::ambiguous_variable_name(¶meter.name, parameter.range())
|
||||
if let Some(diagnostic) = pycodestyle::rules::ambiguous_variable_name(&arg.arg, arg.range())
|
||||
{
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::InvalidArgumentName) {
|
||||
if let Some(diagnostic) = pep8_naming::rules::invalid_argument_name(
|
||||
¶meter.name,
|
||||
parameter,
|
||||
&arg.arg,
|
||||
arg,
|
||||
&checker.settings.pep8_naming.ignore_names,
|
||||
) {
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::BuiltinArgumentShadowing) {
|
||||
flake8_builtins::rules::builtin_argument_shadowing(checker, parameter);
|
||||
flake8_builtins::rules::builtin_argument_shadowing(checker, arg);
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,26 @@
|
||||
use ruff_python_ast::Parameters;
|
||||
use ruff_python_ast::Arguments;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::codes::Rule;
|
||||
use crate::rules::{flake8_bugbear, flake8_pyi, ruff};
|
||||
|
||||
/// Run lint rules over a [`Parameters`] syntax node.
|
||||
pub(crate) fn parameters(parameters: &Parameters, checker: &mut Checker) {
|
||||
/// Run lint rules over a [`Arguments`] syntax node.
|
||||
pub(crate) fn arguments(arguments: &Arguments, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::MutableArgumentDefault) {
|
||||
flake8_bugbear::rules::mutable_argument_default(checker, parameters);
|
||||
flake8_bugbear::rules::mutable_argument_default(checker, arguments);
|
||||
}
|
||||
if checker.enabled(Rule::FunctionCallInDefaultArgument) {
|
||||
flake8_bugbear::rules::function_call_in_argument_default(checker, parameters);
|
||||
flake8_bugbear::rules::function_call_in_argument_default(checker, arguments);
|
||||
}
|
||||
if checker.settings.rules.enabled(Rule::ImplicitOptional) {
|
||||
ruff::rules::implicit_optional(checker, parameters);
|
||||
ruff::rules::implicit_optional(checker, arguments);
|
||||
}
|
||||
if checker.source_type.is_stub() {
|
||||
if checker.is_stub {
|
||||
if checker.enabled(Rule::TypedArgumentDefaultInStub) {
|
||||
flake8_pyi::rules::typed_argument_simple_defaults(checker, parameters);
|
||||
flake8_pyi::rules::typed_argument_simple_defaults(checker, arguments);
|
||||
}
|
||||
if checker.enabled(Rule::ArgumentDefaultInStub) {
|
||||
flake8_pyi::rules::argument_simple_defaults(checker, parameters);
|
||||
flake8_pyi::rules::argument_simple_defaults(checker, arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
use ruff_diagnostics::{Diagnostic, Fix};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::codes::Rule;
|
||||
use crate::rules::{flake8_import_conventions, flake8_pyi, pyflakes, pylint};
|
||||
use ruff_diagnostics::{Diagnostic, Fix};
|
||||
|
||||
/// Run lint rules over the [`Binding`]s.
|
||||
pub(crate) fn bindings(checker: &mut Checker) {
|
||||
@@ -11,7 +10,9 @@ pub(crate) fn bindings(checker: &mut Checker) {
|
||||
Rule::InvalidAllObject,
|
||||
Rule::UnaliasedCollectionsAbcSetImport,
|
||||
Rule::UnconventionalImportAlias,
|
||||
Rule::UnusedPrivateTypeVar,
|
||||
Rule::UnusedVariable,
|
||||
Rule::UnusedPrivateProtocol,
|
||||
]) {
|
||||
return;
|
||||
}
|
||||
@@ -56,11 +57,27 @@ pub(crate) fn bindings(checker: &mut Checker) {
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::UnaliasedCollectionsAbcSetImport) {
|
||||
if let Some(diagnostic) =
|
||||
flake8_pyi::rules::unaliased_collections_abc_set_import(checker, binding)
|
||||
{
|
||||
checker.diagnostics.push(diagnostic);
|
||||
if checker.is_stub {
|
||||
if checker.enabled(Rule::UnaliasedCollectionsAbcSetImport) {
|
||||
if let Some(diagnostic) =
|
||||
flake8_pyi::rules::unaliased_collections_abc_set_import(checker, binding)
|
||||
{
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::UnusedPrivateTypeVar) {
|
||||
if let Some(diagnostic) =
|
||||
flake8_pyi::rules::unused_private_type_var(checker, binding)
|
||||
{
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::UnusedPrivateProtocol) {
|
||||
if let Some(diagnostic) =
|
||||
flake8_pyi::rules::unused_private_protocol(checker, binding)
|
||||
{
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,18 +11,21 @@ pub(crate) fn deferred_for_loops(checker: &mut Checker) {
|
||||
for snapshot in for_loops {
|
||||
checker.semantic.restore(snapshot);
|
||||
|
||||
let Stmt::For(ast::StmtFor {
|
||||
if let Stmt::For(ast::StmtFor {
|
||||
target, iter, body, ..
|
||||
}) = checker.semantic.current_statement()
|
||||
else {
|
||||
unreachable!("Expected Stmt::For");
|
||||
};
|
||||
|
||||
if checker.enabled(Rule::UnusedLoopControlVariable) {
|
||||
flake8_bugbear::rules::unused_loop_control_variable(checker, target, body);
|
||||
}
|
||||
if checker.enabled(Rule::IncorrectDictIterator) {
|
||||
perflint::rules::incorrect_dict_iterator(checker, target, iter);
|
||||
})
|
||||
| Stmt::AsyncFor(ast::StmtAsyncFor {
|
||||
target, iter, body, ..
|
||||
}) = &checker.semantic.stmt()
|
||||
{
|
||||
if checker.enabled(Rule::UnusedLoopControlVariable) {
|
||||
flake8_bugbear::rules::unused_loop_control_variable(checker, target, body);
|
||||
}
|
||||
if checker.enabled(Rule::IncorrectDictIterator) {
|
||||
perflint::rules::incorrect_dict_iterator(checker, target, iter);
|
||||
}
|
||||
} else {
|
||||
unreachable!("Expected Expr::For | Expr::AsyncFor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_python_ast::cast;
|
||||
use ruff_python_semantic::analyze::{branch_detection, visibility};
|
||||
use ruff_python_semantic::{Binding, BindingKind, ScopeKind};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::codes::Rule;
|
||||
use crate::rules::{flake8_pyi, flake8_type_checking, flake8_unused_arguments, pyflakes, pylint};
|
||||
use crate::rules::{flake8_type_checking, flake8_unused_arguments, pyflakes, pylint};
|
||||
|
||||
/// Run lint rules over all deferred scopes in the [`SemanticModel`].
|
||||
pub(crate) fn deferred_scopes(checker: &mut Checker) {
|
||||
@@ -23,10 +24,6 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
|
||||
Rule::UnusedImport,
|
||||
Rule::UnusedLambdaArgument,
|
||||
Rule::UnusedMethodArgument,
|
||||
Rule::UnusedPrivateProtocol,
|
||||
Rule::UnusedPrivateTypeAlias,
|
||||
Rule::UnusedPrivateTypeVar,
|
||||
Rule::UnusedPrivateTypedDict,
|
||||
Rule::UnusedStaticMethodArgument,
|
||||
Rule::UnusedVariable,
|
||||
]) {
|
||||
@@ -36,7 +33,7 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
|
||||
// Identify any valid runtime imports. If a module is imported at runtime, and
|
||||
// used at runtime, then by default, we avoid flagging any other
|
||||
// imports from that model as typing-only.
|
||||
let enforce_typing_imports = !checker.source_type.is_stub()
|
||||
let enforce_typing_imports = !checker.is_stub
|
||||
&& checker.any_enabled(&[
|
||||
Rule::RuntimeImportInTypeCheckingBlock,
|
||||
Rule::TypingOnlyFirstPartyImport,
|
||||
@@ -111,11 +108,7 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
|
||||
// If the bindings are in different forks, abort.
|
||||
if shadowed.source.map_or(true, |left| {
|
||||
binding.source.map_or(true, |right| {
|
||||
branch_detection::different_forks(
|
||||
left,
|
||||
right,
|
||||
checker.semantic.statements(),
|
||||
)
|
||||
branch_detection::different_forks(left, right, &checker.semantic.stmts)
|
||||
})
|
||||
}) {
|
||||
continue;
|
||||
@@ -171,25 +164,16 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(statement_id) = shadowed.source else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// If this is an overloaded function, abort.
|
||||
if shadowed.kind.is_function_definition() {
|
||||
if checker
|
||||
.semantic
|
||||
.statement(statement_id)
|
||||
.as_function_def_stmt()
|
||||
.is_some_and(|function| {
|
||||
visibility::is_overload(
|
||||
&function.decorator_list,
|
||||
&checker.semantic,
|
||||
)
|
||||
})
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if shadowed.kind.is_function_definition()
|
||||
&& visibility::is_overload(
|
||||
cast::decorator_list(
|
||||
checker.semantic.stmts[shadowed.source.unwrap()],
|
||||
),
|
||||
&checker.semantic,
|
||||
)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// Only enforce cross-scope shadowing for imports.
|
||||
@@ -207,11 +191,7 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
|
||||
// If the bindings are in different forks, abort.
|
||||
if shadowed.source.map_or(true, |left| {
|
||||
binding.source.map_or(true, |right| {
|
||||
branch_detection::different_forks(
|
||||
left,
|
||||
right,
|
||||
checker.semantic.statements(),
|
||||
)
|
||||
branch_detection::different_forks(left, right, &checker.semantic.stmts)
|
||||
})
|
||||
}) {
|
||||
continue;
|
||||
@@ -234,20 +214,10 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
|
||||
if checker.enabled(Rule::UnusedPrivateTypeVar) {
|
||||
flake8_pyi::rules::unused_private_type_var(checker, scope, &mut diagnostics);
|
||||
}
|
||||
if checker.enabled(Rule::UnusedPrivateProtocol) {
|
||||
flake8_pyi::rules::unused_private_protocol(checker, scope, &mut diagnostics);
|
||||
}
|
||||
if checker.enabled(Rule::UnusedPrivateTypeAlias) {
|
||||
flake8_pyi::rules::unused_private_type_alias(checker, scope, &mut diagnostics);
|
||||
}
|
||||
if checker.enabled(Rule::UnusedPrivateTypedDict) {
|
||||
flake8_pyi::rules::unused_private_typed_dict(checker, scope, &mut diagnostics);
|
||||
}
|
||||
|
||||
if matches!(scope.kind, ScopeKind::Function(_) | ScopeKind::Lambda(_)) {
|
||||
if matches!(
|
||||
scope.kind,
|
||||
ScopeKind::Function(_) | ScopeKind::AsyncFunction(_) | ScopeKind::Lambda(_)
|
||||
) {
|
||||
if checker.enabled(Rule::UnusedVariable) {
|
||||
pyflakes::rules::unused_variable(checker, scope, &mut diagnostics);
|
||||
}
|
||||
@@ -256,7 +226,7 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
|
||||
pyflakes::rules::unused_annotation(checker, scope, &mut diagnostics);
|
||||
}
|
||||
|
||||
if !checker.source_type.is_stub() {
|
||||
if !checker.is_stub {
|
||||
if checker.any_enabled(&[
|
||||
Rule::UnusedClassMethodArgument,
|
||||
Rule::UnusedFunctionArgument,
|
||||
@@ -273,7 +243,10 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
|
||||
if matches!(scope.kind, ScopeKind::Function(_) | ScopeKind::Module) {
|
||||
if matches!(
|
||||
scope.kind,
|
||||
ScopeKind::Function(_) | ScopeKind::AsyncFunction(_) | ScopeKind::Module
|
||||
) {
|
||||
if enforce_typing_imports {
|
||||
let runtime_imports: Vec<&Binding> = checker
|
||||
.semantic
|
||||
|
||||
@@ -30,8 +30,8 @@ pub(crate) fn definitions(checker: &mut Checker) {
|
||||
Rule::MissingTypeKwargs,
|
||||
Rule::MissingTypeSelf,
|
||||
]);
|
||||
let enforce_stubs = checker.source_type.is_stub() && checker.enabled(Rule::DocstringInStub);
|
||||
let enforce_stubs_and_runtime = checker.enabled(Rule::IterMethodReturnIterable);
|
||||
let enforce_stubs = checker.is_stub
|
||||
&& checker.any_enabled(&[Rule::DocstringInStub, Rule::IterMethodReturnIterable]);
|
||||
let enforce_docstrings = checker.any_enabled(&[
|
||||
Rule::BlankLineAfterLastSection,
|
||||
Rule::BlankLineAfterSummary,
|
||||
@@ -81,7 +81,7 @@ pub(crate) fn definitions(checker: &mut Checker) {
|
||||
Rule::UndocumentedPublicPackage,
|
||||
]);
|
||||
|
||||
if !enforce_annotations && !enforce_docstrings && !enforce_stubs && !enforce_stubs_and_runtime {
|
||||
if !enforce_annotations && !enforce_docstrings && !enforce_stubs {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ pub(crate) fn definitions(checker: &mut Checker) {
|
||||
// interfaces, without any AST nodes in between. Right now, we
|
||||
// only error when traversing definition boundaries (functions,
|
||||
// classes, etc.).
|
||||
if !overloaded_name.is_some_and(|overloaded_name| {
|
||||
if !overloaded_name.map_or(false, |overloaded_name| {
|
||||
flake8_annotations::helpers::is_overload_impl(
|
||||
definition,
|
||||
&overloaded_name,
|
||||
@@ -141,8 +141,6 @@ pub(crate) fn definitions(checker: &mut Checker) {
|
||||
if checker.enabled(Rule::DocstringInStub) {
|
||||
flake8_pyi::rules::docstring_in_stubs(checker, docstring);
|
||||
}
|
||||
}
|
||||
if enforce_stubs_and_runtime {
|
||||
if checker.enabled(Rule::IterMethodReturnIterable) {
|
||||
flake8_pyi::rules::iter_method_return_iterable(checker, definition);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use ruff_python_ast::{self as ast, Arguments, Constant, Expr, ExprContext, Operator, Ranged};
|
||||
use ruff_python_ast::{self as ast, Constant, Expr, ExprContext, Operator, Ranged};
|
||||
use ruff_python_literal::cformat::{CFormatError, CFormatErrorType};
|
||||
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
@@ -31,7 +31,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
if let Some(operator) = typing::to_pep604_operator(value, slice, &checker.semantic)
|
||||
{
|
||||
if checker.enabled(Rule::FutureRewritableTypeAnnotation) {
|
||||
if !checker.source_type.is_stub()
|
||||
if !checker.is_stub
|
||||
&& checker.settings.target_version < PythonVersion::Py310
|
||||
&& checker.settings.target_version >= PythonVersion::Py37
|
||||
&& !checker.semantic.future_annotations()
|
||||
@@ -44,7 +44,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::NonPEP604Annotation) {
|
||||
if checker.source_type.is_stub()
|
||||
if checker.is_stub
|
||||
|| checker.settings.target_version >= PythonVersion::Py310
|
||||
|| (checker.settings.target_version >= PythonVersion::Py37
|
||||
&& checker.semantic.future_annotations()
|
||||
@@ -59,7 +59,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
|
||||
// Ex) list[...]
|
||||
if checker.enabled(Rule::FutureRequiredTypeAnnotation) {
|
||||
if !checker.source_type.is_stub()
|
||||
if !checker.is_stub
|
||||
&& checker.settings.target_version < PythonVersion::Py39
|
||||
&& !checker.semantic.future_annotations()
|
||||
&& checker.semantic.in_annotation()
|
||||
@@ -74,27 +74,24 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
}
|
||||
|
||||
// Ex) Union[...]
|
||||
if checker.any_enabled(&[
|
||||
Rule::UnnecessaryLiteralUnion,
|
||||
Rule::DuplicateUnionMember,
|
||||
Rule::RedundantLiteralUnion,
|
||||
Rule::UnnecessaryTypeUnion,
|
||||
]) {
|
||||
// Avoid duplicate checks if the parent is a union, since these rules already
|
||||
// traverse nested unions.
|
||||
if !checker.semantic.in_nested_union() {
|
||||
if checker.any_enabled(&[Rule::UnnecessaryLiteralUnion, Rule::DuplicateUnionMember]) {
|
||||
// Determine if the current expression is an union
|
||||
// Avoid duplicate checks if the parent is an `Union[...]` since these rules traverse nested unions
|
||||
let is_unchecked_union = checker
|
||||
.semantic
|
||||
.expr_grandparent()
|
||||
.and_then(Expr::as_subscript_expr)
|
||||
.map_or(true, |parent| {
|
||||
!checker.semantic.match_typing_expr(&parent.value, "Union")
|
||||
});
|
||||
|
||||
if is_unchecked_union {
|
||||
if checker.enabled(Rule::UnnecessaryLiteralUnion) {
|
||||
flake8_pyi::rules::unnecessary_literal_union(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::DuplicateUnionMember) {
|
||||
flake8_pyi::rules::duplicate_union_member(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::RedundantLiteralUnion) {
|
||||
flake8_pyi::rules::redundant_literal_union(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryTypeUnion) {
|
||||
flake8_pyi::rules::unnecessary_type_union(checker, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,8 +152,10 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::NumpyDeprecatedFunction) {
|
||||
numpy::rules::deprecated_function(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::CollectionsNamedTuple) {
|
||||
flake8_pyi::rules::collections_named_tuple(checker, expr);
|
||||
if checker.is_stub {
|
||||
if checker.enabled(Rule::CollectionsNamedTuple) {
|
||||
flake8_pyi::rules::collections_named_tuple(checker, expr);
|
||||
}
|
||||
}
|
||||
|
||||
// Ex) List[...]
|
||||
@@ -168,18 +167,20 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
typing::to_pep585_generic(expr, &checker.semantic)
|
||||
{
|
||||
if checker.enabled(Rule::FutureRewritableTypeAnnotation) {
|
||||
if !checker.source_type.is_stub()
|
||||
if !checker.is_stub
|
||||
&& checker.settings.target_version < PythonVersion::Py39
|
||||
&& checker.settings.target_version >= PythonVersion::Py37
|
||||
&& !checker.semantic.future_annotations()
|
||||
&& checker.semantic.in_annotation()
|
||||
&& !checker.settings.pyupgrade.keep_runtime_typing
|
||||
{
|
||||
flake8_future_annotations::rules::future_rewritable_type_annotation(checker, expr);
|
||||
flake8_future_annotations::rules::future_rewritable_type_annotation(
|
||||
checker, expr,
|
||||
);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::NonPEP585Annotation) {
|
||||
if checker.source_type.is_stub()
|
||||
if checker.is_stub
|
||||
|| checker.settings.target_version >= PythonVersion::Py39
|
||||
|| (checker.settings.target_version >= PythonVersion::Py37
|
||||
&& checker.semantic.future_annotations()
|
||||
@@ -198,13 +199,13 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
}
|
||||
ExprContext::Store => {
|
||||
if checker.enabled(Rule::NonLowercaseVariableInFunction) {
|
||||
if checker.semantic.current_scope().kind.is_function() {
|
||||
if checker.semantic.scope().kind.is_any_function() {
|
||||
// Ignore globals.
|
||||
if !checker
|
||||
.semantic
|
||||
.current_scope()
|
||||
.scope()
|
||||
.get(id)
|
||||
.is_some_and(|binding_id| {
|
||||
.map_or(false, |binding_id| {
|
||||
checker.semantic.binding(binding_id).is_global()
|
||||
})
|
||||
{
|
||||
@@ -215,19 +216,16 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::MixedCaseVariableInClassScope) {
|
||||
if let ScopeKind::Class(ast::StmtClassDef { arguments, .. }) =
|
||||
&checker.semantic.current_scope().kind
|
||||
if let ScopeKind::Class(ast::StmtClassDef { bases, .. }) =
|
||||
&checker.semantic.scope().kind
|
||||
{
|
||||
pep8_naming::rules::mixed_case_variable_in_class_scope(
|
||||
checker,
|
||||
expr,
|
||||
id,
|
||||
arguments.as_deref(),
|
||||
checker, expr, id, bases,
|
||||
);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::MixedCaseVariableInGlobalScope) {
|
||||
if matches!(checker.semantic.current_scope().kind, ScopeKind::Module) {
|
||||
if matches!(checker.semantic.scope().kind, ScopeKind::Module) {
|
||||
pep8_naming::rules::mixed_case_variable_in_global_scope(
|
||||
checker, expr, id,
|
||||
);
|
||||
@@ -240,7 +238,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if let ScopeKind::Class(class_def) = checker.semantic.current_scope().kind {
|
||||
if let ScopeKind::Class(class_def) = checker.semantic.scope().kind {
|
||||
if checker.enabled(Rule::BuiltinAttributeShadowing) {
|
||||
flake8_builtins::rules::builtin_attribute_shadowing(
|
||||
checker, class_def, id, *range,
|
||||
@@ -269,7 +267,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
]) {
|
||||
if let Some(replacement) = typing::to_pep585_generic(expr, &checker.semantic) {
|
||||
if checker.enabled(Rule::FutureRewritableTypeAnnotation) {
|
||||
if !checker.source_type.is_stub()
|
||||
if !checker.is_stub
|
||||
&& checker.settings.target_version < PythonVersion::Py39
|
||||
&& checker.settings.target_version >= PythonVersion::Py37
|
||||
&& !checker.semantic.future_annotations()
|
||||
@@ -282,7 +280,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::NonPEP585Annotation) {
|
||||
if checker.source_type.is_stub()
|
||||
if checker.is_stub
|
||||
|| checker.settings.target_version >= PythonVersion::Py39
|
||||
|| (checker.settings.target_version >= PythonVersion::Py37
|
||||
&& checker.semantic.future_annotations()
|
||||
@@ -320,26 +318,22 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::PrivateMemberAccess) {
|
||||
flake8_self::rules::private_member_access(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::CollectionsNamedTuple) {
|
||||
flake8_pyi::rules::collections_named_tuple(checker, expr);
|
||||
if checker.is_stub {
|
||||
if checker.enabled(Rule::CollectionsNamedTuple) {
|
||||
flake8_pyi::rules::collections_named_tuple(checker, expr);
|
||||
}
|
||||
}
|
||||
pandas_vet::rules::attr(checker, attr, value, expr);
|
||||
}
|
||||
Expr::Call(
|
||||
call @ ast::ExprCall {
|
||||
func,
|
||||
arguments:
|
||||
Arguments {
|
||||
args,
|
||||
keywords,
|
||||
range: _,
|
||||
},
|
||||
args,
|
||||
keywords,
|
||||
range: _,
|
||||
},
|
||||
) => {
|
||||
if checker.any_enabled(&[
|
||||
// pylint
|
||||
Rule::BadStringFormatCharacter,
|
||||
// pyflakes
|
||||
Rule::StringDotFormatInvalidFormat,
|
||||
Rule::StringDotFormatExtraNamedArguments,
|
||||
@@ -388,8 +382,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
.enabled(Rule::StringDotFormatExtraPositionalArguments)
|
||||
{
|
||||
pyflakes::rules::string_dot_format_extra_positional_arguments(
|
||||
checker, &summary, args, location,
|
||||
);
|
||||
checker,
|
||||
&summary, args, location,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::StringDotFormatMissingArguments) {
|
||||
pyflakes::rules::string_dot_format_missing_argument(
|
||||
@@ -402,7 +397,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::FormatLiterals) {
|
||||
pyupgrade::rules::format_literals(checker, &summary, call);
|
||||
pyupgrade::rules::format_literals(checker, &summary, expr);
|
||||
}
|
||||
if checker.enabled(Rule::FString) {
|
||||
pyupgrade::rules::f_strings(
|
||||
@@ -415,14 +410,6 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if checker.enabled(Rule::BadStringFormatCharacter) {
|
||||
pylint::rules::bad_string_format_character::call(
|
||||
checker,
|
||||
val.as_str(),
|
||||
location,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -437,10 +424,10 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
pyupgrade::rules::super_call_with_parameters(checker, expr, func, args);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryEncodeUTF8) {
|
||||
pyupgrade::rules::unnecessary_encode_utf8(checker, call);
|
||||
pyupgrade::rules::unnecessary_encode_utf8(checker, expr, func, args, keywords);
|
||||
}
|
||||
if checker.enabled(Rule::RedundantOpenModes) {
|
||||
pyupgrade::rules::redundant_open_modes(checker, call);
|
||||
pyupgrade::rules::redundant_open_modes(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::NativeLiterals) {
|
||||
pyupgrade::rules::native_literals(checker, expr, func, args, keywords);
|
||||
@@ -449,10 +436,10 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
pyupgrade::rules::open_alias(checker, expr, func);
|
||||
}
|
||||
if checker.enabled(Rule::ReplaceUniversalNewlines) {
|
||||
pyupgrade::rules::replace_universal_newlines(checker, call);
|
||||
pyupgrade::rules::replace_universal_newlines(checker, func, keywords);
|
||||
}
|
||||
if checker.enabled(Rule::ReplaceStdoutStderr) {
|
||||
pyupgrade::rules::replace_stdout_stderr(checker, call);
|
||||
pyupgrade::rules::replace_stdout_stderr(checker, expr, func, args, keywords);
|
||||
}
|
||||
if checker.enabled(Rule::OSErrorAlias) {
|
||||
pyupgrade::rules::os_error_alias_call(checker, func);
|
||||
@@ -472,7 +459,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
flake8_async::rules::blocking_os_call(checker, expr);
|
||||
}
|
||||
if checker.any_enabled(&[Rule::Print, Rule::PPrint]) {
|
||||
flake8_print::rules::print_call(checker, call);
|
||||
flake8_print::rules::print_call(checker, func, keywords);
|
||||
}
|
||||
if checker.any_enabled(&[
|
||||
Rule::SuspiciousPickleUsage,
|
||||
@@ -524,11 +511,13 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
}
|
||||
if checker.enabled(Rule::ZipWithoutExplicitStrict) {
|
||||
if checker.settings.target_version >= PythonVersion::Py310 {
|
||||
flake8_bugbear::rules::zip_without_explicit_strict(checker, call);
|
||||
flake8_bugbear::rules::zip_without_explicit_strict(
|
||||
checker, expr, func, args, keywords,
|
||||
);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::NoExplicitStacklevel) {
|
||||
flake8_bugbear::rules::no_explicit_stacklevel(checker, call);
|
||||
flake8_bugbear::rules::no_explicit_stacklevel(checker, func, keywords);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryDictKwargs) {
|
||||
flake8_pie::rules::unnecessary_dict_kwargs(checker, expr, keywords);
|
||||
@@ -537,22 +526,22 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
flake8_bandit::rules::exec_used(checker, func);
|
||||
}
|
||||
if checker.enabled(Rule::BadFilePermissions) {
|
||||
flake8_bandit::rules::bad_file_permissions(checker, call);
|
||||
flake8_bandit::rules::bad_file_permissions(checker, func, args, keywords);
|
||||
}
|
||||
if checker.enabled(Rule::RequestWithNoCertValidation) {
|
||||
flake8_bandit::rules::request_with_no_cert_validation(checker, call);
|
||||
flake8_bandit::rules::request_with_no_cert_validation(checker, func, keywords);
|
||||
}
|
||||
if checker.enabled(Rule::UnsafeYAMLLoad) {
|
||||
flake8_bandit::rules::unsafe_yaml_load(checker, call);
|
||||
flake8_bandit::rules::unsafe_yaml_load(checker, func, args, keywords);
|
||||
}
|
||||
if checker.enabled(Rule::SnmpInsecureVersion) {
|
||||
flake8_bandit::rules::snmp_insecure_version(checker, call);
|
||||
flake8_bandit::rules::snmp_insecure_version(checker, func, keywords);
|
||||
}
|
||||
if checker.enabled(Rule::SnmpWeakCryptography) {
|
||||
flake8_bandit::rules::snmp_weak_cryptography(checker, call);
|
||||
flake8_bandit::rules::snmp_weak_cryptography(checker, func, args, keywords);
|
||||
}
|
||||
if checker.enabled(Rule::Jinja2AutoescapeFalse) {
|
||||
flake8_bandit::rules::jinja2_autoescape_false(checker, call);
|
||||
flake8_bandit::rules::jinja2_autoescape_false(checker, func, keywords);
|
||||
}
|
||||
if checker.enabled(Rule::HardcodedPasswordFuncArg) {
|
||||
flake8_bandit::rules::hardcoded_password_func_arg(checker, keywords);
|
||||
@@ -561,16 +550,18 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
flake8_bandit::rules::hardcoded_sql_expression(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::HashlibInsecureHashFunction) {
|
||||
flake8_bandit::rules::hashlib_insecure_hash_functions(checker, call);
|
||||
flake8_bandit::rules::hashlib_insecure_hash_functions(
|
||||
checker, func, args, keywords,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::RequestWithoutTimeout) {
|
||||
flake8_bandit::rules::request_without_timeout(checker, call);
|
||||
flake8_bandit::rules::request_without_timeout(checker, func, keywords);
|
||||
}
|
||||
if checker.enabled(Rule::ParamikoCall) {
|
||||
flake8_bandit::rules::paramiko_call(checker, func);
|
||||
}
|
||||
if checker.enabled(Rule::LoggingConfigInsecureListen) {
|
||||
flake8_bandit::rules::logging_config_insecure_listen(checker, call);
|
||||
flake8_bandit::rules::logging_config_insecure_listen(checker, func, keywords);
|
||||
}
|
||||
if checker.any_enabled(&[
|
||||
Rule::SubprocessWithoutShellEqualsTrue,
|
||||
@@ -581,7 +572,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
Rule::StartProcessWithPartialPath,
|
||||
Rule::UnixCommandWildcardInjection,
|
||||
]) {
|
||||
flake8_bandit::rules::shell_injection(checker, call);
|
||||
flake8_bandit::rules::shell_injection(checker, func, args, keywords);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryGeneratorList) {
|
||||
flake8_comprehensions::rules::unnecessary_generator_list(
|
||||
@@ -665,7 +656,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
flake8_comprehensions::rules::unnecessary_map(
|
||||
checker,
|
||||
expr,
|
||||
checker.semantic.current_expression_parent(),
|
||||
checker.semantic.expr_parent(),
|
||||
func,
|
||||
args,
|
||||
);
|
||||
@@ -684,17 +675,23 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
flake8_debugger::rules::debugger_call(checker, expr, func);
|
||||
}
|
||||
if checker.enabled(Rule::PandasUseOfInplaceArgument) {
|
||||
pandas_vet::rules::inplace_argument(checker, call);
|
||||
pandas_vet::rules::inplace_argument(checker, expr, func, args, keywords);
|
||||
}
|
||||
pandas_vet::rules::call(checker, func);
|
||||
if checker.enabled(Rule::PandasUseOfDotReadTable) {
|
||||
pandas_vet::rules::use_of_read_table(checker, call);
|
||||
pandas_vet::rules::use_of_read_table(checker, func, keywords);
|
||||
}
|
||||
if checker.enabled(Rule::PandasUseOfPdMerge) {
|
||||
pandas_vet::rules::use_of_pd_merge(checker, func);
|
||||
}
|
||||
if checker.enabled(Rule::CallDatetimeWithoutTzinfo) {
|
||||
flake8_datetimez::rules::call_datetime_without_tzinfo(checker, call);
|
||||
flake8_datetimez::rules::call_datetime_without_tzinfo(
|
||||
checker,
|
||||
func,
|
||||
args,
|
||||
keywords,
|
||||
expr.range(),
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::CallDatetimeToday) {
|
||||
flake8_datetimez::rules::call_datetime_today(checker, func, expr.range());
|
||||
@@ -710,13 +707,30 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::CallDatetimeNowWithoutTzinfo) {
|
||||
flake8_datetimez::rules::call_datetime_now_without_tzinfo(checker, call);
|
||||
flake8_datetimez::rules::call_datetime_now_without_tzinfo(
|
||||
checker,
|
||||
func,
|
||||
args,
|
||||
keywords,
|
||||
expr.range(),
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::CallDatetimeFromtimestamp) {
|
||||
flake8_datetimez::rules::call_datetime_fromtimestamp(checker, call);
|
||||
flake8_datetimez::rules::call_datetime_fromtimestamp(
|
||||
checker,
|
||||
func,
|
||||
args,
|
||||
keywords,
|
||||
expr.range(),
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::CallDatetimeStrptimeWithoutZone) {
|
||||
flake8_datetimez::rules::call_datetime_strptime_without_zone(checker, call);
|
||||
flake8_datetimez::rules::call_datetime_strptime_without_zone(
|
||||
checker,
|
||||
func,
|
||||
args,
|
||||
expr.range(),
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::CallDateToday) {
|
||||
flake8_datetimez::rules::call_date_today(checker, func, expr.range());
|
||||
@@ -740,16 +754,18 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
pylint::rules::bad_str_strip_call(checker, func, args);
|
||||
}
|
||||
if checker.enabled(Rule::InvalidEnvvarDefault) {
|
||||
pylint::rules::invalid_envvar_default(checker, call);
|
||||
pylint::rules::invalid_envvar_default(checker, func, args, keywords);
|
||||
}
|
||||
if checker.enabled(Rule::InvalidEnvvarValue) {
|
||||
pylint::rules::invalid_envvar_value(checker, call);
|
||||
pylint::rules::invalid_envvar_value(checker, func, args, keywords);
|
||||
}
|
||||
if checker.enabled(Rule::NestedMinMax) {
|
||||
pylint::rules::nested_min_max(checker, expr, func, args, keywords);
|
||||
}
|
||||
if checker.enabled(Rule::PytestPatchWithLambda) {
|
||||
if let Some(diagnostic) = flake8_pytest_style::rules::patch_with_lambda(call) {
|
||||
if let Some(diagnostic) =
|
||||
flake8_pytest_style::rules::patch_with_lambda(func, args, keywords)
|
||||
{
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
@@ -761,16 +777,16 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::SubprocessPopenPreexecFn) {
|
||||
pylint::rules::subprocess_popen_preexec_fn(checker, call);
|
||||
pylint::rules::subprocess_popen_preexec_fn(checker, func, keywords);
|
||||
}
|
||||
if checker.any_enabled(&[
|
||||
Rule::PytestRaisesWithoutException,
|
||||
Rule::PytestRaisesTooBroad,
|
||||
]) {
|
||||
flake8_pytest_style::rules::raises_call(checker, call);
|
||||
flake8_pytest_style::rules::raises_call(checker, func, args, keywords);
|
||||
}
|
||||
if checker.enabled(Rule::PytestFailWithoutMessage) {
|
||||
flake8_pytest_style::rules::fail_call(checker, call);
|
||||
flake8_pytest_style::rules::fail_call(checker, func, args, keywords);
|
||||
}
|
||||
if checker.enabled(Rule::PairwiseOverZipped) {
|
||||
if checker.settings.target_version >= PythonVersion::Py310 {
|
||||
@@ -841,7 +857,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
flake8_use_pathlib::rules::path_constructor_current_directory(checker, expr, func);
|
||||
}
|
||||
if checker.enabled(Rule::OsSepSplit) {
|
||||
flake8_use_pathlib::rules::os_sep_split(checker, call);
|
||||
flake8_use_pathlib::rules::os_sep_split(checker, func, args, keywords);
|
||||
}
|
||||
if checker.enabled(Rule::NumpyLegacyRandom) {
|
||||
numpy::rules::legacy_random(checker, func);
|
||||
@@ -856,15 +872,15 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
Rule::LoggingExcInfo,
|
||||
Rule::LoggingRedundantExcInfo,
|
||||
]) {
|
||||
flake8_logging_format::rules::logging_call(checker, call);
|
||||
flake8_logging_format::rules::logging_call(checker, func, args, keywords);
|
||||
}
|
||||
if checker.any_enabled(&[Rule::LoggingTooFewArgs, Rule::LoggingTooManyArgs]) {
|
||||
pylint::rules::logging_call(checker, call);
|
||||
pylint::rules::logging_call(checker, func, args, keywords);
|
||||
}
|
||||
if checker.enabled(Rule::DjangoLocalsInRenderFunction) {
|
||||
flake8_django::rules::locals_in_render_function(checker, call);
|
||||
flake8_django::rules::locals_in_render_function(checker, func, args, keywords);
|
||||
}
|
||||
if checker.enabled(Rule::UnsupportedMethodCallOnAll) {
|
||||
if checker.is_stub && checker.enabled(Rule::UnsupportedMethodCallOnAll) {
|
||||
flake8_pyi::rules::unsupported_method_call_on_all(checker, func);
|
||||
}
|
||||
}
|
||||
@@ -915,7 +931,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
pylint::rules::await_outside_async(checker, expr);
|
||||
}
|
||||
}
|
||||
Expr::FString(ast::ExprFString { values, range: _ }) => {
|
||||
Expr::JoinedStr(ast::ExprJoinedStr { values, range: _ }) => {
|
||||
if checker.enabled(Rule::FStringMissingPlaceholders) {
|
||||
pyflakes::rules::f_string_missing_placeholders(expr, values, checker);
|
||||
}
|
||||
@@ -1029,9 +1045,6 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
checker.locator,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::BadStringFormatCharacter) {
|
||||
pylint::rules::bad_string_format_character::percent(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::BadStringFormatType) {
|
||||
pylint::rules::bad_string_format_type(checker, expr, right);
|
||||
}
|
||||
@@ -1063,7 +1076,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
}) => {
|
||||
// Ex) `str | None`
|
||||
if checker.enabled(Rule::FutureRequiredTypeAnnotation) {
|
||||
if !checker.source_type.is_stub()
|
||||
if !checker.is_stub
|
||||
&& checker.settings.target_version < PythonVersion::Py310
|
||||
&& !checker.semantic.future_annotations()
|
||||
&& checker.semantic.in_annotation()
|
||||
@@ -1075,24 +1088,26 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Avoid duplicate checks if the parent is a union, since these rules already
|
||||
// traverse nested unions.
|
||||
if !checker.semantic.in_nested_union() {
|
||||
if checker.is_stub {
|
||||
if checker.enabled(Rule::DuplicateUnionMember)
|
||||
&& checker.semantic.in_type_definition()
|
||||
&& checker.semantic.in_type_definition()
|
||||
// Avoid duplicate checks if the parent is an `|`
|
||||
&& !matches!(
|
||||
checker.semantic.expr_parent(),
|
||||
Some(Expr::BinOp(ast::ExprBinOp { op: Operator::BitOr, ..}))
|
||||
)
|
||||
{
|
||||
flake8_pyi::rules::duplicate_union_member(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryLiteralUnion) {
|
||||
if checker.enabled(Rule::UnnecessaryLiteralUnion)
|
||||
// Avoid duplicate checks if the parent is an `|`
|
||||
&& !matches!(
|
||||
checker.semantic.expr_parent(),
|
||||
Some(Expr::BinOp(ast::ExprBinOp { op: Operator::BitOr, ..}))
|
||||
)
|
||||
{
|
||||
flake8_pyi::rules::unnecessary_literal_union(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::RedundantLiteralUnion) {
|
||||
flake8_pyi::rules::redundant_literal_union(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessaryTypeUnion) {
|
||||
flake8_pyi::rules::unnecessary_type_union(checker, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
Expr::UnaryOp(ast::ExprUnaryOp {
|
||||
@@ -1127,14 +1142,12 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
flake8_simplify::rules::double_negation(checker, expr, *op, operand);
|
||||
}
|
||||
}
|
||||
Expr::Compare(
|
||||
compare @ ast::ExprCompare {
|
||||
left,
|
||||
ops,
|
||||
comparators,
|
||||
range: _,
|
||||
},
|
||||
) => {
|
||||
Expr::Compare(ast::ExprCompare {
|
||||
left,
|
||||
ops,
|
||||
comparators,
|
||||
range: _,
|
||||
}) => {
|
||||
let check_none_comparisons = checker.enabled(Rule::NoneComparison);
|
||||
let check_true_false_comparisons = checker.enabled(Rule::TrueFalseComparison);
|
||||
if check_none_comparisons || check_true_false_comparisons {
|
||||
@@ -1152,7 +1165,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
pyflakes::rules::invalid_literal_comparison(checker, left, ops, comparators, expr);
|
||||
}
|
||||
if checker.enabled(Rule::TypeComparison) {
|
||||
pycodestyle::rules::type_comparison(checker, compare);
|
||||
pycodestyle::rules::type_comparison(checker, expr, ops, comparators);
|
||||
}
|
||||
if checker.any_enabled(&[
|
||||
Rule::SysVersionCmpStr3,
|
||||
@@ -1203,7 +1216,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
kind: _,
|
||||
range: _,
|
||||
}) => {
|
||||
if checker.source_type.is_stub() && checker.enabled(Rule::NumericLiteralTooLong) {
|
||||
if checker.is_stub && checker.enabled(Rule::NumericLiteralTooLong) {
|
||||
flake8_pyi::rules::numeric_literal_too_long(checker, expr);
|
||||
}
|
||||
}
|
||||
@@ -1212,7 +1225,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
kind: _,
|
||||
range: _,
|
||||
}) => {
|
||||
if checker.source_type.is_stub() && checker.enabled(Rule::StringOrBytesTooLong) {
|
||||
if checker.is_stub && checker.enabled(Rule::StringOrBytesTooLong) {
|
||||
flake8_pyi::rules::string_or_bytes_too_long(checker, expr);
|
||||
}
|
||||
}
|
||||
@@ -1229,12 +1242,18 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::HardcodedTempFile) {
|
||||
flake8_bandit::rules::hardcoded_tmp_directory(checker, expr, value);
|
||||
if let Some(diagnostic) = flake8_bandit::rules::hardcoded_tmp_directory(
|
||||
expr,
|
||||
value,
|
||||
&checker.settings.flake8_bandit.hardcoded_tmp_directory,
|
||||
) {
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::UnicodeKindPrefix) {
|
||||
pyupgrade::rules::unicode_kind_prefix(checker, expr, kind.as_deref());
|
||||
}
|
||||
if checker.source_type.is_stub() {
|
||||
if checker.is_stub {
|
||||
if checker.enabled(Rule::StringOrBytesTooLong) {
|
||||
flake8_pyi::rules::string_or_bytes_too_long(checker, expr);
|
||||
}
|
||||
@@ -1242,7 +1261,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
}
|
||||
Expr::Lambda(
|
||||
lambda @ ast::ExprLambda {
|
||||
parameters: _,
|
||||
args: _,
|
||||
body: _,
|
||||
range: _,
|
||||
},
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
pub(super) use argument::argument;
|
||||
pub(super) use arguments::arguments;
|
||||
pub(super) use bindings::bindings;
|
||||
pub(super) use comprehension::comprehension;
|
||||
pub(super) use deferred_for_loops::deferred_for_loops;
|
||||
@@ -6,12 +8,12 @@ pub(super) use definitions::definitions;
|
||||
pub(super) use except_handler::except_handler;
|
||||
pub(super) use expression::expression;
|
||||
pub(super) use module::module;
|
||||
pub(super) use parameter::parameter;
|
||||
pub(super) use parameters::parameters;
|
||||
pub(super) use statement::statement;
|
||||
pub(super) use suite::suite;
|
||||
pub(super) use unresolved_references::unresolved_references;
|
||||
|
||||
mod argument;
|
||||
mod arguments;
|
||||
mod bindings;
|
||||
mod comprehension;
|
||||
mod deferred_for_loops;
|
||||
@@ -20,8 +22,6 @@ mod definitions;
|
||||
mod except_handler;
|
||||
mod expression;
|
||||
mod module;
|
||||
mod parameter;
|
||||
mod parameters;
|
||||
mod statement;
|
||||
mod suite;
|
||||
mod unresolved_references;
|
||||
|
||||
@@ -53,7 +53,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::BreakOutsideLoop) {
|
||||
if let Some(diagnostic) = pyflakes::rules::break_outside_loop(
|
||||
stmt,
|
||||
&mut checker.semantic.current_statements().skip(1),
|
||||
&mut checker.semantic.parents().skip(1),
|
||||
) {
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
@@ -63,21 +63,27 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::ContinueOutsideLoop) {
|
||||
if let Some(diagnostic) = pyflakes::rules::continue_outside_loop(
|
||||
stmt,
|
||||
&mut checker.semantic.current_statements().skip(1),
|
||||
&mut checker.semantic.parents().skip(1),
|
||||
) {
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
Stmt::FunctionDef(ast::StmtFunctionDef {
|
||||
is_async,
|
||||
name,
|
||||
decorator_list,
|
||||
returns,
|
||||
parameters,
|
||||
args,
|
||||
body,
|
||||
type_params,
|
||||
range: _,
|
||||
..
|
||||
})
|
||||
| Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef {
|
||||
name,
|
||||
decorator_list,
|
||||
returns,
|
||||
args,
|
||||
body,
|
||||
..
|
||||
}) => {
|
||||
if checker.enabled(Rule::DjangoNonLeadingReceiverDecorator) {
|
||||
flake8_django::rules::non_leading_receiver_decorator(checker, decorator_list);
|
||||
@@ -105,10 +111,10 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
if let Some(diagnostic) =
|
||||
pep8_naming::rules::invalid_first_argument_name_for_class_method(
|
||||
checker,
|
||||
checker.semantic.current_scope(),
|
||||
checker.semantic.scope(),
|
||||
name,
|
||||
decorator_list,
|
||||
parameters,
|
||||
args,
|
||||
)
|
||||
{
|
||||
checker.diagnostics.push(diagnostic);
|
||||
@@ -117,15 +123,15 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::InvalidFirstArgumentNameForMethod) {
|
||||
if let Some(diagnostic) = pep8_naming::rules::invalid_first_argument_name_for_method(
|
||||
checker,
|
||||
checker.semantic.current_scope(),
|
||||
checker.semantic.scope(),
|
||||
name,
|
||||
decorator_list,
|
||||
parameters,
|
||||
args,
|
||||
) {
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if checker.source_type.is_stub() {
|
||||
if checker.is_stub {
|
||||
if checker.enabled(Rule::PassStatementStubBody) {
|
||||
flake8_pyi::rules::pass_statement_stub_body(checker, body);
|
||||
}
|
||||
@@ -135,52 +141,41 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::StubBodyMultipleStatements) {
|
||||
flake8_pyi::rules::stub_body_multiple_statements(checker, stmt, body);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::AnyEqNeAnnotation) {
|
||||
flake8_pyi::rules::any_eq_ne_annotation(checker, name, parameters);
|
||||
}
|
||||
if checker.enabled(Rule::NonSelfReturnType) {
|
||||
flake8_pyi::rules::non_self_return_type(
|
||||
checker,
|
||||
stmt,
|
||||
*is_async,
|
||||
name,
|
||||
decorator_list,
|
||||
returns.as_ref().map(AsRef::as_ref),
|
||||
parameters,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::CustomTypeVarReturnType) {
|
||||
flake8_pyi::rules::custom_type_var_return_type(
|
||||
checker,
|
||||
name,
|
||||
decorator_list,
|
||||
returns.as_ref().map(AsRef::as_ref),
|
||||
parameters,
|
||||
type_params.as_ref(),
|
||||
);
|
||||
}
|
||||
if checker.source_type.is_stub() {
|
||||
if checker.enabled(Rule::AnyEqNeAnnotation) {
|
||||
flake8_pyi::rules::any_eq_ne_annotation(checker, name, args);
|
||||
}
|
||||
if checker.enabled(Rule::NonSelfReturnType) {
|
||||
flake8_pyi::rules::non_self_return_type(
|
||||
checker,
|
||||
stmt,
|
||||
name,
|
||||
decorator_list,
|
||||
returns.as_ref().map(AsRef::as_ref),
|
||||
args,
|
||||
stmt.is_async_function_def_stmt(),
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::StrOrReprDefinedInStub) {
|
||||
flake8_pyi::rules::str_or_repr_defined_in_stub(checker, stmt);
|
||||
}
|
||||
}
|
||||
if checker.source_type.is_stub()
|
||||
|| checker.settings.target_version >= PythonVersion::Py311
|
||||
{
|
||||
if checker.enabled(Rule::NoReturnArgumentAnnotationInStub) {
|
||||
flake8_pyi::rules::no_return_argument_annotation(checker, parameters);
|
||||
flake8_pyi::rules::no_return_argument_annotation(checker, args);
|
||||
}
|
||||
if checker.enabled(Rule::BadExitAnnotation) {
|
||||
flake8_pyi::rules::bad_exit_annotation(
|
||||
checker,
|
||||
stmt.is_async_function_def_stmt(),
|
||||
name,
|
||||
args,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::RedundantNumericUnion) {
|
||||
flake8_pyi::rules::redundant_numeric_union(checker, args);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::BadExitAnnotation) {
|
||||
flake8_pyi::rules::bad_exit_annotation(checker, *is_async, name, parameters);
|
||||
}
|
||||
if checker.enabled(Rule::RedundantNumericUnion) {
|
||||
flake8_pyi::rules::redundant_numeric_union(checker, parameters);
|
||||
}
|
||||
if checker.enabled(Rule::DunderFunctionName) {
|
||||
if let Some(diagnostic) = pep8_naming::rules::dunder_function_name(
|
||||
checker.semantic.current_scope(),
|
||||
checker.semantic.scope(),
|
||||
stmt,
|
||||
name,
|
||||
&checker.settings.pep8_naming.ignore_names,
|
||||
@@ -235,13 +230,13 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::HardcodedPasswordDefault) {
|
||||
flake8_bandit::rules::hardcoded_password_default(checker, parameters);
|
||||
flake8_bandit::rules::hardcoded_password_default(checker, args);
|
||||
}
|
||||
if checker.enabled(Rule::PropertyWithParameters) {
|
||||
pylint::rules::property_with_parameters(checker, stmt, decorator_list, parameters);
|
||||
pylint::rules::property_with_parameters(checker, stmt, decorator_list, args);
|
||||
}
|
||||
if checker.enabled(Rule::TooManyArguments) {
|
||||
pylint::rules::too_many_arguments(checker, parameters, stmt);
|
||||
pylint::rules::too_many_arguments(checker, args, stmt);
|
||||
}
|
||||
if checker.enabled(Rule::TooManyReturnStatements) {
|
||||
if let Some(diagnostic) = pylint::rules::too_many_return_statements(
|
||||
@@ -287,7 +282,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
checker,
|
||||
stmt,
|
||||
name,
|
||||
parameters,
|
||||
args,
|
||||
decorator_list,
|
||||
body,
|
||||
);
|
||||
@@ -309,7 +304,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
checker,
|
||||
name,
|
||||
decorator_list,
|
||||
parameters,
|
||||
args,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::BooleanDefaultValueInFunctionDefinition) {
|
||||
@@ -317,7 +312,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
checker,
|
||||
name,
|
||||
decorator_list,
|
||||
parameters,
|
||||
args,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::UnexpectedSpecialMethodSignature) {
|
||||
@@ -326,7 +321,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
stmt,
|
||||
name,
|
||||
decorator_list,
|
||||
parameters,
|
||||
args,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::FStringDocstring) {
|
||||
@@ -335,7 +330,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::YieldInForLoop) {
|
||||
pyupgrade::rules::yield_in_for_loop(checker, stmt);
|
||||
}
|
||||
if let ScopeKind::Class(class_def) = checker.semantic.current_scope().kind {
|
||||
if let ScopeKind::Class(class_def) = checker.semantic.scope().kind {
|
||||
if checker.enabled(Rule::BuiltinAttributeShadowing) {
|
||||
flake8_builtins::rules::builtin_method_shadowing(
|
||||
checker,
|
||||
@@ -368,7 +363,8 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
Stmt::ClassDef(
|
||||
class_def @ ast::StmtClassDef {
|
||||
name,
|
||||
arguments,
|
||||
bases,
|
||||
keywords,
|
||||
type_params: _,
|
||||
decorator_list,
|
||||
body,
|
||||
@@ -379,36 +375,27 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
flake8_django::rules::nullable_model_string_field(checker, body);
|
||||
}
|
||||
if checker.enabled(Rule::DjangoExcludeWithModelForm) {
|
||||
if let Some(diagnostic) = flake8_django::rules::exclude_with_model_form(
|
||||
checker,
|
||||
arguments.as_deref(),
|
||||
body,
|
||||
) {
|
||||
if let Some(diagnostic) =
|
||||
flake8_django::rules::exclude_with_model_form(checker, bases, body)
|
||||
{
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::DjangoAllWithModelForm) {
|
||||
if let Some(diagnostic) =
|
||||
flake8_django::rules::all_with_model_form(checker, arguments.as_deref(), body)
|
||||
flake8_django::rules::all_with_model_form(checker, bases, body)
|
||||
{
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::DjangoUnorderedBodyContentInModel) {
|
||||
flake8_django::rules::unordered_body_content_in_model(
|
||||
checker,
|
||||
arguments.as_deref(),
|
||||
body,
|
||||
);
|
||||
flake8_django::rules::unordered_body_content_in_model(checker, bases, body);
|
||||
}
|
||||
if !checker.source_type.is_stub() {
|
||||
if !checker.is_stub {
|
||||
if checker.enabled(Rule::DjangoModelWithoutDunderStr) {
|
||||
flake8_django::rules::model_without_dunder_str(checker, class_def);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::EqWithoutHash) {
|
||||
pylint::rules::object_without_hash_method(checker, class_def);
|
||||
}
|
||||
if checker.enabled(Rule::GlobalStatement) {
|
||||
pylint::rules::global_statement(checker, name);
|
||||
}
|
||||
@@ -435,37 +422,33 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::ErrorSuffixOnExceptionName) {
|
||||
if let Some(diagnostic) = pep8_naming::rules::error_suffix_on_exception_name(
|
||||
stmt,
|
||||
arguments.as_deref(),
|
||||
bases,
|
||||
name,
|
||||
&checker.settings.pep8_naming.ignore_names,
|
||||
) {
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if !checker.source_type.is_stub() {
|
||||
if !checker.is_stub {
|
||||
if checker.any_enabled(&[
|
||||
Rule::AbstractBaseClassWithoutAbstractMethod,
|
||||
Rule::EmptyMethodWithoutAbstractDecorator,
|
||||
]) {
|
||||
flake8_bugbear::rules::abstract_base_class(
|
||||
checker,
|
||||
stmt,
|
||||
name,
|
||||
arguments.as_deref(),
|
||||
body,
|
||||
checker, stmt, name, bases, keywords, body,
|
||||
);
|
||||
}
|
||||
}
|
||||
if checker.source_type.is_stub() {
|
||||
if checker.is_stub {
|
||||
if checker.enabled(Rule::PassStatementStubBody) {
|
||||
flake8_pyi::rules::pass_statement_stub_body(checker, body);
|
||||
}
|
||||
if checker.enabled(Rule::PassInClassBody) {
|
||||
flake8_pyi::rules::pass_in_class_body(checker, stmt, body);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::EllipsisInNonEmptyClassBody) {
|
||||
flake8_pyi::rules::ellipsis_in_non_empty_class_body(checker, stmt, body);
|
||||
if checker.enabled(Rule::EllipsisInNonEmptyClassBody) {
|
||||
flake8_pyi::rules::ellipsis_in_non_empty_class_body(checker, stmt, body);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::PytestIncorrectMarkParenthesesStyle) {
|
||||
flake8_pytest_style::rules::marks(checker, decorator_list);
|
||||
@@ -492,7 +475,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
flake8_builtins::rules::builtin_variable_shadowing(checker, name, name.range());
|
||||
}
|
||||
if checker.enabled(Rule::DuplicateBases) {
|
||||
pylint::rules::duplicate_bases(checker, name, arguments.as_deref());
|
||||
pylint::rules::duplicate_bases(checker, name, bases);
|
||||
}
|
||||
if checker.enabled(Rule::NoSlotsInStrSubclass) {
|
||||
flake8_slots::rules::no_slots_in_str_subclass(checker, stmt, class_def);
|
||||
@@ -558,7 +541,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
alias,
|
||||
);
|
||||
}
|
||||
if !checker.source_type.is_stub() {
|
||||
if !checker.is_stub {
|
||||
if checker.enabled(Rule::UselessImportAlias) {
|
||||
pylint::rules::useless_import_alias(checker, alias);
|
||||
}
|
||||
@@ -733,7 +716,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if checker.source_type.is_stub() {
|
||||
if checker.is_stub {
|
||||
if checker.enabled(Rule::FutureAnnotationsInStub) {
|
||||
flake8_pyi::rules::from_future_import(checker, import_from);
|
||||
}
|
||||
@@ -753,7 +736,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
}
|
||||
} else if &alias.name == "*" {
|
||||
if checker.enabled(Rule::UndefinedLocalWithNestedImportStarUsage) {
|
||||
if !matches!(checker.semantic.current_scope().kind, ScopeKind::Module) {
|
||||
if !matches!(checker.semantic.scope().kind, ScopeKind::Module) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
pyflakes::rules::UndefinedLocalWithNestedImportStarUsage {
|
||||
name: helpers::format_import_from(level, module),
|
||||
@@ -878,7 +861,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if !checker.source_type.is_stub() {
|
||||
if !checker.is_stub {
|
||||
if checker.enabled(Rule::UselessImportAlias) {
|
||||
pylint::rules::useless_import_alias(checker, alias);
|
||||
}
|
||||
@@ -969,7 +952,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
flake8_simplify::rules::nested_if_statements(
|
||||
checker,
|
||||
if_,
|
||||
checker.semantic.current_statement_parent(),
|
||||
checker.semantic.stmt_parent(),
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::IfWithSameArms) {
|
||||
@@ -991,7 +974,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
tryceratops::rules::type_check_without_type_error(
|
||||
checker,
|
||||
if_,
|
||||
checker.semantic.current_statement_parent(),
|
||||
checker.semantic.stmt_parent(),
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::OutdatedVersionBlock) {
|
||||
@@ -1002,7 +985,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if checker.source_type.is_stub() {
|
||||
if checker.is_stub {
|
||||
if checker.any_enabled(&[
|
||||
Rule::UnrecognizedVersionInfoCheck,
|
||||
Rule::PatchVersionComparison,
|
||||
@@ -1084,7 +1067,8 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
pygrep_hooks::rules::non_existent_mock_method(checker, test);
|
||||
}
|
||||
}
|
||||
Stmt::With(with_ @ ast::StmtWith { items, body, .. }) => {
|
||||
Stmt::With(ast::StmtWith { items, body, .. })
|
||||
| Stmt::AsyncWith(ast::StmtAsyncWith { items, body, .. }) => {
|
||||
if checker.enabled(Rule::AssertRaisesException) {
|
||||
flake8_bugbear::rules::assert_raises_exception(checker, items);
|
||||
}
|
||||
@@ -1094,8 +1078,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::MultipleWithStatements) {
|
||||
flake8_simplify::rules::multiple_with_statements(
|
||||
checker,
|
||||
with_,
|
||||
checker.semantic.current_statement_parent(),
|
||||
stmt,
|
||||
body,
|
||||
checker.semantic.stmt_parent(),
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::RedefinedLoopName) {
|
||||
@@ -1119,6 +1104,13 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
iter,
|
||||
orelse,
|
||||
..
|
||||
})
|
||||
| Stmt::AsyncFor(ast::StmtAsyncFor {
|
||||
target,
|
||||
body,
|
||||
iter,
|
||||
orelse,
|
||||
..
|
||||
}) => {
|
||||
if checker.any_enabled(&[Rule::UnusedLoopControlVariable, Rule::IncorrectDictIterator])
|
||||
{
|
||||
@@ -1305,7 +1297,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
if checker.settings.rules.enabled(Rule::TypeBivariance) {
|
||||
pylint::rules::type_bivariance(checker, value);
|
||||
}
|
||||
if checker.source_type.is_stub() {
|
||||
if checker.is_stub {
|
||||
if checker.any_enabled(&[
|
||||
Rule::UnprefixedTypeParam,
|
||||
Rule::AssignmentDefaultInStub,
|
||||
@@ -1316,8 +1308,8 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
// Ignore assignments in function bodies; those are covered by other rules.
|
||||
if !checker
|
||||
.semantic
|
||||
.current_scopes()
|
||||
.any(|scope| scope.kind.is_function())
|
||||
.scopes()
|
||||
.any(|scope| scope.kind.is_any_function())
|
||||
{
|
||||
if checker.enabled(Rule::UnprefixedTypeParam) {
|
||||
flake8_pyi::rules::prefix_type_params(checker, value, targets);
|
||||
@@ -1342,14 +1334,12 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
}
|
||||
Stmt::AnnAssign(
|
||||
assign_stmt @ ast::StmtAnnAssign {
|
||||
target,
|
||||
value,
|
||||
annotation,
|
||||
..
|
||||
},
|
||||
) => {
|
||||
Stmt::AnnAssign(ast::StmtAnnAssign {
|
||||
target,
|
||||
value,
|
||||
annotation,
|
||||
..
|
||||
}) => {
|
||||
if let Some(value) = value {
|
||||
if checker.enabled(Rule::LambdaAssignment) {
|
||||
pycodestyle::rules::lambda_assignment(
|
||||
@@ -1372,17 +1362,14 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
stmt,
|
||||
);
|
||||
}
|
||||
if checker.enabled(Rule::NonPEP695TypeAlias) {
|
||||
pyupgrade::rules::non_pep695_type_alias(checker, assign_stmt);
|
||||
}
|
||||
if checker.source_type.is_stub() {
|
||||
if checker.is_stub {
|
||||
if let Some(value) = value {
|
||||
if checker.enabled(Rule::AssignmentDefaultInStub) {
|
||||
// Ignore assignments in function bodies; those are covered by other rules.
|
||||
if !checker
|
||||
.semantic
|
||||
.current_scopes()
|
||||
.any(|scope| scope.kind.is_function())
|
||||
.scopes()
|
||||
.any(|scope| scope.kind.is_any_function())
|
||||
{
|
||||
flake8_pyi::rules::annotated_assignment_default_in_stub(
|
||||
checker, target, value, annotation,
|
||||
@@ -1396,13 +1383,13 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if checker.semantic.match_typing_expr(annotation, "TypeAlias") {
|
||||
if checker.enabled(Rule::SnakeCaseTypeAlias) {
|
||||
flake8_pyi::rules::snake_case_type_alias(checker, target);
|
||||
}
|
||||
if checker.enabled(Rule::TSuffixedTypeAlias) {
|
||||
flake8_pyi::rules::t_suffixed_type_alias(checker, target);
|
||||
if checker.semantic.match_typing_expr(annotation, "TypeAlias") {
|
||||
if checker.enabled(Rule::SnakeCaseTypeAlias) {
|
||||
flake8_pyi::rules::snake_case_type_alias(checker, target);
|
||||
}
|
||||
if checker.enabled(Rule::TSuffixedTypeAlias) {
|
||||
flake8_pyi::rules::t_suffixed_type_alias(checker, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use ruff_python_ast::{Expr, TypeParam};
|
||||
use ruff_python_semantic::{ScopeId, Snapshot};
|
||||
use ruff_python_ast::Expr;
|
||||
use ruff_text_size::TextRange;
|
||||
|
||||
use ruff_python_semantic::{ScopeId, Snapshot};
|
||||
|
||||
/// A collection of AST nodes that are deferred for later analysis.
|
||||
/// Used to, e.g., store functions, whose bodies shouldn't be analyzed until all
|
||||
/// module-level definitions have been analyzed.
|
||||
@@ -10,7 +11,6 @@ pub(crate) struct Deferred<'a> {
|
||||
pub(crate) scopes: Vec<ScopeId>,
|
||||
pub(crate) string_type_definitions: Vec<(TextRange, &'a str, Snapshot)>,
|
||||
pub(crate) future_type_definitions: Vec<(&'a Expr, Snapshot)>,
|
||||
pub(crate) type_param_definitions: Vec<(&'a TypeParam, Snapshot)>,
|
||||
pub(crate) functions: Vec<Snapshot>,
|
||||
pub(crate) lambdas: Vec<(&'a Expr, Snapshot)>,
|
||||
pub(crate) for_loops: Vec<Snapshot>,
|
||||
|
||||
@@ -31,21 +31,18 @@ use std::path::Path;
|
||||
use itertools::Itertools;
|
||||
use log::error;
|
||||
use ruff_python_ast::{
|
||||
self as ast, Arguments, Comprehension, Constant, ElifElseClause, ExceptHandler, Expr,
|
||||
ExprContext, Keyword, Parameter, ParameterWithDefault, Parameters, Pattern, Ranged, Stmt,
|
||||
Suite, UnaryOp,
|
||||
self as ast, Arg, ArgWithDefault, Arguments, Comprehension, Constant, ElifElseClause,
|
||||
ExceptHandler, Expr, ExprContext, Keyword, Pattern, Ranged, Stmt, Suite, UnaryOp,
|
||||
};
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, IsolationLevel};
|
||||
use ruff_python_ast::all::{extract_all_names, DunderAllFlags};
|
||||
use ruff_python_ast::helpers::{
|
||||
collect_import_from_member, extract_handled_exceptions, to_module_path,
|
||||
};
|
||||
use ruff_python_ast::helpers::{extract_handled_exceptions, to_module_path};
|
||||
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::{helpers, str, visitor, PySourceType};
|
||||
use ruff_python_ast::{helpers, str, visitor};
|
||||
use ruff_python_codegen::{Generator, Quote, Stylist};
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_python_parser::typing::{parse_type_annotation, AnnotationKind};
|
||||
@@ -55,6 +52,7 @@ use ruff_python_semantic::{
|
||||
ModuleKind, ScopeId, ScopeKind, SemanticModel, SemanticModelFlags, StarImport, SubmoduleImport,
|
||||
};
|
||||
use ruff_python_stdlib::builtins::{BUILTINS, MAGIC_GLOBALS};
|
||||
use ruff_python_stdlib::path::is_python_stub_file;
|
||||
use ruff_source_file::Locator;
|
||||
|
||||
use crate::checkers::ast::deferred::Deferred;
|
||||
@@ -76,8 +74,8 @@ pub(crate) struct Checker<'a> {
|
||||
package: Option<&'a Path>,
|
||||
/// The module representation of the current file (e.g., `foo.bar`).
|
||||
module_path: Option<&'a [String]>,
|
||||
/// The [`PySourceType`] of the current file.
|
||||
pub(crate) source_type: PySourceType,
|
||||
/// Whether the current file is a stub (`.pyi`) file.
|
||||
is_stub: bool,
|
||||
/// The [`flags::Noqa`] for the current analysis (i.e., whether to respect suppression
|
||||
/// comments).
|
||||
noqa: flags::Noqa,
|
||||
@@ -119,7 +117,6 @@ impl<'a> Checker<'a> {
|
||||
stylist: &'a Stylist,
|
||||
indexer: &'a Indexer,
|
||||
importer: Importer<'a>,
|
||||
source_type: PySourceType,
|
||||
) -> Checker<'a> {
|
||||
Checker {
|
||||
settings,
|
||||
@@ -128,7 +125,7 @@ impl<'a> Checker<'a> {
|
||||
path,
|
||||
package,
|
||||
module_path: module.path(),
|
||||
source_type,
|
||||
is_stub: is_python_stub_file(path),
|
||||
locator,
|
||||
stylist,
|
||||
indexer,
|
||||
@@ -176,12 +173,13 @@ impl<'a> Checker<'a> {
|
||||
///
|
||||
/// If the current expression in the context is not an f-string, returns ``None``.
|
||||
pub(crate) fn f_string_quote_style(&self) -> Option<Quote> {
|
||||
if !self.semantic.in_f_string() {
|
||||
let model = &self.semantic;
|
||||
if !model.in_f_string() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Find the quote character used to start the containing f-string.
|
||||
let expr = self.semantic.current_expression()?;
|
||||
let expr = model.expr()?;
|
||||
let string_range = self.indexer.f_string_range(expr.start())?;
|
||||
let trailing_quote = trailing_quote(self.locator.slice(string_range))?;
|
||||
|
||||
@@ -201,7 +199,7 @@ impl<'a> Checker<'a> {
|
||||
/// thus be applied whenever we delete a statement, but can otherwise be omitted.
|
||||
pub(crate) fn isolation(&self, parent: Option<&Stmt>) -> IsolationLevel {
|
||||
parent
|
||||
.and_then(|stmt| self.semantic.statement_id(stmt))
|
||||
.and_then(|stmt| self.semantic.stmts.node_id(stmt))
|
||||
.map_or(IsolationLevel::default(), |node_id| {
|
||||
IsolationLevel::Group(node_id.into())
|
||||
})
|
||||
@@ -263,7 +261,7 @@ where
|
||||
{
|
||||
fn visit_stmt(&mut self, stmt: &'b Stmt) {
|
||||
// Step 0: Pre-processing
|
||||
self.semantic.push_statement(stmt);
|
||||
self.semantic.push_stmt(stmt);
|
||||
|
||||
// Track whether we've seen docstrings, non-imports, etc.
|
||||
match stmt {
|
||||
@@ -287,7 +285,7 @@ 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())
|
||||
&& !helpers::in_nested_block(self.semantic.parents())
|
||||
{
|
||||
self.semantic.flags |= SemanticModelFlags::IMPORT_BOUNDARY;
|
||||
}
|
||||
@@ -321,11 +319,11 @@ where
|
||||
// Given `import foo.bar`, `name` would be "foo", and `qualified_name` would be
|
||||
// "foo.bar".
|
||||
let name = alias.name.split('.').next().unwrap();
|
||||
let call_path: Box<[&str]> = alias.name.split('.').collect();
|
||||
let qualified_name = &alias.name;
|
||||
self.add_binding(
|
||||
name,
|
||||
alias.identifier(),
|
||||
BindingKind::SubmoduleImport(SubmoduleImport { call_path }),
|
||||
BindingKind::SubmoduleImport(SubmoduleImport { qualified_name }),
|
||||
BindingFlags::EXTERNAL,
|
||||
);
|
||||
} else {
|
||||
@@ -336,17 +334,17 @@ where
|
||||
if alias
|
||||
.asname
|
||||
.as_ref()
|
||||
.is_some_and(|asname| asname.as_str() == alias.name.as_str())
|
||||
.map_or(false, |asname| asname.as_str() == alias.name.as_str())
|
||||
{
|
||||
flags |= BindingFlags::EXPLICIT_EXPORT;
|
||||
}
|
||||
|
||||
let name = alias.asname.as_ref().unwrap_or(&alias.name);
|
||||
let call_path: Box<[&str]> = alias.name.split('.').collect();
|
||||
let qualified_name = &alias.name;
|
||||
self.add_binding(
|
||||
name,
|
||||
alias.identifier(),
|
||||
BindingKind::Import(Import { call_path }),
|
||||
BindingKind::Import(Import { qualified_name }),
|
||||
flags,
|
||||
);
|
||||
}
|
||||
@@ -371,7 +369,7 @@ where
|
||||
);
|
||||
} else if &alias.name == "*" {
|
||||
self.semantic
|
||||
.current_scope_mut()
|
||||
.scope_mut()
|
||||
.add_star_import(StarImport { level, module });
|
||||
} else {
|
||||
let mut flags = BindingFlags::EXTERNAL;
|
||||
@@ -381,7 +379,7 @@ where
|
||||
if alias
|
||||
.asname
|
||||
.as_ref()
|
||||
.is_some_and(|asname| asname.as_str() == alias.name.as_str())
|
||||
.map_or(false, |asname| asname.as_str() == alias.name.as_str())
|
||||
{
|
||||
flags |= BindingFlags::EXPLICIT_EXPORT;
|
||||
}
|
||||
@@ -390,16 +388,12 @@ where
|
||||
// be "foo.bar". Given `from foo import bar as baz`, `name` would be "baz"
|
||||
// and `qualified_name` would be "foo.bar".
|
||||
let name = alias.asname.as_ref().unwrap_or(&alias.name);
|
||||
|
||||
// Attempt to resolve any relative imports; but if we don't know the current
|
||||
// module path, or the relative import extends beyond the package root,
|
||||
// fallback to a literal representation (e.g., `[".", "foo"]`).
|
||||
let call_path = collect_import_from_member(level, module, &alias.name)
|
||||
.into_boxed_slice();
|
||||
let qualified_name =
|
||||
helpers::format_import_from_member(level, module, &alias.name);
|
||||
self.add_binding(
|
||||
name,
|
||||
alias.identifier(),
|
||||
BindingKind::FromImport(FromImport { call_path }),
|
||||
BindingKind::FromImport(FromImport { qualified_name }),
|
||||
flags,
|
||||
);
|
||||
}
|
||||
@@ -420,7 +414,7 @@ where
|
||||
BindingKind::Global,
|
||||
BindingFlags::GLOBAL,
|
||||
);
|
||||
let scope = self.semantic.current_scope_mut();
|
||||
let scope = self.semantic.scope_mut();
|
||||
scope.add(name, binding_id);
|
||||
}
|
||||
}
|
||||
@@ -443,7 +437,7 @@ where
|
||||
BindingKind::Nonlocal(scope_id),
|
||||
BindingFlags::NONLOCAL,
|
||||
);
|
||||
let scope = self.semantic.current_scope_mut();
|
||||
let scope = self.semantic.scope_mut();
|
||||
scope.add(name, binding_id);
|
||||
}
|
||||
}
|
||||
@@ -454,16 +448,20 @@ where
|
||||
|
||||
// Step 2: Traversal
|
||||
match stmt {
|
||||
Stmt::FunctionDef(
|
||||
function_def @ ast::StmtFunctionDef {
|
||||
body,
|
||||
parameters,
|
||||
decorator_list,
|
||||
returns,
|
||||
type_params,
|
||||
..
|
||||
},
|
||||
) => {
|
||||
Stmt::FunctionDef(ast::StmtFunctionDef {
|
||||
body,
|
||||
args,
|
||||
decorator_list,
|
||||
returns,
|
||||
..
|
||||
})
|
||||
| Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef {
|
||||
body,
|
||||
args,
|
||||
decorator_list,
|
||||
returns,
|
||||
..
|
||||
}) => {
|
||||
// Visit the decorators and arguments, but avoid the body, which will be
|
||||
// deferred.
|
||||
for decorator in decorator_list {
|
||||
@@ -474,30 +472,24 @@ where
|
||||
// are enabled.
|
||||
let runtime_annotation = !self.semantic.future_annotations();
|
||||
|
||||
self.semantic.push_scope(ScopeKind::Type);
|
||||
|
||||
if let Some(type_params) = type_params {
|
||||
self.visit_type_params(type_params);
|
||||
}
|
||||
|
||||
for parameter_with_default in parameters
|
||||
for arg_with_default in args
|
||||
.posonlyargs
|
||||
.iter()
|
||||
.chain(¶meters.args)
|
||||
.chain(¶meters.kwonlyargs)
|
||||
.chain(&args.args)
|
||||
.chain(&args.kwonlyargs)
|
||||
{
|
||||
if let Some(expr) = ¶meter_with_default.parameter.annotation {
|
||||
if let Some(expr) = &arg_with_default.def.annotation {
|
||||
if runtime_annotation {
|
||||
self.visit_runtime_annotation(expr);
|
||||
} else {
|
||||
self.visit_annotation(expr);
|
||||
};
|
||||
}
|
||||
if let Some(expr) = ¶meter_with_default.default {
|
||||
if let Some(expr) = &arg_with_default.default {
|
||||
self.visit_expr(expr);
|
||||
}
|
||||
}
|
||||
if let Some(arg) = ¶meters.vararg {
|
||||
if let Some(arg) = &args.vararg {
|
||||
if let Some(expr) = &arg.annotation {
|
||||
if runtime_annotation {
|
||||
self.visit_runtime_annotation(expr);
|
||||
@@ -506,7 +498,7 @@ where
|
||||
};
|
||||
}
|
||||
}
|
||||
if let Some(arg) = ¶meters.kwarg {
|
||||
if let Some(arg) = &args.kwarg {
|
||||
if let Some(expr) = &arg.annotation {
|
||||
if runtime_annotation {
|
||||
self.visit_runtime_annotation(expr);
|
||||
@@ -524,7 +516,8 @@ where
|
||||
}
|
||||
|
||||
let definition = docstrings::extraction::extract_definition(
|
||||
ExtractionTarget::Function(function_def),
|
||||
ExtractionTarget::Function,
|
||||
stmt,
|
||||
self.semantic.definition_id,
|
||||
&self.semantic.definitions,
|
||||
);
|
||||
@@ -532,7 +525,8 @@ where
|
||||
|
||||
self.semantic.push_scope(match &stmt {
|
||||
Stmt::FunctionDef(stmt) => ScopeKind::Function(stmt),
|
||||
_ => unreachable!("Expected Stmt::FunctionDef"),
|
||||
Stmt::AsyncFunctionDef(stmt) => ScopeKind::AsyncFunction(stmt),
|
||||
_ => unreachable!("Expected Stmt::FunctionDef | Stmt::AsyncFunctionDef"),
|
||||
});
|
||||
|
||||
self.deferred.functions.push(self.semantic.snapshot());
|
||||
@@ -545,32 +539,30 @@ where
|
||||
Stmt::ClassDef(
|
||||
class_def @ ast::StmtClassDef {
|
||||
body,
|
||||
arguments,
|
||||
bases,
|
||||
keywords,
|
||||
decorator_list,
|
||||
type_params,
|
||||
..
|
||||
},
|
||||
) => {
|
||||
for expr in bases {
|
||||
self.visit_expr(expr);
|
||||
}
|
||||
for keyword in keywords {
|
||||
self.visit_keyword(keyword);
|
||||
}
|
||||
for decorator in decorator_list {
|
||||
self.visit_decorator(decorator);
|
||||
}
|
||||
|
||||
self.semantic.push_scope(ScopeKind::Type);
|
||||
|
||||
if let Some(type_params) = type_params {
|
||||
self.visit_type_params(type_params);
|
||||
}
|
||||
|
||||
if let Some(arguments) = arguments {
|
||||
self.visit_arguments(arguments);
|
||||
}
|
||||
|
||||
let definition = docstrings::extraction::extract_definition(
|
||||
ExtractionTarget::Class(class_def),
|
||||
ExtractionTarget::Class,
|
||||
stmt,
|
||||
self.semantic.definition_id,
|
||||
&self.semantic.definitions,
|
||||
);
|
||||
self.semantic.push_definition(definition);
|
||||
|
||||
self.semantic.push_scope(ScopeKind::Class(class_def));
|
||||
|
||||
// Extract any global bindings from the class body.
|
||||
@@ -580,20 +572,6 @@ where
|
||||
|
||||
self.visit_body(body);
|
||||
}
|
||||
Stmt::TypeAlias(ast::StmtTypeAlias {
|
||||
range: _,
|
||||
name,
|
||||
type_params,
|
||||
value,
|
||||
}) => {
|
||||
self.semantic.push_scope(ScopeKind::Type);
|
||||
if let Some(type_params) = type_params {
|
||||
self.visit_type_params(type_params);
|
||||
}
|
||||
self.visit_expr(value);
|
||||
self.semantic.pop_scope();
|
||||
self.visit_expr(name);
|
||||
}
|
||||
Stmt::Try(ast::StmtTry {
|
||||
body,
|
||||
handlers,
|
||||
@@ -647,7 +625,7 @@ where
|
||||
// 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() {
|
||||
if self.semantic.scope().kind.is_class() {
|
||||
let baseclasses = &self
|
||||
.settings
|
||||
.flake8_type_checking
|
||||
@@ -666,7 +644,7 @@ where
|
||||
}
|
||||
} else {
|
||||
matches!(
|
||||
self.semantic.current_scope().kind,
|
||||
self.semantic.scope().kind,
|
||||
ScopeKind::Class(_) | ScopeKind::Module
|
||||
)
|
||||
};
|
||||
@@ -733,12 +711,12 @@ where
|
||||
|
||||
// Step 3: Clean-up
|
||||
match stmt {
|
||||
Stmt::FunctionDef(ast::StmtFunctionDef { name, .. }) => {
|
||||
Stmt::FunctionDef(ast::StmtFunctionDef { name, .. })
|
||||
| Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef { name, .. }) => {
|
||||
let scope_id = self.semantic.scope_id;
|
||||
self.deferred.scopes.push(scope_id);
|
||||
self.semantic.pop_scope(); // Function scope
|
||||
self.semantic.pop_scope();
|
||||
self.semantic.pop_definition();
|
||||
self.semantic.pop_scope(); // Type parameter scope
|
||||
self.add_binding(
|
||||
name,
|
||||
stmt.identifier(),
|
||||
@@ -749,9 +727,8 @@ where
|
||||
Stmt::ClassDef(ast::StmtClassDef { name, .. }) => {
|
||||
let scope_id = self.semantic.scope_id;
|
||||
self.deferred.scopes.push(scope_id);
|
||||
self.semantic.pop_scope(); // Class scope
|
||||
self.semantic.pop_scope();
|
||||
self.semantic.pop_definition();
|
||||
self.semantic.pop_scope(); // Type parameter scope
|
||||
self.add_binding(
|
||||
name,
|
||||
stmt.identifier(),
|
||||
@@ -766,7 +743,7 @@ where
|
||||
analyze::statement(stmt, self);
|
||||
|
||||
self.semantic.flags = flags_snapshot;
|
||||
self.semantic.pop_statement();
|
||||
self.semantic.pop_stmt();
|
||||
}
|
||||
|
||||
fn visit_annotation(&mut self, expr: &'b Expr) {
|
||||
@@ -802,7 +779,7 @@ where
|
||||
return;
|
||||
}
|
||||
|
||||
self.semantic.push_expression(expr);
|
||||
self.semantic.push_expr(expr);
|
||||
|
||||
// Store the flags prior to any further descent, so that we can restore them after visiting
|
||||
// the node.
|
||||
@@ -825,12 +802,13 @@ where
|
||||
match expr {
|
||||
Expr::Call(ast::ExprCall {
|
||||
func,
|
||||
arguments: _,
|
||||
args: _,
|
||||
keywords: _,
|
||||
range: _,
|
||||
}) => {
|
||||
if let Expr::Name(ast::ExprName { id, ctx, range: _ }) = func.as_ref() {
|
||||
if id == "locals" && ctx.is_load() {
|
||||
let scope = self.semantic.current_scope_mut();
|
||||
let scope = self.semantic.scope_mut();
|
||||
scope.set_uses_locals();
|
||||
}
|
||||
}
|
||||
@@ -875,21 +853,21 @@ where
|
||||
}
|
||||
Expr::Lambda(
|
||||
lambda @ ast::ExprLambda {
|
||||
parameters,
|
||||
args,
|
||||
body: _,
|
||||
range: _,
|
||||
},
|
||||
) => {
|
||||
// Visit the default arguments, but avoid the body, which will be deferred.
|
||||
for ParameterWithDefault {
|
||||
for ArgWithDefault {
|
||||
default,
|
||||
parameter: _,
|
||||
def: _,
|
||||
range: _,
|
||||
} in parameters
|
||||
} in args
|
||||
.posonlyargs
|
||||
.iter()
|
||||
.chain(¶meters.args)
|
||||
.chain(¶meters.kwonlyargs)
|
||||
.chain(&args.args)
|
||||
.chain(&args.kwonlyargs)
|
||||
{
|
||||
if let Some(expr) = &default {
|
||||
self.visit_expr(expr);
|
||||
@@ -911,12 +889,8 @@ where
|
||||
}
|
||||
Expr::Call(ast::ExprCall {
|
||||
func,
|
||||
arguments:
|
||||
Arguments {
|
||||
args,
|
||||
keywords,
|
||||
range: _,
|
||||
},
|
||||
args,
|
||||
keywords,
|
||||
range: _,
|
||||
}) => {
|
||||
self.visit_expr(func);
|
||||
@@ -1094,7 +1068,7 @@ where
|
||||
arg,
|
||||
range: _,
|
||||
} = keyword;
|
||||
if arg.as_ref().is_some_and(|arg| arg == "type") {
|
||||
if arg.as_ref().map_or(false, |arg| arg == "type") {
|
||||
self.visit_type_definition(value);
|
||||
} else {
|
||||
self.visit_non_type_definition(value);
|
||||
@@ -1196,7 +1170,7 @@ where
|
||||
));
|
||||
}
|
||||
}
|
||||
Expr::FString(_) => {
|
||||
Expr::JoinedStr(_) => {
|
||||
self.semantic.flags |= SemanticModelFlags::F_STRING;
|
||||
visitor::walk_expr(self, expr);
|
||||
}
|
||||
@@ -1220,7 +1194,7 @@ where
|
||||
analyze::expression(expr, self);
|
||||
|
||||
self.semantic.flags = flags_snapshot;
|
||||
self.semantic.pop_expression();
|
||||
self.semantic.pop_expr();
|
||||
}
|
||||
|
||||
fn visit_except_handler(&mut self, except_handler: &'b ExceptHandler) {
|
||||
@@ -1275,7 +1249,7 @@ where
|
||||
|
||||
fn visit_format_spec(&mut self, format_spec: &'b Expr) {
|
||||
match format_spec {
|
||||
Expr::FString(ast::ExprFString { values, range: _ }) => {
|
||||
Expr::JoinedStr(ast::ExprJoinedStr { values, range: _ }) => {
|
||||
for value in values {
|
||||
self.visit_expr(value);
|
||||
}
|
||||
@@ -1284,43 +1258,43 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_parameters(&mut self, parameters: &'b Parameters) {
|
||||
fn visit_arguments(&mut self, arguments: &'b Arguments) {
|
||||
// Step 1: Binding.
|
||||
// Bind, but intentionally avoid walking default expressions, as we handle them
|
||||
// upstream.
|
||||
for parameter_with_default in ¶meters.posonlyargs {
|
||||
self.visit_parameter(¶meter_with_default.parameter);
|
||||
for arg_with_default in &arguments.posonlyargs {
|
||||
self.visit_arg(&arg_with_default.def);
|
||||
}
|
||||
for parameter_with_default in ¶meters.args {
|
||||
self.visit_parameter(¶meter_with_default.parameter);
|
||||
for arg_with_default in &arguments.args {
|
||||
self.visit_arg(&arg_with_default.def);
|
||||
}
|
||||
if let Some(arg) = ¶meters.vararg {
|
||||
self.visit_parameter(arg);
|
||||
if let Some(arg) = &arguments.vararg {
|
||||
self.visit_arg(arg);
|
||||
}
|
||||
for parameter_with_default in ¶meters.kwonlyargs {
|
||||
self.visit_parameter(¶meter_with_default.parameter);
|
||||
for arg_with_default in &arguments.kwonlyargs {
|
||||
self.visit_arg(&arg_with_default.def);
|
||||
}
|
||||
if let Some(arg) = ¶meters.kwarg {
|
||||
self.visit_parameter(arg);
|
||||
if let Some(arg) = &arguments.kwarg {
|
||||
self.visit_arg(arg);
|
||||
}
|
||||
|
||||
// Step 4: Analysis
|
||||
analyze::parameters(parameters, self);
|
||||
analyze::arguments(arguments, self);
|
||||
}
|
||||
|
||||
fn visit_parameter(&mut self, parameter: &'b Parameter) {
|
||||
fn visit_arg(&mut self, arg: &'b Arg) {
|
||||
// Step 1: Binding.
|
||||
// Bind, but intentionally avoid walking the annotation, as we handle it
|
||||
// upstream.
|
||||
self.add_binding(
|
||||
¶meter.name,
|
||||
parameter.identifier(),
|
||||
&arg.arg,
|
||||
arg.identifier(),
|
||||
BindingKind::Argument,
|
||||
BindingFlags::empty(),
|
||||
);
|
||||
|
||||
// Step 4: Analysis
|
||||
analyze::parameter(parameter, self);
|
||||
analyze::argument(arg, self);
|
||||
}
|
||||
|
||||
fn visit_pattern(&mut self, pattern: &'b Pattern) {
|
||||
@@ -1357,26 +1331,6 @@ where
|
||||
self.visit_stmt(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_type_param(&mut self, type_param: &'b ast::TypeParam) {
|
||||
// Step 1: Binding
|
||||
match type_param {
|
||||
ast::TypeParam::TypeVar(ast::TypeParamTypeVar { name, range, .. })
|
||||
| ast::TypeParam::TypeVarTuple(ast::TypeParamTypeVarTuple { name, range })
|
||||
| ast::TypeParam::ParamSpec(ast::TypeParamParamSpec { name, range }) => {
|
||||
self.add_binding(
|
||||
name.as_str(),
|
||||
*range,
|
||||
BindingKind::TypeParam,
|
||||
BindingFlags::empty(),
|
||||
);
|
||||
}
|
||||
}
|
||||
// Step 2: Traversal
|
||||
self.deferred
|
||||
.type_param_definitions
|
||||
.push((type_param, self.semantic.snapshot()));
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Checker<'a> {
|
||||
@@ -1593,14 +1547,14 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
|
||||
fn handle_node_load(&mut self, expr: &Expr) {
|
||||
let Expr::Name(expr) = expr else {
|
||||
let Expr::Name(ast::ExprName { id, .. }) = expr else {
|
||||
return;
|
||||
};
|
||||
self.semantic.resolve_load(expr);
|
||||
self.semantic.resolve_load(id, expr.range());
|
||||
}
|
||||
|
||||
fn handle_node_store(&mut self, id: &'a str, expr: &Expr) {
|
||||
let parent = self.semantic.current_statement();
|
||||
let parent = self.semantic.stmt();
|
||||
|
||||
if matches!(
|
||||
parent,
|
||||
@@ -1615,7 +1569,7 @@ impl<'a> Checker<'a> {
|
||||
return;
|
||||
}
|
||||
|
||||
if parent.is_for_stmt() {
|
||||
if matches!(parent, Stmt::For(_) | Stmt::AsyncFor(_)) {
|
||||
self.add_binding(
|
||||
id,
|
||||
expr.range(),
|
||||
@@ -1635,7 +1589,7 @@ impl<'a> Checker<'a> {
|
||||
return;
|
||||
}
|
||||
|
||||
let scope = self.semantic.current_scope();
|
||||
let scope = self.semantic.scope();
|
||||
|
||||
if scope.kind.is_module()
|
||||
&& match parent {
|
||||
@@ -1687,8 +1641,8 @@ impl<'a> Checker<'a> {
|
||||
|
||||
if self
|
||||
.semantic
|
||||
.current_expressions()
|
||||
.any(Expr::is_named_expr_expr)
|
||||
.expr_ancestors()
|
||||
.any(|expr| expr.is_named_expr_expr())
|
||||
{
|
||||
self.add_binding(
|
||||
id,
|
||||
@@ -1714,7 +1668,7 @@ impl<'a> Checker<'a> {
|
||||
|
||||
self.semantic.resolve_del(id, expr.range());
|
||||
|
||||
if helpers::on_conditional_branch(&mut self.semantic.current_statements()) {
|
||||
if helpers::on_conditional_branch(&mut self.semantic.parents()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1722,12 +1676,11 @@ impl<'a> Checker<'a> {
|
||||
let binding_id =
|
||||
self.semantic
|
||||
.push_binding(expr.range(), BindingKind::Deletion, BindingFlags::empty());
|
||||
let scope = self.semantic.current_scope_mut();
|
||||
let scope = self.semantic.scope_mut();
|
||||
scope.add(id, binding_id);
|
||||
}
|
||||
|
||||
fn visit_deferred_future_type_definitions(&mut self) {
|
||||
let snapshot = self.semantic.snapshot();
|
||||
while !self.deferred.future_type_definitions.is_empty() {
|
||||
let type_definitions = std::mem::take(&mut self.deferred.future_type_definitions);
|
||||
for (expr, snapshot) in type_definitions {
|
||||
@@ -1738,29 +1691,9 @@ impl<'a> Checker<'a> {
|
||||
self.visit_expr(expr);
|
||||
}
|
||||
}
|
||||
self.semantic.restore(snapshot);
|
||||
}
|
||||
|
||||
fn visit_deferred_type_param_definitions(&mut self) {
|
||||
let snapshot = self.semantic.snapshot();
|
||||
while !self.deferred.type_param_definitions.is_empty() {
|
||||
let type_params = std::mem::take(&mut self.deferred.type_param_definitions);
|
||||
for (type_param, snapshot) in type_params {
|
||||
self.semantic.restore(snapshot);
|
||||
|
||||
if let ast::TypeParam::TypeVar(ast::TypeParamTypeVar {
|
||||
bound: Some(bound), ..
|
||||
}) = type_param
|
||||
{
|
||||
self.visit_expr(bound);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.semantic.restore(snapshot);
|
||||
}
|
||||
|
||||
fn visit_deferred_string_type_definitions(&mut self, allocator: &'a typed_arena::Arena<Expr>) {
|
||||
let snapshot = self.semantic.snapshot();
|
||||
while !self.deferred.string_type_definitions.is_empty() {
|
||||
let type_definitions = std::mem::take(&mut self.deferred.string_type_definitions);
|
||||
for (range, value, snapshot) in type_definitions {
|
||||
@@ -1771,12 +1704,12 @@ impl<'a> Checker<'a> {
|
||||
|
||||
self.semantic.restore(snapshot);
|
||||
|
||||
if self.semantic.in_annotation() && self.semantic.future_annotations() {
|
||||
if self.semantic.in_typing_only_annotation() {
|
||||
if self.enabled(Rule::QuotedAnnotation) {
|
||||
pyupgrade::rules::quoted_annotation(self, value, range);
|
||||
}
|
||||
}
|
||||
if self.source_type.is_stub() {
|
||||
if self.is_stub {
|
||||
if self.enabled(Rule::QuotedAnnotationInStub) {
|
||||
flake8_pyi::rules::quoted_annotation_in_stub(self, value, range);
|
||||
}
|
||||
@@ -1804,57 +1737,51 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
self.semantic.restore(snapshot);
|
||||
}
|
||||
|
||||
fn visit_deferred_functions(&mut self) {
|
||||
let snapshot = self.semantic.snapshot();
|
||||
while !self.deferred.functions.is_empty() {
|
||||
let deferred_functions = std::mem::take(&mut self.deferred.functions);
|
||||
for snapshot in deferred_functions {
|
||||
self.semantic.restore(snapshot);
|
||||
|
||||
if let Stmt::FunctionDef(ast::StmtFunctionDef {
|
||||
body, parameters, ..
|
||||
}) = self.semantic.current_statement()
|
||||
{
|
||||
self.visit_parameters(parameters);
|
||||
self.visit_body(body);
|
||||
} else {
|
||||
unreachable!("Expected Stmt::FunctionDef")
|
||||
match &self.semantic.stmt() {
|
||||
Stmt::FunctionDef(ast::StmtFunctionDef { body, args, .. })
|
||||
| Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef { body, args, .. }) => {
|
||||
self.visit_arguments(args);
|
||||
self.visit_body(body);
|
||||
}
|
||||
_ => {
|
||||
unreachable!("Expected Stmt::FunctionDef | Stmt::AsyncFunctionDef")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.semantic.restore(snapshot);
|
||||
}
|
||||
|
||||
fn visit_deferred_lambdas(&mut self) {
|
||||
let snapshot = self.semantic.snapshot();
|
||||
while !self.deferred.lambdas.is_empty() {
|
||||
let lambdas = std::mem::take(&mut self.deferred.lambdas);
|
||||
for (expr, snapshot) in lambdas {
|
||||
self.semantic.restore(snapshot);
|
||||
|
||||
if let Expr::Lambda(ast::ExprLambda {
|
||||
parameters,
|
||||
args,
|
||||
body,
|
||||
range: _,
|
||||
}) = expr
|
||||
{
|
||||
self.visit_parameters(parameters);
|
||||
self.visit_arguments(args);
|
||||
self.visit_expr(body);
|
||||
} else {
|
||||
unreachable!("Expected Expr::Lambda");
|
||||
}
|
||||
}
|
||||
}
|
||||
self.semantic.restore(snapshot);
|
||||
}
|
||||
|
||||
/// Run any lint rules that operate over the module exports (i.e., members of `__all__`).
|
||||
fn visit_exports(&mut self) {
|
||||
let snapshot = self.semantic.snapshot();
|
||||
|
||||
let exports: Vec<(&str, TextRange)> = self
|
||||
.semantic
|
||||
.global_scope()
|
||||
@@ -1897,8 +1824,6 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.semantic.restore(snapshot);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1913,7 +1838,6 @@ pub(crate) fn check_ast(
|
||||
noqa: flags::Noqa,
|
||||
path: &Path,
|
||||
package: Option<&Path>,
|
||||
source_type: PySourceType,
|
||||
) -> Vec<Diagnostic> {
|
||||
let module_path = package.and_then(|package| to_module_path(package, path));
|
||||
let module = Module {
|
||||
@@ -1941,7 +1865,6 @@ pub(crate) fn check_ast(
|
||||
stylist,
|
||||
indexer,
|
||||
Importer::new(python_ast, locator, stylist),
|
||||
source_type,
|
||||
);
|
||||
checker.bind_builtins();
|
||||
|
||||
@@ -1959,7 +1882,6 @@ pub(crate) fn check_ast(
|
||||
checker.visit_deferred_functions();
|
||||
checker.visit_deferred_lambdas();
|
||||
checker.visit_deferred_future_type_definitions();
|
||||
checker.visit_deferred_type_param_definitions();
|
||||
let allocator = typed_arena::Arena::new();
|
||||
checker.visit_deferred_string_type_definitions(&allocator);
|
||||
checker.visit_exports();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
use std::borrow::Cow;
|
||||
use std::path::Path;
|
||||
|
||||
use ruff_python_ast::{self as ast, PySourceType, Ranged, Stmt, Suite};
|
||||
use ruff_python_ast::{self as ast, Ranged, Stmt, Suite};
|
||||
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_python_ast::helpers::to_module_path;
|
||||
@@ -10,7 +10,7 @@ use ruff_python_ast::imports::{ImportMap, ModuleImport};
|
||||
use ruff_python_ast::statement_visitor::StatementVisitor;
|
||||
use ruff_python_codegen::Stylist;
|
||||
use ruff_python_index::Indexer;
|
||||
|
||||
use ruff_python_stdlib::path::is_python_stub_file;
|
||||
use ruff_source_file::Locator;
|
||||
|
||||
use crate::directives::IsortDirectives;
|
||||
@@ -87,12 +87,12 @@ pub(crate) fn check_imports(
|
||||
path: &Path,
|
||||
package: Option<&Path>,
|
||||
source_kind: Option<&SourceKind>,
|
||||
source_type: PySourceType,
|
||||
) -> (Vec<Diagnostic>, Option<ImportMap>) {
|
||||
let is_stub = is_python_stub_file(path);
|
||||
|
||||
// Extract all import blocks from the AST.
|
||||
let tracker = {
|
||||
let mut tracker =
|
||||
BlockBuilder::new(locator, directives, source_type.is_stub(), source_kind);
|
||||
let mut tracker = BlockBuilder::new(locator, directives, is_stub, source_kind);
|
||||
tracker.visit_body(python_ast);
|
||||
tracker
|
||||
};
|
||||
@@ -104,13 +104,7 @@ pub(crate) fn check_imports(
|
||||
for block in &blocks {
|
||||
if !block.imports.is_empty() {
|
||||
if let Some(diagnostic) = isort::rules::organize_imports(
|
||||
block,
|
||||
locator,
|
||||
stylist,
|
||||
indexer,
|
||||
settings,
|
||||
package,
|
||||
source_type,
|
||||
block, locator, stylist, indexer, settings, package,
|
||||
) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
@@ -119,11 +113,7 @@ pub(crate) fn check_imports(
|
||||
}
|
||||
if settings.rules.enabled(Rule::MissingRequiredImport) {
|
||||
diagnostics.extend(isort::rules::add_required_imports(
|
||||
python_ast,
|
||||
locator,
|
||||
stylist,
|
||||
settings,
|
||||
source_type,
|
||||
python_ast, locator, stylist, settings, is_stub,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@ use ruff_source_file::Locator;
|
||||
use crate::registry::{AsRule, Rule};
|
||||
use crate::rules::pycodestyle::rules::logical_lines::{
|
||||
extraneous_whitespace, indentation, missing_whitespace, missing_whitespace_after_keyword,
|
||||
missing_whitespace_around_operator, space_after_comma, space_around_operator,
|
||||
whitespace_around_keywords, whitespace_around_named_parameter_equals,
|
||||
whitespace_before_comment, whitespace_before_parameters, LogicalLines, TokenFlags,
|
||||
missing_whitespace_around_operator, space_around_operator, whitespace_around_keywords,
|
||||
whitespace_around_named_parameter_equals, whitespace_before_comment,
|
||||
whitespace_before_parameters, LogicalLines, TokenFlags,
|
||||
};
|
||||
use crate::settings::Settings;
|
||||
|
||||
@@ -61,9 +61,6 @@ pub(crate) fn check_logical_lines(
|
||||
missing_whitespace_around_operator(&line, &mut context);
|
||||
missing_whitespace(&line, should_fix_missing_whitespace, &mut context);
|
||||
}
|
||||
if line.flags().contains(TokenFlags::PUNCTUATION) {
|
||||
space_after_comma(&line, &mut context);
|
||||
}
|
||||
|
||||
if line
|
||||
.flags()
|
||||
|
||||
@@ -94,15 +94,8 @@ pub(crate) fn check_noqa(
|
||||
}
|
||||
}
|
||||
|
||||
// Enforce that the noqa directive was actually used (RUF100), unless RUF100 was itself
|
||||
// suppressed.
|
||||
if settings.rules.enabled(Rule::UnusedNOQA)
|
||||
&& analyze_directives
|
||||
&& !exemption.is_some_and(|exemption| match exemption {
|
||||
FileExemption::All => true,
|
||||
FileExemption::Codes(codes) => codes.contains(&Rule::UnusedNOQA.noqa_code()),
|
||||
})
|
||||
{
|
||||
// Enforce that the noqa directive was actually used (RUF100).
|
||||
if analyze_directives && settings.rules.enabled(Rule::UnusedNOQA) {
|
||||
for line in noqa_directives.lines() {
|
||||
match &line.directive {
|
||||
Directive::All(directive) => {
|
||||
|
||||
@@ -84,8 +84,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Pycodestyle, "E227") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::MissingWhitespaceAroundBitwiseOrShiftOperator),
|
||||
(Pycodestyle, "E228") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::MissingWhitespaceAroundModuloOperator),
|
||||
(Pycodestyle, "E231") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::MissingWhitespace),
|
||||
(Pycodestyle, "E241") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::MultipleSpacesAfterComma),
|
||||
(Pycodestyle, "E242") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::TabAfterComma),
|
||||
(Pycodestyle, "E251") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::UnexpectedSpacesAroundKeywordParameterEquals),
|
||||
(Pycodestyle, "E252") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::MissingWhitespaceAroundParameterEquals),
|
||||
(Pycodestyle, "E261") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::TooFewSpacesBeforeInlineComment),
|
||||
@@ -191,7 +189,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Pylint, "E1142") => (RuleGroup::Unspecified, rules::pylint::rules::AwaitOutsideAsync),
|
||||
(Pylint, "E1205") => (RuleGroup::Unspecified, rules::pylint::rules::LoggingTooManyArgs),
|
||||
(Pylint, "E1206") => (RuleGroup::Unspecified, rules::pylint::rules::LoggingTooFewArgs),
|
||||
(Pylint, "E1300") => (RuleGroup::Unspecified, rules::pylint::rules::BadStringFormatCharacter),
|
||||
(Pylint, "E1307") => (RuleGroup::Unspecified, rules::pylint::rules::BadStringFormatType),
|
||||
(Pylint, "E1310") => (RuleGroup::Unspecified, rules::pylint::rules::BadStrStripCall),
|
||||
(Pylint, "E1507") => (RuleGroup::Unspecified, rules::pylint::rules::InvalidEnvvarValue),
|
||||
@@ -226,7 +223,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Pylint, "W0711") => (RuleGroup::Unspecified, rules::pylint::rules::BinaryOpException),
|
||||
(Pylint, "W1508") => (RuleGroup::Unspecified, rules::pylint::rules::InvalidEnvvarDefault),
|
||||
(Pylint, "W1509") => (RuleGroup::Unspecified, rules::pylint::rules::SubprocessPopenPreexecFn),
|
||||
(Pylint, "W1641") => (RuleGroup::Nursery, rules::pylint::rules::EqWithoutHash),
|
||||
(Pylint, "W2901") => (RuleGroup::Unspecified, rules::pylint::rules::RedefinedLoopName),
|
||||
(Pylint, "W3301") => (RuleGroup::Unspecified, rules::pylint::rules::NestedMinMax),
|
||||
|
||||
@@ -439,7 +435,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Pyupgrade, "037") => (RuleGroup::Unspecified, rules::pyupgrade::rules::QuotedAnnotation),
|
||||
(Pyupgrade, "038") => (RuleGroup::Unspecified, rules::pyupgrade::rules::NonPEP604Isinstance),
|
||||
(Pyupgrade, "039") => (RuleGroup::Unspecified, rules::pyupgrade::rules::UnnecessaryClassParentheses),
|
||||
(Pyupgrade, "040") => (RuleGroup::Unspecified, rules::pyupgrade::rules::NonPEP695TypeAlias),
|
||||
|
||||
// pydocstyle
|
||||
(Pydocstyle, "100") => (RuleGroup::Unspecified, rules::pydocstyle::rules::UndocumentedPublicModule),
|
||||
@@ -638,7 +633,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Flake8Pyi, "016") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::DuplicateUnionMember),
|
||||
(Flake8Pyi, "017") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::ComplexAssignmentInStub),
|
||||
(Flake8Pyi, "018") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::UnusedPrivateTypeVar),
|
||||
(Flake8Pyi, "019") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::CustomTypeVarReturnType),
|
||||
(Flake8Pyi, "020") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::QuotedAnnotationInStub),
|
||||
(Flake8Pyi, "021") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::DocstringInStub),
|
||||
(Flake8Pyi, "024") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::CollectionsNamedTuple),
|
||||
@@ -657,15 +651,11 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Flake8Pyi, "044") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::FutureAnnotationsInStub),
|
||||
(Flake8Pyi, "045") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::IterMethodReturnIterable),
|
||||
(Flake8Pyi, "046") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::UnusedPrivateProtocol),
|
||||
(Flake8Pyi, "047") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::UnusedPrivateTypeAlias),
|
||||
(Flake8Pyi, "048") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::StubBodyMultipleStatements),
|
||||
(Flake8Pyi, "049") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::UnusedPrivateTypedDict),
|
||||
(Flake8Pyi, "050") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::NoReturnArgumentAnnotationInStub),
|
||||
(Flake8Pyi, "051") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::RedundantLiteralUnion),
|
||||
(Flake8Pyi, "052") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::UnannotatedAssignmentInStub),
|
||||
(Flake8Pyi, "054") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::NumericLiteralTooLong),
|
||||
(Flake8Pyi, "053") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::StringOrBytesTooLong),
|
||||
(Flake8Pyi, "055") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::UnnecessaryTypeUnion),
|
||||
(Flake8Pyi, "056") => (RuleGroup::Unspecified, rules::flake8_pyi::rules::UnsupportedMethodCallOnAll),
|
||||
|
||||
// flake8-pytest-style
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Extract docstrings from an AST.
|
||||
|
||||
use ruff_python_ast::{self as ast, Constant, Expr, Stmt};
|
||||
|
||||
use ruff_python_semantic::{Definition, DefinitionId, Definitions, Member, MemberKind};
|
||||
|
||||
/// Extract a docstring from a function or class body.
|
||||
@@ -27,48 +28,63 @@ pub(crate) fn docstring_from(suite: &[Stmt]) -> Option<&Expr> {
|
||||
pub(crate) fn extract_docstring<'a>(definition: &'a Definition<'a>) -> Option<&'a Expr> {
|
||||
match definition {
|
||||
Definition::Module(module) => docstring_from(module.python_ast),
|
||||
Definition::Member(member) => docstring_from(member.body()),
|
||||
Definition::Member(member) => {
|
||||
if let Stmt::ClassDef(ast::StmtClassDef { body, .. })
|
||||
| Stmt::FunctionDef(ast::StmtFunctionDef { body, .. })
|
||||
| Stmt::AsyncFunctionDef(ast::StmtAsyncFunctionDef { body, .. }) = &member.stmt
|
||||
{
|
||||
docstring_from(body)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) enum ExtractionTarget<'a> {
|
||||
Class(&'a ast::StmtClassDef),
|
||||
Function(&'a ast::StmtFunctionDef),
|
||||
pub(crate) enum ExtractionTarget {
|
||||
Class,
|
||||
Function,
|
||||
}
|
||||
|
||||
/// Extract a `Definition` from the AST node defined by a `Stmt`.
|
||||
pub(crate) fn extract_definition<'a>(
|
||||
target: ExtractionTarget<'a>,
|
||||
target: ExtractionTarget,
|
||||
stmt: &'a Stmt,
|
||||
parent: DefinitionId,
|
||||
definitions: &Definitions<'a>,
|
||||
) -> Member<'a> {
|
||||
match target {
|
||||
ExtractionTarget::Function(function) => match &definitions[parent] {
|
||||
ExtractionTarget::Function => match &definitions[parent] {
|
||||
Definition::Module(..) => Member {
|
||||
parent,
|
||||
kind: MemberKind::Function(function),
|
||||
kind: MemberKind::Function,
|
||||
stmt,
|
||||
},
|
||||
Definition::Member(Member {
|
||||
kind: MemberKind::Class(_) | MemberKind::NestedClass(_),
|
||||
kind: MemberKind::Class | MemberKind::NestedClass,
|
||||
..
|
||||
}) => Member {
|
||||
parent,
|
||||
kind: MemberKind::Method(function),
|
||||
kind: MemberKind::Method,
|
||||
stmt,
|
||||
},
|
||||
Definition::Member(_) => Member {
|
||||
Definition::Member(..) => Member {
|
||||
parent,
|
||||
kind: MemberKind::NestedFunction(function),
|
||||
kind: MemberKind::NestedFunction,
|
||||
stmt,
|
||||
},
|
||||
},
|
||||
ExtractionTarget::Class(class) => match &definitions[parent] {
|
||||
Definition::Module(_) => Member {
|
||||
ExtractionTarget::Class => match &definitions[parent] {
|
||||
Definition::Module(..) => Member {
|
||||
parent,
|
||||
kind: MemberKind::Class(class),
|
||||
kind: MemberKind::Class,
|
||||
stmt,
|
||||
},
|
||||
Definition::Member(_) => Member {
|
||||
Definition::Member(..) => Member {
|
||||
parent,
|
||||
kind: MemberKind::NestedClass(class),
|
||||
kind: MemberKind::NestedClass,
|
||||
stmt,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user