Compare commits
121 Commits
0.8.0
...
micha/sals
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35f3815087 | ||
|
|
81bfcc9899 | ||
|
|
74309008fd | ||
|
|
a255d79087 | ||
|
|
70bd10614f | ||
|
|
bf0fd04e4e | ||
|
|
a69dfd4a74 | ||
|
|
c2e17d0399 | ||
|
|
10fef8bd5d | ||
|
|
246a6df87d | ||
|
|
3e702e12f7 | ||
|
|
91e2d9a139 | ||
|
|
5137fcc9c8 | ||
|
|
83651deac7 | ||
|
|
6dfe125f44 | ||
|
|
f96dfc179f | ||
|
|
76d2e56501 | ||
|
|
30d80d9746 | ||
|
|
5a67d3269b | ||
|
|
02d1e6a94a | ||
|
|
48ec3a8add | ||
|
|
289a938ae8 | ||
|
|
3e5ab6cf38 | ||
|
|
48d33595b9 | ||
|
|
23ee7a954e | ||
|
|
d4a7c098dc | ||
|
|
0c5f03a059 | ||
|
|
239bfb6de7 | ||
|
|
3c3ec6755c | ||
|
|
4c05f2c8b4 | ||
|
|
d594796e3a | ||
|
|
b5ef2844ef | ||
|
|
06183bd8a1 | ||
|
|
4068006c5f | ||
|
|
145c97c94f | ||
|
|
84748be163 | ||
|
|
9e017634cb | ||
|
|
56ae73a925 | ||
|
|
be07424e80 | ||
|
|
579ef01294 | ||
|
|
90487b8cbd | ||
|
|
f3d8c023d3 | ||
|
|
b63c2e126b | ||
|
|
a6402fb51e | ||
|
|
b3b2c982cd | ||
|
|
abb3c6ea95 | ||
|
|
224fe75a76 | ||
|
|
dc29f52750 | ||
|
|
d9cbf2fe44 | ||
|
|
3f6c65e78c | ||
|
|
976c37a849 | ||
|
|
a378ff38dc | ||
|
|
d8bca0d3a2 | ||
|
|
6f1cf5b686 | ||
|
|
8639f8c1a6 | ||
|
|
f1b2e85339 | ||
|
|
6d61c8aa16 | ||
|
|
8a7ba5d2df | ||
|
|
6fcbe8efb4 | ||
|
|
c40b37aa36 | ||
|
|
ef0e2a6e1b | ||
|
|
4fb1416bf4 | ||
|
|
8a860b89b4 | ||
|
|
f96fa6b0e2 | ||
|
|
4cd2b9926e | ||
|
|
11a2929ed7 | ||
|
|
187974eff4 | ||
|
|
14ba469fc0 | ||
|
|
6fd10e2fe7 | ||
|
|
e0f3eaf1dd | ||
|
|
c84c690f1e | ||
|
|
0d649f9afd | ||
|
|
82c01aa662 | ||
|
|
9f446faa6c | ||
|
|
b94d6cf567 | ||
|
|
cd0c97211c | ||
|
|
0e71c9e3bb | ||
|
|
24c90d6953 | ||
|
|
fbff4dec3a | ||
|
|
f3dac27e9a | ||
|
|
e4cefd9bf9 | ||
|
|
9e4ee98109 | ||
|
|
557d583e32 | ||
|
|
f98eebdbab | ||
|
|
c606bf014e | ||
|
|
e8fce20736 | ||
|
|
5a30ec0df6 | ||
|
|
fab1b0d546 | ||
|
|
66abef433b | ||
|
|
fa22bd604a | ||
|
|
0c9165fc3a | ||
|
|
9f6147490b | ||
|
|
b7571c3e24 | ||
|
|
d178d115f3 | ||
|
|
6501782678 | ||
|
|
bca4341dcc | ||
|
|
31ede11774 | ||
|
|
ba9f881687 | ||
|
|
4357a0a3c2 | ||
|
|
c18afa93b3 | ||
|
|
8f04202ee4 | ||
|
|
efe54081d6 | ||
|
|
ac23c99744 | ||
|
|
e5c7d87461 | ||
|
|
de62e39eba | ||
|
|
d285717da8 | ||
|
|
545e9deba3 | ||
|
|
e3d792605f | ||
|
|
1f303a5eb6 | ||
|
|
07d13c6b4a | ||
|
|
e1838aac29 | ||
|
|
4ba847f250 | ||
|
|
13e9fc9362 | ||
|
|
3fda2d17c7 | ||
|
|
931fa06d85 | ||
|
|
e53ac7985d | ||
|
|
e25e7044ba | ||
|
|
b80de52592 | ||
|
|
2917534279 | ||
|
|
f6b2cd5588 | ||
|
|
302fe76c2b |
5
.github/CODEOWNERS
vendored
5
.github/CODEOWNERS
vendored
@@ -13,9 +13,10 @@
|
||||
# flake8-pyi
|
||||
/crates/ruff_linter/src/rules/flake8_pyi/ @AlexWaygood
|
||||
|
||||
# Script for fuzzing the parser
|
||||
/scripts/fuzz-parser/ @AlexWaygood
|
||||
# Script for fuzzing the parser/red-knot etc.
|
||||
/python/py-fuzzer/ @AlexWaygood
|
||||
|
||||
# red-knot
|
||||
/crates/red_knot* @carljm @MichaReiser @AlexWaygood @sharkdp
|
||||
/crates/ruff_db/ @carljm @MichaReiser @AlexWaygood @sharkdp
|
||||
/scripts/knot_benchmark/ @carljm @MichaReiser @AlexWaygood @sharkdp
|
||||
|
||||
53
.github/workflows/ci.yaml
vendored
53
.github/workflows/ci.yaml
vendored
@@ -49,7 +49,7 @@ jobs:
|
||||
- crates/ruff_text_size/**
|
||||
- crates/ruff_python_ast/**
|
||||
- crates/ruff_python_parser/**
|
||||
- scripts/fuzz-parser/**
|
||||
- python/py-fuzzer/**
|
||||
- .github/workflows/ci.yaml
|
||||
|
||||
linter:
|
||||
@@ -82,6 +82,7 @@ jobs:
|
||||
code:
|
||||
- "**/*"
|
||||
- "!**/*.md"
|
||||
- "crates/red_knot_python_semantic/resources/mdtest/**/*.md"
|
||||
- "!docs/**"
|
||||
- "!assets/**"
|
||||
|
||||
@@ -157,6 +158,33 @@ jobs:
|
||||
name: ruff
|
||||
path: target/debug/ruff
|
||||
|
||||
cargo-test-linux-release:
|
||||
name: "cargo test (linux, release)"
|
||||
runs-on: depot-ubuntu-22.04-16
|
||||
needs: determine_changes
|
||||
if: ${{ needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main' }}
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@v1
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Install cargo insta"
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-insta
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "Run tests"
|
||||
shell: bash
|
||||
env:
|
||||
NEXTEST_PROFILE: "ci"
|
||||
run: cargo insta test --release --all-features --unreferenced reject --test-runner nextest
|
||||
|
||||
cargo-test-windows:
|
||||
name: "cargo test (windows)"
|
||||
runs-on: windows-latest-xlarge
|
||||
@@ -212,7 +240,6 @@ jobs:
|
||||
cargo-build-release:
|
||||
name: "cargo build (release)"
|
||||
runs-on: macos-latest
|
||||
needs: determine_changes
|
||||
if: ${{ github.ref == 'refs/heads/main' }}
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
@@ -291,13 +318,7 @@ jobs:
|
||||
FORCE_COLOR: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
- name: Install uv
|
||||
run: curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
- name: Install Python requirements
|
||||
run: uv pip install -r scripts/fuzz-parser/requirements.txt --system
|
||||
- uses: astral-sh/setup-uv@v4
|
||||
- uses: actions/download-artifact@v4
|
||||
name: Download Ruff binary to test
|
||||
id: download-cached-binary
|
||||
@@ -309,7 +330,15 @@ jobs:
|
||||
# Make executable, since artifact download doesn't preserve this
|
||||
chmod +x ${{ steps.download-cached-binary.outputs.download-path }}/ruff
|
||||
|
||||
python scripts/fuzz-parser/fuzz.py 0-500 --test-executable ${{ steps.download-cached-binary.outputs.download-path }}/ruff
|
||||
(
|
||||
uvx \
|
||||
--python=${{ env.PYTHON_VERSION }} \
|
||||
--from=./python/py-fuzzer \
|
||||
fuzz \
|
||||
--test-executable=${{ steps.download-cached-binary.outputs.download-path }}/ruff \
|
||||
--bin=ruff \
|
||||
0-500
|
||||
)
|
||||
|
||||
scripts:
|
||||
name: "test scripts"
|
||||
@@ -354,7 +383,7 @@ jobs:
|
||||
name: ruff
|
||||
path: target/debug
|
||||
|
||||
- uses: dawidd6/action-download-artifact@v6
|
||||
- uses: dawidd6/action-download-artifact@v7
|
||||
name: Download baseline Ruff binary
|
||||
with:
|
||||
name: ruff
|
||||
@@ -530,7 +559,7 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v3
|
||||
uses: astral-sh/setup-uv@v4
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "Install Insiders dependencies"
|
||||
if: ${{ env.MKDOCS_INSIDERS_SSH_KEY_EXISTS == 'true' }}
|
||||
|
||||
19
.github/workflows/daily_fuzz.yaml
vendored
19
.github/workflows/daily_fuzz.yaml
vendored
@@ -32,13 +32,7 @@ jobs:
|
||||
if: ${{ github.repository == 'astral-sh/ruff' || github.event_name != 'schedule' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
- name: Install uv
|
||||
run: curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
- name: Install Python requirements
|
||||
run: uv pip install -r scripts/fuzz-parser/requirements.txt --system
|
||||
- uses: astral-sh/setup-uv@v4
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install mold"
|
||||
@@ -49,7 +43,16 @@ jobs:
|
||||
# but this is outweighed by the fact that a release build takes *much* longer to compile in CI
|
||||
run: cargo build --locked
|
||||
- name: Fuzz
|
||||
run: python scripts/fuzz-parser/fuzz.py $(shuf -i 0-9999999999999999999 -n 1000) --test-executable target/debug/ruff
|
||||
run: |
|
||||
(
|
||||
uvx \
|
||||
--python=3.12 \
|
||||
--from=./python/py-fuzzer \
|
||||
fuzz \
|
||||
--test-executable=target/debug/ruff \
|
||||
--bin=ruff \
|
||||
$(shuf -i 0-9999999999999999999 -n 1000)
|
||||
)
|
||||
|
||||
create-issue-on-failure:
|
||||
name: Create an issue if the daily fuzz surfaced any bugs
|
||||
|
||||
4
.github/workflows/pr-comment.yaml
vendored
4
.github/workflows/pr-comment.yaml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
comment:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dawidd6/action-download-artifact@v6
|
||||
- uses: dawidd6/action-download-artifact@v7
|
||||
name: Download pull request number
|
||||
with:
|
||||
name: pr-number
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
echo "pr-number=$(<pr-number)" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- uses: dawidd6/action-download-artifact@v6
|
||||
- uses: dawidd6/action-download-artifact@v7
|
||||
name: "Download ecosystem results"
|
||||
id: download-ecosystem-result
|
||||
if: steps.pr-number.outputs.pr-number
|
||||
|
||||
2
.github/workflows/publish-playground.yml
vendored
2
.github/workflows/publish-playground.yml
vendored
@@ -47,7 +47,7 @@ jobs:
|
||||
working-directory: playground
|
||||
- name: "Deploy to Cloudflare Pages"
|
||||
if: ${{ env.CF_API_TOKEN_EXISTS == 'true' }}
|
||||
uses: cloudflare/wrangler-action@v3.12.1
|
||||
uses: cloudflare/wrangler-action@v3.13.0
|
||||
with:
|
||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||
accountId: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
|
||||
2
.github/workflows/publish-pypi.yml
vendored
2
.github/workflows/publish-pypi.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
id-token: write
|
||||
steps:
|
||||
- name: "Install uv"
|
||||
uses: astral-sh/setup-uv@v3
|
||||
uses: astral-sh/setup-uv@v4
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: wheels-*
|
||||
|
||||
@@ -22,7 +22,7 @@ repos:
|
||||
- id: validate-pyproject
|
||||
|
||||
- repo: https://github.com/executablebooks/mdformat
|
||||
rev: 0.7.18
|
||||
rev: 0.7.19
|
||||
hooks:
|
||||
- id: mdformat
|
||||
additional_dependencies:
|
||||
@@ -36,7 +36,7 @@ repos:
|
||||
)$
|
||||
|
||||
- repo: https://github.com/igorshubovych/markdownlint-cli
|
||||
rev: v0.42.0
|
||||
rev: v0.43.0
|
||||
hooks:
|
||||
- id: markdownlint-fix
|
||||
exclude: |
|
||||
@@ -59,7 +59,7 @@ repos:
|
||||
- black==24.10.0
|
||||
|
||||
- repo: https://github.com/crate-ci/typos
|
||||
rev: v1.27.3
|
||||
rev: v1.28.1
|
||||
hooks:
|
||||
- id: typos
|
||||
|
||||
@@ -73,7 +73,7 @@ repos:
|
||||
pass_filenames: false # This makes it a lot faster
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.7.4
|
||||
rev: v0.8.1
|
||||
hooks:
|
||||
- id: ruff-format
|
||||
- id: ruff
|
||||
@@ -83,7 +83,7 @@ repos:
|
||||
|
||||
# Prettier
|
||||
- repo: https://github.com/rbubley/mirrors-prettier
|
||||
rev: v3.3.3
|
||||
rev: v3.4.1
|
||||
hooks:
|
||||
- id: prettier
|
||||
types: [yaml]
|
||||
|
||||
@@ -192,7 +192,7 @@ flag or `unsafe-fixes` configuration option can be used to enable unsafe fixes.
|
||||
|
||||
See the [docs](https://docs.astral.sh/ruff/configuration/#fix-safety) for details.
|
||||
|
||||
### Remove formatter-conflicting rules from the default rule set ([#7900](https://github.com/astral-sh/ruff/pull/7900))
|
||||
### Remove formatter-conflicting rules from the default rule set ([#7900](https://github.com/astral-sh/ruff/pull/7900))
|
||||
|
||||
Previously, Ruff enabled all implemented rules in Pycodestyle (`E`) by default. Ruff now only includes the
|
||||
Pycodestyle prefixes `E4`, `E7`, and `E9` to exclude rules that conflict with automatic formatters. Consequently,
|
||||
|
||||
51
CHANGELOG.md
51
CHANGELOG.md
@@ -1,5 +1,46 @@
|
||||
# Changelog
|
||||
|
||||
## 0.8.1
|
||||
|
||||
### Preview features
|
||||
|
||||
- Formatter: Avoid invalid syntax for format-spec with quotes for all Python versions ([#14625](https://github.com/astral-sh/ruff/pull/14625))
|
||||
- Formatter: Consider quotes inside format-specs when choosing the quotes for an f-string ([#14493](https://github.com/astral-sh/ruff/pull/14493))
|
||||
- Formatter: Do not consider f-strings with escaped newlines as multiline ([#14624](https://github.com/astral-sh/ruff/pull/14624))
|
||||
- Formatter: Fix f-string formatting in assignment statement ([#14454](https://github.com/astral-sh/ruff/pull/14454))
|
||||
- Formatter: Fix unnecessary space around power operator (`**`) in overlong f-string expressions ([#14489](https://github.com/astral-sh/ruff/pull/14489))
|
||||
- \[`airflow`\] Avoid implicit `schedule` argument to `DAG` and `@dag` (`AIR301`) ([#14581](https://github.com/astral-sh/ruff/pull/14581))
|
||||
- \[`flake8-builtins`\] Exempt private built-in modules (`A005`) ([#14505](https://github.com/astral-sh/ruff/pull/14505))
|
||||
- \[`flake8-pytest-style`\] Fix `pytest.mark.parametrize` rules to check calls instead of decorators ([#14515](https://github.com/astral-sh/ruff/pull/14515))
|
||||
- \[`flake8-type-checking`\] Implement `runtime-cast-value` (`TC006`) ([#14511](https://github.com/astral-sh/ruff/pull/14511))
|
||||
- \[`flake8-type-checking`\] Implement `unquoted-type-alias` (`TC007`) and `quoted-type-alias` (`TC008`) ([#12927](https://github.com/astral-sh/ruff/pull/12927))
|
||||
- \[`flake8-use-pathlib`\] Recommend `Path.iterdir()` over `os.listdir()` (`PTH208`) ([#14509](https://github.com/astral-sh/ruff/pull/14509))
|
||||
- \[`pylint`\] Extend `invalid-envvar-default` to detect `os.environ.get` (`PLW1508`) ([#14512](https://github.com/astral-sh/ruff/pull/14512))
|
||||
- \[`pylint`\] Implement `len-test` (`PLC1802`) ([#14309](https://github.com/astral-sh/ruff/pull/14309))
|
||||
- \[`refurb`\] Fix bug where methods defined using lambdas were flagged by `FURB118` ([#14639](https://github.com/astral-sh/ruff/pull/14639))
|
||||
- \[`ruff`\] Auto-add `r` prefix when string has no backslashes for `unraw-re-pattern` (`RUF039`) ([#14536](https://github.com/astral-sh/ruff/pull/14536))
|
||||
- \[`ruff`\] Implement `invalid-assert-message-literal-argument` (`RUF040`) ([#14488](https://github.com/astral-sh/ruff/pull/14488))
|
||||
- \[`ruff`\] Implement `unnecessary-nested-literal` (`RUF041`) ([#14323](https://github.com/astral-sh/ruff/pull/14323))
|
||||
- \[`ruff`\] Implement `unnecessary-regular-expression` (`RUF055`) ([#14659](https://github.com/astral-sh/ruff/pull/14659))
|
||||
|
||||
### Rule changes
|
||||
|
||||
- Ignore more rules for stub files ([#14541](https://github.com/astral-sh/ruff/pull/14541))
|
||||
- \[`pep8-naming`\] Eliminate false positives for single-letter names (`N811`, `N814`) ([#14584](https://github.com/astral-sh/ruff/pull/14584))
|
||||
- \[`pyflakes`\] Avoid false positives in `@no_type_check` contexts (`F821`, `F722`) ([#14615](https://github.com/astral-sh/ruff/pull/14615))
|
||||
- \[`ruff`\] Detect redirected-noqa in file-level comments (`RUF101`) ([#14635](https://github.com/astral-sh/ruff/pull/14635))
|
||||
- \[`ruff`\] Mark fixes for `unsorted-dunder-all` and `unsorted-dunder-slots` as unsafe when there are complex comments in the sequence (`RUF022`, `RUF023`) ([#14560](https://github.com/astral-sh/ruff/pull/14560))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Avoid fixing code to `None | None` for `redundant-none-literal` (`PYI061`) and `never-union` (`RUF020`) ([#14583](https://github.com/astral-sh/ruff/pull/14583), [#14589](https://github.com/astral-sh/ruff/pull/14589))
|
||||
- \[`flake8-bugbear`\] Fix `mutable-contextvar-default` to resolve annotated function calls properly (`B039`) ([#14532](https://github.com/astral-sh/ruff/pull/14532))
|
||||
- \[`flake8-pyi`, `ruff`\] Fix traversal of nested literals and unions (`PYI016`, `PYI051`, `PYI055`, `PYI062`, `RUF041`) ([#14641](https://github.com/astral-sh/ruff/pull/14641))
|
||||
- \[`flake8-pyi`\] Avoid rewriting invalid type expressions in `unnecessary-type-union` (`PYI055`) ([#14660](https://github.com/astral-sh/ruff/pull/14660))
|
||||
- \[`flake8-type-checking`\] Avoid syntax errors and type checking problem for quoted annotations autofix (`TC003`, `TC006`) ([#14634](https://github.com/astral-sh/ruff/pull/14634))
|
||||
- \[`pylint`\] Do not wrap function calls in parentheses in the fix for unnecessary-dunder-call (`PLC2801`) ([#14601](https://github.com/astral-sh/ruff/pull/14601))
|
||||
- \[`ruff`\] Handle `attrs`'s `auto_attribs` correctly (`RUF009`) ([#14520](https://github.com/astral-sh/ruff/pull/14520))
|
||||
|
||||
## 0.8.0
|
||||
|
||||
Check out the [blog post](https://astral.sh/blog/ruff-v0.8.0) for a migration guide and overview of the changes!
|
||||
@@ -57,7 +98,7 @@ The following rules have been stabilized and are no longer in preview:
|
||||
- [`fast-api-redundant-response-model`](https://docs.astral.sh/ruff/rules/fast-api-redundant-response-model/) (`FAST001`)
|
||||
- [`fast-api-non-annotated-dependency`](https://docs.astral.sh/ruff/rules/fast-api-non-annotated-dependency/) (`FAST002`)
|
||||
- [`dict-index-missing-items`](https://docs.astral.sh/ruff/rules/dict-index-missing-items/) (`PLC0206`)
|
||||
- [`pep484-style-positional-only-argument`](https://docs.astral.sh/ruff/rules/pep484-style-positional-only-argument/) (`PYI063`)
|
||||
- [`pep484-style-positional-only-parameter`](https://docs.astral.sh/ruff/rules/pep484-style-positional-only-parameter/) (`PYI063`)
|
||||
- [`redundant-final-literal`](https://docs.astral.sh/ruff/rules/redundant-final-literal/) (`PYI064`)
|
||||
- [`bad-version-info-order`](https://docs.astral.sh/ruff/rules/bad-version-info-order/) (`PYI066`)
|
||||
- [`parenthesize-chained-operators`](https://docs.astral.sh/ruff/rules/parenthesize-chained-operators/) (`RUF021`)
|
||||
@@ -216,7 +257,7 @@ The following fixes have been stabilized:
|
||||
### Preview features
|
||||
|
||||
- Fix `E221` and `E222` to flag missing or extra whitespace around `==` operator ([#13890](https://github.com/astral-sh/ruff/pull/13890))
|
||||
- Formatter: Alternate quotes for strings inside f-strings in preview ([#13860](https://github.com/astral-sh/ruff/pull/13860))
|
||||
- Formatter: Alternate quotes for strings inside f-strings in preview ([#13860](https://github.com/astral-sh/ruff/pull/13860))
|
||||
- Formatter: Join implicit concatenated strings when they fit on a line ([#13663](https://github.com/astral-sh/ruff/pull/13663))
|
||||
- \[`pylint`\] Restrict `iteration-over-set` to only work on sets of literals (`PLC0208`) ([#13731](https://github.com/astral-sh/ruff/pull/13731))
|
||||
|
||||
@@ -1077,7 +1118,7 @@ The following deprecated CLI commands have been removed:
|
||||
### Preview features
|
||||
|
||||
- \[`flake8-bugbear`\] Implement `return-in-generator` (`B901`) ([#11644](https://github.com/astral-sh/ruff/pull/11644))
|
||||
- \[`flake8-pyi`\] Implement `PYI063` ([#11699](https://github.com/astral-sh/ruff/pull/11699))
|
||||
- \[`flake8-pyi`\] Implement `pep484-style-positional-only-parameter` (`PYI063`) ([#11699](https://github.com/astral-sh/ruff/pull/11699))
|
||||
- \[`pygrep_hooks`\] Check blanket ignores via file-level pragmas (`PGH004`) ([#11540](https://github.com/astral-sh/ruff/pull/11540))
|
||||
|
||||
### Rule changes
|
||||
@@ -1231,7 +1272,7 @@ To read more about this exciting milestone, check out our [blog post](https://as
|
||||
### Preview features
|
||||
|
||||
- \[`pycodestyle`\] Ignore end-of-line comments when determining blank line rules ([#11342](https://github.com/astral-sh/ruff/pull/11342))
|
||||
- \[`pylint`\] Detect `pathlib.Path.open` calls in `unspecified-encoding` (`PLW1514`) ([#11288](https://github.com/astral-sh/ruff/pull/11288))
|
||||
- \[`pylint`\] Detect `pathlib.Path.open` calls in `unspecified-encoding` (`PLW1514`) ([#11288](https://github.com/astral-sh/ruff/pull/11288))
|
||||
- \[`flake8-pyi`\] Implement `PYI059` (`generic-not-last-base-class`) ([#11233](https://github.com/astral-sh/ruff/pull/11233))
|
||||
- \[`flake8-pyi`\] Implement `PYI062` (`duplicate-literal-member`) ([#11269](https://github.com/astral-sh/ruff/pull/11269))
|
||||
|
||||
@@ -1606,7 +1647,7 @@ To setup `ruff server` with your editor, refer to the [README.md](https://github
|
||||
- \[`pycodestyle`\] Do not ignore lines before the first logical line in blank lines rules. ([#10382](https://github.com/astral-sh/ruff/pull/10382))
|
||||
- \[`pycodestyle`\] Do not trigger `E225` and `E275` when the next token is a ')' ([#10315](https://github.com/astral-sh/ruff/pull/10315))
|
||||
- \[`pylint`\] Avoid false-positive slot non-assignment for `__dict__` (`PLE0237`) ([#10348](https://github.com/astral-sh/ruff/pull/10348))
|
||||
- Gate f-string struct size test for Rustc \< 1.76 ([#10371](https://github.com/astral-sh/ruff/pull/10371))
|
||||
- Gate f-string struct size test for Rustc < 1.76 ([#10371](https://github.com/astral-sh/ruff/pull/10371))
|
||||
|
||||
### Documentation
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ At a high level, the steps involved in adding a new lint rule are as follows:
|
||||
1. Create a file for your rule (e.g., `crates/ruff_linter/src/rules/flake8_bugbear/rules/assert_false.rs`).
|
||||
|
||||
1. In that file, define a violation struct (e.g., `pub struct AssertFalse`). You can grep for
|
||||
`#[violation]` to see examples.
|
||||
`#[derive(ViolationMetadata)]` to see examples.
|
||||
|
||||
1. In that file, define a function that adds the violation to the diagnostic list as appropriate
|
||||
(e.g., `pub(crate) fn assert_false`) based on whatever inputs are required for the rule (e.g.,
|
||||
@@ -863,7 +863,7 @@ each configuration file.
|
||||
|
||||
The package root is used to determine a file's "module path". Consider, again, `baz.py`. In that
|
||||
case, `./my_project/src/foo` was identified as the package root, so the module path for `baz.py`
|
||||
would resolve to `foo.bar.baz` — as computed by taking the relative path from the package root
|
||||
would resolve to `foo.bar.baz` — as computed by taking the relative path from the package root
|
||||
(inclusive of the root itself). The module path can be thought of as "the path you would use to
|
||||
import the module" (e.g., `import foo.bar.baz`).
|
||||
|
||||
|
||||
287
Cargo.lock
generated
287
Cargo.lock
generated
@@ -413,7 +413,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -693,7 +693,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim 0.10.0",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -704,7 +704,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -758,23 +758,23 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "dir-test"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c44bdf9319ad5223afb7eb15a7110452b0adf0373ea6756561b2c708eef0dd1"
|
||||
checksum = "b12781621d53fd9087021f5a338df5c57c04f84a6231c1f4726f45e2e333470b"
|
||||
dependencies = [
|
||||
"dir-test-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dir-test-macros"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "644f96047137dfaa7a09e34d4623f9e52a1926ecc25ba32ad2ba3fc422536b25"
|
||||
checksum = "1340852f50b2285d01a7f598cc5d08b572669c3e09e614925175cc3c26787b91"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -826,7 +826,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1068,9 +1068,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.1"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
|
||||
[[package]]
|
||||
name = "hashlink"
|
||||
@@ -1246,7 +1246,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1314,12 +1314,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.6.0"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
|
||||
checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.15.1",
|
||||
"hashbrown 0.15.2",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@@ -1420,7 +1420,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1485,10 +1485,11 @@ checksum = "8b23360e99b8717f20aaa4598f5a6541efbe30630039fbc7706cf954a87947ae"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.72"
|
||||
version = "0.3.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9"
|
||||
checksum = "a865e038f7f6ed956f788f0d7d60c541fff74c7bd74272c5d4cf15c63743e705"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
@@ -1520,15 +1521,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.164"
|
||||
version = "0.2.167"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f"
|
||||
checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc"
|
||||
|
||||
[[package]]
|
||||
name = "libcst"
|
||||
version = "1.5.0"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1586dd7a857d8a61a577afde1a24cc9573ff549eff092d5ce968b1ec93cc61b6"
|
||||
checksum = "fa3e60579a8cba3d86aa4a5f7fc98973cc0fd2ac270bf02f85a9bef09700b075"
|
||||
dependencies = [
|
||||
"chic",
|
||||
"libcst_derive",
|
||||
@@ -1546,7 +1547,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2ae40017ac09cd2c6a53504cb3c871c7f2b41466eac5bc66ba63f39073b467b"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1710,9 +1711,9 @@ checksum = "308d96db8debc727c3fd9744aac51751243420e46edf401010908da7f8d5e57c"
|
||||
|
||||
[[package]]
|
||||
name = "newtype-uuid"
|
||||
version = "1.1.0"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3526cb7c660872e401beaf3297f95f548ce3b4b4bdd8121b7c0713771d7c4a6e"
|
||||
checksum = "4c8781e2ef64806278a55ad223f0bc875772fd40e1fe6e73e8adbf027817229d"
|
||||
dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
@@ -1833,9 +1834,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||
|
||||
[[package]]
|
||||
name = "ordermap"
|
||||
version = "0.5.3"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31f2bd7b03bf2c767e1bb7b91505dbe022833776e60480275e6f2fb0db0c7503"
|
||||
checksum = "f80a48eb68b6a7da9829b8b0429011708f775af80676a91063d023a66a656106"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
]
|
||||
@@ -1910,9 +1911,9 @@ checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42"
|
||||
|
||||
[[package]]
|
||||
name = "pathdiff"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d61c5ce1153ab5b689d0c074c4e7fc613e942dfb7dd9eea5ab202d2ad91fe361"
|
||||
checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
|
||||
|
||||
[[package]]
|
||||
name = "peg"
|
||||
@@ -1965,7 +1966,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"pep440_rs",
|
||||
"regex",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"serde",
|
||||
"smallvec",
|
||||
"thiserror 1.0.67",
|
||||
@@ -2012,7 +2013,7 @@ dependencies = [
|
||||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2127,9 +2128,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.89"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
|
||||
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -2150,28 +2151,48 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quick-junit"
|
||||
version = "0.5.0"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62ffd2f9a162cfae131bed6d9d1ed60adced33be340a94f96952897d7cb0c240"
|
||||
checksum = "3ed1a693391a16317257103ad06a88c6529ac640846021da7c435a06fffdacd7"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"indexmap",
|
||||
"newtype-uuid",
|
||||
"quick-xml",
|
||||
"strip-ansi-escapes",
|
||||
"thiserror 1.0.67",
|
||||
"thiserror 2.0.3",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.36.1"
|
||||
version = "0.37.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96a05e2e8efddfa51a84ca47cec303fac86c8541b686d37cac5efc0e094417bc"
|
||||
checksum = "f22f29bdff3987b4d8632ef95fd6424ec7e4e0a57e2f4fc63e489e75357f6a03"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quickcheck"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6"
|
||||
dependencies = [
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quickcheck_macros"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b22a693222d716a9587786f37ac3f6b4faedb5b80c23914e7303ff5a1d8016e9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
@@ -2266,12 +2287,14 @@ dependencies = [
|
||||
"compact_str",
|
||||
"countme",
|
||||
"dir-test",
|
||||
"hashbrown 0.15.1",
|
||||
"hashbrown 0.15.2",
|
||||
"indexmap",
|
||||
"insta",
|
||||
"itertools 0.13.0",
|
||||
"memchr",
|
||||
"ordermap",
|
||||
"quickcheck",
|
||||
"quickcheck_macros",
|
||||
"red_knot_test",
|
||||
"red_knot_vendored",
|
||||
"ruff_db",
|
||||
@@ -2282,7 +2305,7 @@ dependencies = [
|
||||
"ruff_python_stdlib",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"salsa",
|
||||
"serde",
|
||||
"smallvec",
|
||||
@@ -2309,7 +2332,7 @@ dependencies = [
|
||||
"ruff_python_ast",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"shellexpand",
|
||||
@@ -2322,6 +2345,7 @@ name = "red_knot_test"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"camino",
|
||||
"colored",
|
||||
"memchr",
|
||||
"red_knot_python_semantic",
|
||||
@@ -2332,7 +2356,7 @@ dependencies = [
|
||||
"ruff_python_trivia",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"salsa",
|
||||
"smallvec",
|
||||
]
|
||||
@@ -2379,8 +2403,7 @@ dependencies = [
|
||||
"ruff_cache",
|
||||
"ruff_db",
|
||||
"ruff_python_ast",
|
||||
"ruff_text_size",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"salsa",
|
||||
"serde",
|
||||
"thiserror 2.0.3",
|
||||
@@ -2489,7 +2512,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.8.0"
|
||||
version = "0.8.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argfile",
|
||||
@@ -2529,7 +2552,7 @@ dependencies = [
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"ruff_workspace",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"shellexpand",
|
||||
@@ -2560,7 +2583,7 @@ dependencies = [
|
||||
"ruff_python_formatter",
|
||||
"ruff_python_parser",
|
||||
"ruff_python_trivia",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tikv-jemallocator",
|
||||
@@ -2602,11 +2625,10 @@ dependencies = [
|
||||
"ruff_python_trivia",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"salsa",
|
||||
"serde",
|
||||
"tempfile",
|
||||
"thiserror 2.0.3",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"tracing-tree",
|
||||
@@ -2671,7 +2693,7 @@ dependencies = [
|
||||
"ruff_cache",
|
||||
"ruff_macros",
|
||||
"ruff_text_size",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"schemars",
|
||||
"serde",
|
||||
"static_assertions",
|
||||
@@ -2708,7 +2730,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_linter"
|
||||
version = "0.8.0"
|
||||
version = "0.8.1"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"annotate-snippets 0.9.2",
|
||||
@@ -2749,7 +2771,7 @@ dependencies = [
|
||||
"ruff_python_trivia",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -2775,7 +2797,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"ruff_python_trivia",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2811,7 +2833,7 @@ dependencies = [
|
||||
"ruff_python_trivia",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
@@ -2857,7 +2879,7 @@ dependencies = [
|
||||
"ruff_python_trivia",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -2904,7 +2926,7 @@ dependencies = [
|
||||
"ruff_python_trivia",
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"static_assertions",
|
||||
"unicode-ident",
|
||||
"unicode-normalization",
|
||||
@@ -2935,7 +2957,7 @@ dependencies = [
|
||||
"ruff_python_parser",
|
||||
"ruff_python_stdlib",
|
||||
"ruff_text_size",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
@@ -2993,7 +3015,7 @@ dependencies = [
|
||||
"ruff_source_file",
|
||||
"ruff_text_size",
|
||||
"ruff_workspace",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"shellexpand",
|
||||
@@ -3023,7 +3045,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_wasm"
|
||||
version = "0.8.0"
|
||||
version = "0.8.1"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"console_log",
|
||||
@@ -3074,7 +3096,7 @@ dependencies = [
|
||||
"ruff_python_semantic",
|
||||
"ruff_python_stdlib",
|
||||
"ruff_source_file",
|
||||
"rustc-hash 2.0.0",
|
||||
"rustc-hash 2.1.0",
|
||||
"schemars",
|
||||
"serde",
|
||||
"shellexpand",
|
||||
@@ -3101,9 +3123,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "2.0.0"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152"
|
||||
checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
@@ -3120,9 +3142,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.10"
|
||||
version = "0.23.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402"
|
||||
checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1"
|
||||
dependencies = [
|
||||
"log",
|
||||
"once_cell",
|
||||
@@ -3135,15 +3157,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.7.0"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
|
||||
checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b"
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.102.5"
|
||||
version = "0.102.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9a6fccd794a42c2c105b513a2f62bc3fd8f3ba57a4593677ceb0bd035164d78"
|
||||
checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
@@ -3165,7 +3187,7 @@ checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
|
||||
[[package]]
|
||||
name = "salsa"
|
||||
version = "0.18.0"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=254c749b02cde2fd29852a7463a33e800b771758#254c749b02cde2fd29852a7463a33e800b771758"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=e68679b3a9c2b5cfd8eab92de89edf4073b03601#e68679b3a9c2b5cfd8eab92de89edf4073b03601"
|
||||
dependencies = [
|
||||
"append-only-vec",
|
||||
"arc-swap",
|
||||
@@ -3175,7 +3197,8 @@ dependencies = [
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"parking_lot",
|
||||
"rustc-hash 2.0.0",
|
||||
"rayon",
|
||||
"rustc-hash 2.1.0",
|
||||
"salsa-macro-rules",
|
||||
"salsa-macros",
|
||||
"smallvec",
|
||||
@@ -3185,17 +3208,17 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "salsa-macro-rules"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=254c749b02cde2fd29852a7463a33e800b771758#254c749b02cde2fd29852a7463a33e800b771758"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=e68679b3a9c2b5cfd8eab92de89edf4073b03601#e68679b3a9c2b5cfd8eab92de89edf4073b03601"
|
||||
|
||||
[[package]]
|
||||
name = "salsa-macros"
|
||||
version = "0.18.0"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=254c749b02cde2fd29852a7463a33e800b771758#254c749b02cde2fd29852a7463a33e800b771758"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=e68679b3a9c2b5cfd8eab92de89edf4073b03601#e68679b3a9c2b5cfd8eab92de89edf4073b03601"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@@ -3229,7 +3252,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_derive_internals",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3278,7 +3301,7 @@ checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3289,7 +3312,7 @@ checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3312,7 +3335,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3353,7 +3376,7 @@ dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3387,9 +3410,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "similar"
|
||||
version = "2.5.0"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640"
|
||||
checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e"
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
@@ -3461,7 +3484,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3483,9 +3506,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.87"
|
||||
version = "2.0.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
|
||||
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3500,7 +3523,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3563,7 +3586,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3574,7 +3597,7 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
"test-case-core",
|
||||
]
|
||||
|
||||
@@ -3604,7 +3627,7 @@ checksum = "b607164372e89797d78b8e23a6d67d5d1038c1c65efd52e1389ef8b77caba2a6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3615,7 +3638,7 @@ checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3719,9 +3742,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.40"
|
||||
version = "0.1.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
|
||||
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
@@ -3731,20 +3754,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.27"
|
||||
version = "0.1.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.32"
|
||||
version = "0.1.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
||||
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
@@ -3786,9 +3809,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.18"
|
||||
version = "0.3.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
|
||||
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
|
||||
dependencies = [
|
||||
"matchers",
|
||||
"nu-ansi-term 0.46.0",
|
||||
@@ -3876,9 +3899,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.13"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
@@ -3937,25 +3960,28 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "ureq"
|
||||
version = "2.10.1"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a"
|
||||
checksum = "b30e6f97efe1fa43535ee241ee76967d3ff6ff3953ebb430d8d55c5393029e7b"
|
||||
dependencies = [
|
||||
"base64 0.22.0",
|
||||
"flate2",
|
||||
"litemap",
|
||||
"log",
|
||||
"once_cell",
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"url",
|
||||
"webpki-roots",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.3"
|
||||
version = "2.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada"
|
||||
checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
@@ -4007,7 +4033,7 @@ checksum = "6b91f57fe13a38d0ce9e28a03463d8d3c2468ed03d75375110ec71d93b449a08"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4082,9 +4108,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.95"
|
||||
version = "0.2.97"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
|
||||
checksum = "d15e63b4482863c109d70a7b8706c1e364eb6ea449b201a76c5b89cedcec2d5c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
@@ -4093,36 +4119,37 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.95"
|
||||
version = "0.2.97"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358"
|
||||
checksum = "8d36ef12e3aaca16ddd3f67922bc63e48e953f126de60bd33ccc0101ef9998cd"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.45"
|
||||
version = "0.4.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b"
|
||||
checksum = "9dfaf8f50e5f293737ee323940c7d8b08a66a95a419223d9f41610ca08b0833d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.95"
|
||||
version = "0.2.97"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56"
|
||||
checksum = "705440e08b42d3e4b36de7d66c944be628d579796b8090bfa3471478a2260051"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -4130,32 +4157,32 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.95"
|
||||
version = "0.2.97"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
|
||||
checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.95"
|
||||
version = "0.2.97"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
|
||||
checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test"
|
||||
version = "0.3.45"
|
||||
version = "0.3.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d381749acb0943d357dcbd8f0b100640679883fcdeeef04def49daf8d33a5426"
|
||||
checksum = "3d919bb60ebcecb9160afee6c71b43a58a4f0517a2de0054cd050d02cec08201"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"js-sys",
|
||||
"minicov",
|
||||
"once_cell",
|
||||
"scoped-tls",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
@@ -4164,20 +4191,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-test-macro"
|
||||
version = "0.3.45"
|
||||
version = "0.3.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c97b2ef2c8d627381e51c071c2ab328eac606d3f69dd82bcbca20a9e389d95f0"
|
||||
checksum = "222ebde6ea87fbfa6bdd2e9f1fd8a91d60aee5db68792632176c4e16a74fc7d8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.69"
|
||||
version = "0.3.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
|
||||
checksum = "a98bc3c33f0fe7e59ad7cd041b89034fa82a7c2d4365ca538dda6cdaf513863c"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
@@ -4473,7 +4500,7 @@ checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@@ -4494,7 +4521,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4514,7 +4541,7 @@ checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@@ -4543,7 +4570,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -65,7 +65,7 @@ compact_str = "0.8.0"
|
||||
criterion = { version = "0.5.1", default-features = false }
|
||||
crossbeam = { version = "0.8.4" }
|
||||
dashmap = { version = "6.0.1" }
|
||||
dir-test = { version = "0.3.0" }
|
||||
dir-test = { version = "0.4.0" }
|
||||
dunce = { version = "1.0.5" }
|
||||
drop_bomb = { version = "0.1.5" }
|
||||
env_logger = { version = "0.11.0" }
|
||||
@@ -118,7 +118,7 @@ rand = { version = "0.8.5" }
|
||||
rayon = { version = "1.10.0" }
|
||||
regex = { version = "1.10.2" }
|
||||
rustc-hash = { version = "2.0.0" }
|
||||
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "254c749b02cde2fd29852a7463a33e800b771758" }
|
||||
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "e68679b3a9c2b5cfd8eab92de89edf4073b03601" }
|
||||
schemars = { version = "0.8.16" }
|
||||
seahash = { version = "4.1.0" }
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
|
||||
@@ -136,8 +136,8 @@ curl -LsSf https://astral.sh/ruff/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/install.ps1 | iex"
|
||||
|
||||
# For a specific version.
|
||||
curl -LsSf https://astral.sh/ruff/0.8.0/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/0.8.0/install.ps1 | iex"
|
||||
curl -LsSf https://astral.sh/ruff/0.8.1/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/0.8.1/install.ps1 | iex"
|
||||
```
|
||||
|
||||
You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff),
|
||||
@@ -170,7 +170,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff
|
||||
```yaml
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.8.0
|
||||
rev: v0.8.1
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
|
||||
36
clippy.toml
36
clippy.toml
@@ -1,21 +1,25 @@
|
||||
doc-valid-idents = [
|
||||
"..",
|
||||
"CodeQL",
|
||||
"FastAPI",
|
||||
"IPython",
|
||||
"LangChain",
|
||||
"LibCST",
|
||||
"McCabe",
|
||||
"NumPy",
|
||||
"SCREAMING_SNAKE_CASE",
|
||||
"SQLAlchemy",
|
||||
"StackOverflow",
|
||||
"PyCharm",
|
||||
"..",
|
||||
"CodeQL",
|
||||
"FastAPI",
|
||||
"IPython",
|
||||
"LangChain",
|
||||
"LibCST",
|
||||
"McCabe",
|
||||
"NumPy",
|
||||
"SCREAMING_SNAKE_CASE",
|
||||
"SQLAlchemy",
|
||||
"StackOverflow",
|
||||
"PyCharm",
|
||||
"SNMPv1",
|
||||
"SNMPv2",
|
||||
"SNMPv3",
|
||||
"PyFlakes"
|
||||
]
|
||||
|
||||
ignore-interior-mutability = [
|
||||
# Interned is read-only. The wrapped `Rc` never gets updated.
|
||||
"ruff_formatter::format_element::Interned",
|
||||
# The expression is read-only.
|
||||
"ruff_python_ast::hashable::HashableExpr",
|
||||
# Interned is read-only. The wrapped `Rc` never gets updated.
|
||||
"ruff_formatter::format_element::Interned",
|
||||
# The expression is read-only.
|
||||
"ruff_python_ast::hashable::HashableExpr",
|
||||
]
|
||||
|
||||
@@ -103,7 +103,7 @@ called **once**.
|
||||
|
||||
## Profiling
|
||||
|
||||
Red Knot generates a folded stack trace to the current directory named `tracing.folded` when setting the environment variable `RED_KNOT_LOG_PROFILE` to `1` or `true`.
|
||||
Red Knot generates a folded stack trace to the current directory named `tracing.folded` when setting the environment variable `RED_KNOT_LOG_PROFILE` to `1` or `true`.
|
||||
|
||||
```bash
|
||||
RED_KNOT_LOG_PROFILE=1 red_knot -- --current-directory=../test -vvv
|
||||
|
||||
@@ -12,7 +12,7 @@ use red_knot_workspace::watch;
|
||||
use red_knot_workspace::watch::WorkspaceWatcher;
|
||||
use red_knot_workspace::workspace::settings::Configuration;
|
||||
use red_knot_workspace::workspace::WorkspaceMetadata;
|
||||
use ruff_db::diagnostic::Diagnostic;
|
||||
use ruff_db::diagnostic::CompileDiagnostic;
|
||||
use ruff_db::system::{OsSystem, System, SystemPath, SystemPathBuf};
|
||||
use salsa::plumbing::ZalsaDatabase;
|
||||
use target_version::TargetVersion;
|
||||
@@ -297,7 +297,7 @@ impl MainLoop {
|
||||
while let Ok(message) = self.receiver.recv() {
|
||||
match message {
|
||||
MainLoopMessage::CheckWorkspace => {
|
||||
let db = db.snapshot();
|
||||
let db = db.clone();
|
||||
let sender = self.sender.clone();
|
||||
|
||||
// Spawn a new task that checks the workspace. This needs to be done in a separate thread
|
||||
@@ -380,7 +380,7 @@ impl MainLoopCancellationToken {
|
||||
enum MainLoopMessage {
|
||||
CheckWorkspace,
|
||||
CheckCompleted {
|
||||
result: Vec<Box<dyn Diagnostic>>,
|
||||
result: Vec<CompileDiagnostic>,
|
||||
revision: u64,
|
||||
},
|
||||
ApplyChanges(Vec<watch::ChangeEvent>),
|
||||
|
||||
@@ -1,26 +1,23 @@
|
||||
#![allow(clippy::disallowed_names)]
|
||||
|
||||
use std::io::Write;
|
||||
use std::time::Duration;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
|
||||
use red_knot_python_semantic::{resolve_module, ModuleName, Program, PythonVersion, SitePackages};
|
||||
use red_knot_workspace::db::{Db, RootDatabase};
|
||||
use red_knot_workspace::watch;
|
||||
use red_knot_workspace::watch::{directory_watcher, WorkspaceWatcher};
|
||||
use red_knot_workspace::watch::{directory_watcher, ChangeEvent, WorkspaceWatcher};
|
||||
use red_knot_workspace::workspace::settings::{Configuration, SearchPathConfiguration};
|
||||
use red_knot_workspace::workspace::WorkspaceMetadata;
|
||||
use ruff_db::files::{system_path_to_file, File, FileError};
|
||||
use ruff_db::source::source_text;
|
||||
use ruff_db::system::{OsSystem, SystemPath, SystemPathBuf};
|
||||
use ruff_db::testing::setup_logging;
|
||||
use ruff_db::Upcast;
|
||||
|
||||
struct TestCase {
|
||||
db: RootDatabase,
|
||||
watcher: Option<WorkspaceWatcher>,
|
||||
changes_receiver: crossbeam::channel::Receiver<Vec<watch::ChangeEvent>>,
|
||||
changes_receiver: crossbeam::channel::Receiver<Vec<ChangeEvent>>,
|
||||
/// The temporary directory that contains the test files.
|
||||
/// We need to hold on to it in the test case or the temp files get deleted.
|
||||
_temp_dir: tempfile::TempDir,
|
||||
@@ -41,44 +38,87 @@ impl TestCase {
|
||||
&self.db
|
||||
}
|
||||
|
||||
fn stop_watch(&mut self) -> Vec<watch::ChangeEvent> {
|
||||
self.try_stop_watch(Duration::from_secs(10))
|
||||
.expect("Expected watch changes but observed none")
|
||||
#[track_caller]
|
||||
fn stop_watch<M>(&mut self, matcher: M) -> Vec<ChangeEvent>
|
||||
where
|
||||
M: MatchEvent,
|
||||
{
|
||||
// track_caller is unstable for lambdas -> That's why this is a fn
|
||||
#[track_caller]
|
||||
fn panic_with_formatted_events(events: Vec<ChangeEvent>) -> Vec<ChangeEvent> {
|
||||
panic!(
|
||||
"Didn't observe expected change:\n{}",
|
||||
events
|
||||
.into_iter()
|
||||
.map(|event| format!(" - {event:?}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
)
|
||||
}
|
||||
|
||||
self.try_stop_watch(matcher, Duration::from_secs(10))
|
||||
.unwrap_or_else(panic_with_formatted_events)
|
||||
}
|
||||
|
||||
fn try_stop_watch(&mut self, timeout: Duration) -> Option<Vec<watch::ChangeEvent>> {
|
||||
fn try_stop_watch<M>(
|
||||
&mut self,
|
||||
mut matcher: M,
|
||||
timeout: Duration,
|
||||
) -> Result<Vec<ChangeEvent>, Vec<ChangeEvent>>
|
||||
where
|
||||
M: MatchEvent,
|
||||
{
|
||||
tracing::debug!("Try stopping watch with timeout {:?}", timeout);
|
||||
|
||||
let watcher = self
|
||||
.watcher
|
||||
.take()
|
||||
.expect("Cannot call `stop_watch` more than once");
|
||||
|
||||
let mut all_events = self
|
||||
.changes_receiver
|
||||
.recv_timeout(timeout)
|
||||
.unwrap_or_default();
|
||||
watcher.flush();
|
||||
watcher.stop();
|
||||
let start = Instant::now();
|
||||
let mut all_events = Vec::new();
|
||||
|
||||
loop {
|
||||
let events = self
|
||||
.changes_receiver
|
||||
.recv_timeout(Duration::from_millis(100))
|
||||
.unwrap_or_default();
|
||||
|
||||
if events
|
||||
.iter()
|
||||
.any(|event| matcher.match_event(event) || event.is_rescan())
|
||||
{
|
||||
all_events.extend(events);
|
||||
break;
|
||||
}
|
||||
|
||||
all_events.extend(events);
|
||||
|
||||
if start.elapsed() > timeout {
|
||||
return Err(all_events);
|
||||
}
|
||||
}
|
||||
|
||||
watcher.flush();
|
||||
tracing::debug!("Flushed file watcher");
|
||||
watcher.stop();
|
||||
tracing::debug!("Stopping file watcher");
|
||||
|
||||
// Consume remaining events
|
||||
for event in &self.changes_receiver {
|
||||
all_events.extend(event);
|
||||
}
|
||||
|
||||
if all_events.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(all_events)
|
||||
Ok(all_events)
|
||||
}
|
||||
|
||||
fn take_watch_changes(&self) -> Vec<watch::ChangeEvent> {
|
||||
fn take_watch_changes(&self) -> Vec<ChangeEvent> {
|
||||
self.try_take_watch_changes(Duration::from_secs(10))
|
||||
.expect("Expected watch changes but observed none")
|
||||
}
|
||||
|
||||
fn try_take_watch_changes(&self, timeout: Duration) -> Option<Vec<watch::ChangeEvent>> {
|
||||
let Some(watcher) = &self.watcher else {
|
||||
return None;
|
||||
};
|
||||
fn try_take_watch_changes(&self, timeout: Duration) -> Option<Vec<ChangeEvent>> {
|
||||
let watcher = self.watcher.as_ref()?;
|
||||
|
||||
let mut all_events = self
|
||||
.changes_receiver
|
||||
@@ -100,7 +140,7 @@ impl TestCase {
|
||||
Some(all_events)
|
||||
}
|
||||
|
||||
fn apply_changes(&mut self, changes: Vec<watch::ChangeEvent>) {
|
||||
fn apply_changes(&mut self, changes: Vec<ChangeEvent>) {
|
||||
self.db.apply_changes(changes, Some(&self.configuration));
|
||||
}
|
||||
|
||||
@@ -136,6 +176,23 @@ impl TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
trait MatchEvent {
|
||||
fn match_event(&mut self, event: &ChangeEvent) -> bool;
|
||||
}
|
||||
|
||||
fn event_for_file(name: &str) -> impl MatchEvent + '_ {
|
||||
|event: &ChangeEvent| event.file_name() == Some(name)
|
||||
}
|
||||
|
||||
impl<F> MatchEvent for F
|
||||
where
|
||||
F: FnMut(&ChangeEvent) -> bool,
|
||||
{
|
||||
fn match_event(&mut self, event: &ChangeEvent) -> bool {
|
||||
(*self)(event)
|
||||
}
|
||||
}
|
||||
|
||||
trait SetupFiles {
|
||||
fn setup(self, root_path: &SystemPath, workspace_path: &SystemPath) -> anyhow::Result<()>;
|
||||
}
|
||||
@@ -311,7 +368,7 @@ fn new_file() -> anyhow::Result<()> {
|
||||
|
||||
std::fs::write(foo_path.as_std_path(), "print('Hello')")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("foo.py"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -334,7 +391,7 @@ fn new_ignored_file() -> anyhow::Result<()> {
|
||||
|
||||
std::fs::write(foo_path.as_std_path(), "print('Hello')")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("foo.py"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -356,7 +413,7 @@ fn changed_file() -> anyhow::Result<()> {
|
||||
|
||||
update_file(&foo_path, "print('Version 2')")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("foo.py"));
|
||||
|
||||
assert!(!changes.is_empty());
|
||||
|
||||
@@ -381,7 +438,7 @@ fn deleted_file() -> anyhow::Result<()> {
|
||||
|
||||
std::fs::remove_file(foo_path.as_std_path())?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("foo.py"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -413,7 +470,7 @@ fn move_file_to_trash() -> anyhow::Result<()> {
|
||||
trash_path.join("foo.py").as_std_path(),
|
||||
)?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("foo.py"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -445,7 +502,7 @@ fn move_file_to_workspace() -> anyhow::Result<()> {
|
||||
|
||||
std::fs::rename(foo_path.as_std_path(), foo_in_workspace_path.as_std_path())?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("foo.py"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -473,7 +530,7 @@ fn rename_file() -> anyhow::Result<()> {
|
||||
|
||||
std::fs::rename(foo_path.as_std_path(), bar_path.as_std_path())?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("bar.py"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -517,7 +574,7 @@ fn directory_moved_to_workspace() -> anyhow::Result<()> {
|
||||
std::fs::rename(sub_original_path.as_std_path(), sub_new_path.as_std_path())
|
||||
.with_context(|| "Failed to move sub directory")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("sub"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -576,7 +633,7 @@ fn directory_moved_to_trash() -> anyhow::Result<()> {
|
||||
std::fs::rename(sub_path.as_std_path(), trashed_sub.as_std_path())
|
||||
.with_context(|| "Failed to move the sub directory to the trash")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("sub"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -638,7 +695,8 @@ fn directory_renamed() -> anyhow::Result<()> {
|
||||
std::fs::rename(sub_path.as_std_path(), foo_baz.as_std_path())
|
||||
.with_context(|| "Failed to move the sub directory")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
// Linux and windows only emit an event for the newly created root directory, but not for every new component.
|
||||
let changes = case.stop_watch(event_for_file("sub"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -711,7 +769,7 @@ fn directory_deleted() -> anyhow::Result<()> {
|
||||
std::fs::remove_dir_all(sub_path.as_std_path())
|
||||
.with_context(|| "Failed to remove the sub directory")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("sub"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -748,7 +806,7 @@ fn search_path() -> anyhow::Result<()> {
|
||||
|
||||
std::fs::write(site_packages.join("a.py").as_std_path(), "class A: ...")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("a.py"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -779,7 +837,7 @@ fn add_search_path() -> anyhow::Result<()> {
|
||||
|
||||
std::fs::write(site_packages.join("a.py").as_std_path(), "class A: ...")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("a.py"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -808,9 +866,9 @@ fn remove_search_path() -> anyhow::Result<()> {
|
||||
|
||||
std::fs::write(site_packages.join("a.py").as_std_path(), "class A: ...")?;
|
||||
|
||||
let changes = case.try_stop_watch(Duration::from_millis(100));
|
||||
let changes = case.try_stop_watch(|_: &ChangeEvent| true, Duration::from_millis(100));
|
||||
|
||||
assert_eq!(changes, None);
|
||||
assert_eq!(changes, Err(vec![]));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -848,7 +906,7 @@ fn changed_versions_file() -> anyhow::Result<()> {
|
||||
"os: 3.0-",
|
||||
)?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("VERSIONS"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -901,7 +959,7 @@ fn hard_links_in_workspace() -> anyhow::Result<()> {
|
||||
// Write to the hard link target.
|
||||
update_file(foo_path, "print('Version 2')").context("Failed to update foo.py")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("foo.py"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -972,7 +1030,7 @@ fn hard_links_to_target_outside_workspace() -> anyhow::Result<()> {
|
||||
// Write to the hard link target.
|
||||
update_file(foo_path, "print('Version 2')").context("Failed to update foo.py")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(ChangeEvent::is_changed);
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -1011,7 +1069,7 @@ mod unix {
|
||||
)
|
||||
.with_context(|| "Failed to set file permissions.")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("foo.py"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -1109,7 +1167,7 @@ mod unix {
|
||||
update_file(baz_workspace, "def baz(): print('Version 3')")
|
||||
.context("Failed to update bar/baz.py")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("baz.py"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -1180,7 +1238,7 @@ mod unix {
|
||||
update_file(&patched_bar_baz, "def baz(): print('Version 2')")
|
||||
.context("Failed to update bar/baz.py")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("baz.py"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -1288,7 +1346,7 @@ mod unix {
|
||||
update_file(&baz_original, "def baz(): print('Version 2')")
|
||||
.context("Failed to update bar/baz.py")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("baz.py"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -1342,7 +1400,7 @@ fn nested_packages_delete_root() -> anyhow::Result<()> {
|
||||
|
||||
std::fs::remove_file(case.workspace_path("pyproject.toml").as_std_path())?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(ChangeEvent::is_deleted);
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -1354,7 +1412,6 @@ fn nested_packages_delete_root() -> anyhow::Result<()> {
|
||||
|
||||
#[test]
|
||||
fn added_package() -> anyhow::Result<()> {
|
||||
let _ = setup_logging();
|
||||
let mut case = setup([
|
||||
(
|
||||
"pyproject.toml",
|
||||
@@ -1396,7 +1453,7 @@ fn added_package() -> anyhow::Result<()> {
|
||||
)
|
||||
.context("failed to write pyproject.toml for package b")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(event_for_file("pyproject.toml"));
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
@@ -1439,7 +1496,7 @@ fn removed_package() -> anyhow::Result<()> {
|
||||
std::fs::remove_dir_all(case.workspace_path("packages/b").as_std_path())
|
||||
.context("failed to remove package 'b'")?;
|
||||
|
||||
let changes = case.stop_watch();
|
||||
let changes = case.stop_watch(ChangeEvent::is_deleted);
|
||||
|
||||
case.apply_changes(changes);
|
||||
|
||||
|
||||
@@ -49,6 +49,8 @@ anyhow = { workspace = true }
|
||||
dir-test = { workspace = true }
|
||||
insta = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
quickcheck = { version = "1.0.3", default-features = false }
|
||||
quickcheck_macros = { version = "1.0.0" }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
# `LiteralString`
|
||||
|
||||
`LiteralString` represents a string that is either defined directly within the source code or is
|
||||
made up of such components.
|
||||
|
||||
Parts of the testcases defined here were adapted from [the specification's examples][1].
|
||||
|
||||
## Usages
|
||||
|
||||
### Valid places
|
||||
|
||||
It can be used anywhere a type is accepted:
|
||||
|
||||
```py
|
||||
from typing import LiteralString
|
||||
|
||||
x: LiteralString
|
||||
|
||||
def f():
|
||||
reveal_type(x) # revealed: LiteralString
|
||||
```
|
||||
|
||||
### Within `Literal`
|
||||
|
||||
`LiteralString` cannot be used within `Literal`:
|
||||
|
||||
```py
|
||||
from typing import Literal, LiteralString
|
||||
|
||||
bad_union: Literal["hello", LiteralString] # error: [invalid-literal-parameter]
|
||||
bad_nesting: Literal[LiteralString] # error: [invalid-literal-parameter]
|
||||
```
|
||||
|
||||
### Parametrized
|
||||
|
||||
`LiteralString` cannot be parametrized.
|
||||
|
||||
```py
|
||||
from typing import LiteralString
|
||||
|
||||
a: LiteralString[str] # error: [invalid-type-parameter]
|
||||
b: LiteralString["foo"] # error: [invalid-type-parameter]
|
||||
```
|
||||
|
||||
### As a base class
|
||||
|
||||
Subclassing `LiteralString` leads to a runtime error.
|
||||
|
||||
```py
|
||||
from typing import LiteralString
|
||||
|
||||
class C(LiteralString): ... # error: [invalid-base]
|
||||
```
|
||||
|
||||
## Inference
|
||||
|
||||
### Common operations
|
||||
|
||||
```py
|
||||
foo: LiteralString = "foo"
|
||||
reveal_type(foo) # revealed: Literal["foo"]
|
||||
|
||||
bar: LiteralString = "bar"
|
||||
reveal_type(foo + bar) # revealed: Literal["foobar"]
|
||||
|
||||
baz: LiteralString = "baz"
|
||||
baz += foo
|
||||
reveal_type(baz) # revealed: Literal["bazfoo"]
|
||||
|
||||
qux = (foo, bar)
|
||||
reveal_type(qux) # revealed: tuple[Literal["foo"], Literal["bar"]]
|
||||
|
||||
# TODO: Infer "LiteralString"
|
||||
reveal_type(foo.join(qux)) # revealed: @Todo(call todo)
|
||||
|
||||
template: LiteralString = "{}, {}"
|
||||
reveal_type(template) # revealed: Literal["{}, {}"]
|
||||
# TODO: Infer `LiteralString`
|
||||
reveal_type(template.format(foo, bar)) # revealed: @Todo(call todo)
|
||||
```
|
||||
|
||||
### Assignability
|
||||
|
||||
`Literal[""]` is assignable to `LiteralString`, and `LiteralString` is assignable to `str`, but not
|
||||
vice versa.
|
||||
|
||||
```py
|
||||
def coinflip() -> bool:
|
||||
return True
|
||||
|
||||
foo_1: Literal["foo"] = "foo"
|
||||
bar_1: LiteralString = foo_1 # fine
|
||||
|
||||
foo_2 = "foo" if coinflip() else "bar"
|
||||
reveal_type(foo_2) # revealed: Literal["foo", "bar"]
|
||||
bar_2: LiteralString = foo_2 # fine
|
||||
|
||||
foo_3: LiteralString = "foo" * 1_000_000_000
|
||||
bar_3: str = foo_2 # fine
|
||||
|
||||
baz_1: str = str()
|
||||
qux_1: LiteralString = baz_1 # error: [invalid-assignment]
|
||||
|
||||
baz_2: LiteralString = "baz" * 1_000_000_000
|
||||
qux_2: Literal["qux"] = baz_2 # error: [invalid-assignment]
|
||||
|
||||
baz_3 = "foo" if coinflip() else 1
|
||||
reveal_type(baz_3) # revealed: Literal["foo"] | Literal[1]
|
||||
qux_3: LiteralString = baz_3 # error: [invalid-assignment]
|
||||
```
|
||||
|
||||
### Narrowing
|
||||
|
||||
```py
|
||||
lorem: LiteralString = "lorem" * 1_000_000_000
|
||||
|
||||
reveal_type(lorem) # revealed: LiteralString
|
||||
|
||||
if lorem == "ipsum":
|
||||
reveal_type(lorem) # revealed: Literal["ipsum"]
|
||||
|
||||
reveal_type(lorem) # revealed: LiteralString
|
||||
|
||||
if "" < lorem == "ipsum":
|
||||
reveal_type(lorem) # revealed: Literal["ipsum"]
|
||||
```
|
||||
|
||||
[1]: https://typing.readthedocs.io/en/latest/spec/literal.html#literalstring
|
||||
@@ -0,0 +1,62 @@
|
||||
# NoReturn & Never
|
||||
|
||||
`NoReturn` is used to annotate the return type for functions that never return. `Never` is the
|
||||
bottom type, representing the empty set of Python objects. These two annotations can be used
|
||||
interchangeably.
|
||||
|
||||
## Function Return Type Annotation
|
||||
|
||||
```py
|
||||
from typing import NoReturn
|
||||
|
||||
def stop() -> NoReturn:
|
||||
raise RuntimeError("no way")
|
||||
|
||||
# revealed: Never
|
||||
reveal_type(stop())
|
||||
```
|
||||
|
||||
## Assignment
|
||||
|
||||
```py
|
||||
from typing import NoReturn, Never, Any
|
||||
|
||||
# error: [invalid-type-parameter] "Type `typing.Never` expected no type parameter"
|
||||
x: Never[int]
|
||||
a1: NoReturn
|
||||
# TODO: Test `Never` is only available in python >= 3.11
|
||||
a2: Never
|
||||
b1: Any
|
||||
b2: int
|
||||
|
||||
def f():
|
||||
# revealed: Never
|
||||
reveal_type(a1)
|
||||
# revealed: Never
|
||||
reveal_type(a2)
|
||||
|
||||
# Never is assignable to all types.
|
||||
v1: int = a1
|
||||
v2: str = a1
|
||||
# Other types are not assignable to Never except for Never (and Any).
|
||||
v3: Never = b1
|
||||
v4: Never = a2
|
||||
v5: Any = b2
|
||||
# error: [invalid-assignment] "Object of type `Literal[1]` is not assignable to `Never`"
|
||||
v6: Never = 1
|
||||
```
|
||||
|
||||
## Typing Extensions
|
||||
|
||||
```py
|
||||
from typing_extensions import NoReturn, Never
|
||||
|
||||
x: NoReturn
|
||||
y: Never
|
||||
|
||||
def f():
|
||||
# revealed: Never
|
||||
reveal_type(x)
|
||||
# revealed: Never
|
||||
reveal_type(y)
|
||||
```
|
||||
@@ -189,3 +189,31 @@ reveal_type(d) # revealed: Foo
|
||||
## Parameter
|
||||
|
||||
TODO: Add tests once parameter inference is supported
|
||||
|
||||
## Invalid expressions
|
||||
|
||||
The expressions in these string annotations aren't valid expressions in this context but we
|
||||
shouldn't panic.
|
||||
|
||||
```py
|
||||
a: "1 or 2"
|
||||
b: "(x := 1)"
|
||||
c: "1 + 2"
|
||||
d: "lambda x: x"
|
||||
e: "x if True else y"
|
||||
f: "{'a': 1, 'b': 2}"
|
||||
g: "{1, 2}"
|
||||
h: "[i for i in range(5)]"
|
||||
i: "{i for i in range(5)}"
|
||||
j: "{i: i for i in range(5)}"
|
||||
k: "(i for i in range(5))"
|
||||
l: "await 1"
|
||||
# error: [forward-annotation-syntax-error]
|
||||
m: "yield 1"
|
||||
# error: [forward-annotation-syntax-error]
|
||||
n: "yield from 1"
|
||||
o: "1 < 2"
|
||||
p: "call()"
|
||||
r: "[1, 2]"
|
||||
s: "(1, 2)"
|
||||
```
|
||||
|
||||
@@ -22,3 +22,22 @@ reveal_type(1 if None else 2) # revealed: Literal[2]
|
||||
reveal_type(1 if "" else 2) # revealed: Literal[2]
|
||||
reveal_type(1 if 0 else 2) # revealed: Literal[2]
|
||||
```
|
||||
|
||||
## Leaked Narrowing Constraint
|
||||
|
||||
(issue #14588)
|
||||
|
||||
The test inside an if expression should not affect code outside of the expression.
|
||||
|
||||
```py
|
||||
def bool_instance() -> bool:
|
||||
return True
|
||||
|
||||
x: Literal[42, "hello"] = 42 if bool_instance() else "hello"
|
||||
|
||||
reveal_type(x) # revealed: Literal[42] | Literal["hello"]
|
||||
|
||||
_ = ... if isinstance(x, str) else ...
|
||||
|
||||
reveal_type(x) # revealed: Literal[42] | Literal["hello"]
|
||||
```
|
||||
|
||||
@@ -60,52 +60,20 @@ reveal_type(S) # revealed: Literal[S]
|
||||
|
||||
## Type params
|
||||
|
||||
A PEP695 type variable defines a value of type `typing.TypeVar` with attributes `__name__`,
|
||||
`__bounds__`, `__constraints__`, and `__default__` (the latter three all lazily evaluated):
|
||||
A PEP695 type variable defines a value of type `typing.TypeVar`.
|
||||
|
||||
```py
|
||||
def f[T, U: A, V: (A, B), W = A, X: A = A1]():
|
||||
def f[T]():
|
||||
reveal_type(T) # revealed: T
|
||||
reveal_type(T.__name__) # revealed: Literal["T"]
|
||||
reveal_type(T.__bound__) # revealed: None
|
||||
reveal_type(T.__constraints__) # revealed: tuple[()]
|
||||
reveal_type(T.__default__) # revealed: NoDefault
|
||||
|
||||
reveal_type(U) # revealed: U
|
||||
reveal_type(U.__name__) # revealed: Literal["U"]
|
||||
reveal_type(U.__bound__) # revealed: type[A]
|
||||
reveal_type(U.__constraints__) # revealed: tuple[()]
|
||||
reveal_type(U.__default__) # revealed: NoDefault
|
||||
|
||||
reveal_type(V) # revealed: V
|
||||
reveal_type(V.__name__) # revealed: Literal["V"]
|
||||
reveal_type(V.__bound__) # revealed: None
|
||||
reveal_type(V.__constraints__) # revealed: tuple[type[A], type[B]]
|
||||
reveal_type(V.__default__) # revealed: NoDefault
|
||||
|
||||
reveal_type(W) # revealed: W
|
||||
reveal_type(W.__name__) # revealed: Literal["W"]
|
||||
reveal_type(W.__bound__) # revealed: None
|
||||
reveal_type(W.__constraints__) # revealed: tuple[()]
|
||||
reveal_type(W.__default__) # revealed: type[A]
|
||||
|
||||
reveal_type(X) # revealed: X
|
||||
reveal_type(X.__name__) # revealed: Literal["X"]
|
||||
reveal_type(X.__bound__) # revealed: type[A]
|
||||
reveal_type(X.__constraints__) # revealed: tuple[()]
|
||||
reveal_type(X.__default__) # revealed: type[A1]
|
||||
|
||||
class A: ...
|
||||
class B: ...
|
||||
class A1(A): ...
|
||||
```
|
||||
|
||||
## Minimum two constraints
|
||||
|
||||
A typevar with less than two constraints emits a diagnostic and is treated as unconstrained:
|
||||
A typevar with less than two constraints emits a diagnostic:
|
||||
|
||||
```py
|
||||
# error: [invalid-typevar-constraints] "TypeVar must have at least two constrained types"
|
||||
def f[T: (int,)]():
|
||||
reveal_type(T.__constraints__) # revealed: tuple[()]
|
||||
pass
|
||||
```
|
||||
|
||||
@@ -52,3 +52,29 @@ else:
|
||||
reveal_type(x) # revealed: Literal[2, 3]
|
||||
reveal_type(y) # revealed: Literal[1, 2, 4]
|
||||
```
|
||||
|
||||
## Nested while loops
|
||||
|
||||
```py
|
||||
def flag() -> bool:
|
||||
return True
|
||||
|
||||
x = 1
|
||||
|
||||
while flag():
|
||||
x = 2
|
||||
|
||||
while flag():
|
||||
x = 3
|
||||
if flag():
|
||||
break
|
||||
else:
|
||||
x = 4
|
||||
|
||||
if flag():
|
||||
break
|
||||
else:
|
||||
x = 5
|
||||
|
||||
reveal_type(x) # revealed: Literal[3, 4, 5]
|
||||
```
|
||||
|
||||
@@ -256,7 +256,7 @@ class O: ...
|
||||
class X(O): ...
|
||||
class Y(O): ...
|
||||
|
||||
if bool():
|
||||
if returns_bool():
|
||||
foo = Y
|
||||
else:
|
||||
foo = object
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
## Narrowing for `bool(..)` checks
|
||||
|
||||
```py
|
||||
def flag() -> bool: ...
|
||||
|
||||
x = 1 if flag() else None
|
||||
|
||||
# valid invocation, positive
|
||||
reveal_type(x) # revealed: Literal[1] | None
|
||||
if bool(x is not None):
|
||||
reveal_type(x) # revealed: Literal[1]
|
||||
|
||||
# valid invocation, negative
|
||||
reveal_type(x) # revealed: Literal[1] | None
|
||||
if not bool(x is not None):
|
||||
reveal_type(x) # revealed: None
|
||||
|
||||
# no args/narrowing
|
||||
reveal_type(x) # revealed: Literal[1] | None
|
||||
if not bool():
|
||||
reveal_type(x) # revealed: Literal[1] | None
|
||||
|
||||
# invalid invocation, too many positional args
|
||||
reveal_type(x) # revealed: Literal[1] | None
|
||||
if bool(x is not None, 5): # TODO diagnostic
|
||||
reveal_type(x) # revealed: Literal[1] | None
|
||||
|
||||
# invalid invocation, too many kwargs
|
||||
reveal_type(x) # revealed: Literal[1] | None
|
||||
if bool(x is not None, y=5): # TODO diagnostic
|
||||
reveal_type(x) # revealed: Literal[1] | None
|
||||
```
|
||||
@@ -0,0 +1,64 @@
|
||||
# Consolidating narrowed types after if statement
|
||||
|
||||
## After if-else statements, narrowing has no effect if the variable is not mutated in any branch
|
||||
|
||||
```py
|
||||
def optional_int() -> int | None: ...
|
||||
|
||||
x = optional_int()
|
||||
|
||||
if x is None:
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
|
||||
reveal_type(x) # revealed: int | None
|
||||
```
|
||||
|
||||
## Narrowing can have a persistent effect if the variable is mutated in one branch
|
||||
|
||||
```py
|
||||
def optional_int() -> int | None: ...
|
||||
|
||||
x = optional_int()
|
||||
|
||||
if x is None:
|
||||
x = 10
|
||||
else:
|
||||
pass
|
||||
|
||||
reveal_type(x) # revealed: int
|
||||
```
|
||||
|
||||
## An if statement without an explicit `else` branch is equivalent to one with a no-op `else` branch
|
||||
|
||||
```py
|
||||
def optional_int() -> int | None: ...
|
||||
|
||||
x = optional_int()
|
||||
y = optional_int()
|
||||
|
||||
if x is None:
|
||||
x = 0
|
||||
|
||||
if y is None:
|
||||
pass
|
||||
|
||||
reveal_type(x) # revealed: int
|
||||
reveal_type(y) # revealed: int | None
|
||||
```
|
||||
|
||||
## An if-elif without an explicit else branch is equivalent to one with an empty else branch
|
||||
|
||||
```py
|
||||
def optional_int() -> int | None: ...
|
||||
|
||||
x = optional_int()
|
||||
|
||||
if x is None:
|
||||
x = 0
|
||||
elif x > 50:
|
||||
x = 50
|
||||
|
||||
reveal_type(x) # revealed: int
|
||||
```
|
||||
@@ -21,10 +21,11 @@ reveal_type(Identity[0]) # revealed: str
|
||||
## Class getitem union
|
||||
|
||||
```py
|
||||
flag = True
|
||||
def bool_instance() -> bool:
|
||||
return True
|
||||
|
||||
class UnionClassGetItem:
|
||||
if flag:
|
||||
if bool_instance():
|
||||
|
||||
def __class_getitem__(cls, item: int) -> str:
|
||||
return item
|
||||
@@ -59,9 +60,10 @@ reveal_type(x[0]) # revealed: str | int
|
||||
## Class getitem with unbound method union
|
||||
|
||||
```py
|
||||
flag = True
|
||||
def bool_instance() -> bool:
|
||||
return True
|
||||
|
||||
if flag:
|
||||
if bool_instance():
|
||||
class Spam:
|
||||
def __class_getitem__(self, x: int) -> str:
|
||||
return "foo"
|
||||
@@ -77,9 +79,10 @@ reveal_type(Spam[42])
|
||||
## TODO: Class getitem non-class union
|
||||
|
||||
```py
|
||||
flag = True
|
||||
def bool_instance() -> bool:
|
||||
return True
|
||||
|
||||
if flag:
|
||||
if bool_instance():
|
||||
class Eggs:
|
||||
def __class_getitem__(self, x: int) -> str:
|
||||
return "foo"
|
||||
|
||||
@@ -30,10 +30,11 @@ reveal_type(Identity()[0]) # revealed: int
|
||||
## Getitem union
|
||||
|
||||
```py
|
||||
flag = True
|
||||
def bool_instance() -> bool:
|
||||
return True
|
||||
|
||||
class Identity:
|
||||
if flag:
|
||||
if bool_instance():
|
||||
|
||||
def __getitem__(self, index: int) -> int:
|
||||
return index
|
||||
|
||||
@@ -19,6 +19,7 @@ pub(crate) mod tests {
|
||||
use super::Db;
|
||||
|
||||
#[salsa::db]
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct TestDb {
|
||||
storage: salsa::Storage<Self>,
|
||||
files: Files,
|
||||
|
||||
@@ -416,7 +416,7 @@ impl<'db> Iterator for SearchPathIterator<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> FusedIterator for SearchPathIterator<'db> {}
|
||||
impl FusedIterator for SearchPathIterator<'_> {}
|
||||
|
||||
/// Represents a single `.pth` file in a `site-packages` directory.
|
||||
/// One or more lines in a `.pth` file may be a (relative or absolute)
|
||||
|
||||
@@ -36,12 +36,25 @@ use super::definition::{
|
||||
|
||||
mod except_handlers;
|
||||
|
||||
/// Are we in a state where a `break` statement is allowed?
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum LoopState {
|
||||
InLoop,
|
||||
NotInLoop,
|
||||
}
|
||||
|
||||
impl LoopState {
|
||||
fn is_inside(self) -> bool {
|
||||
matches!(self, LoopState::InLoop)
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct SemanticIndexBuilder<'db> {
|
||||
// Builder state
|
||||
db: &'db dyn Db,
|
||||
file: File,
|
||||
module: &'db ParsedModule,
|
||||
scope_stack: Vec<FileScopeId>,
|
||||
scope_stack: Vec<(FileScopeId, LoopState)>,
|
||||
/// The assignments we're currently visiting, with
|
||||
/// the most recent visit at the end of the Vec
|
||||
current_assignments: Vec<CurrentAssignment<'db>>,
|
||||
@@ -103,9 +116,24 @@ impl<'db> SemanticIndexBuilder<'db> {
|
||||
*self
|
||||
.scope_stack
|
||||
.last()
|
||||
.map(|(scope, _)| scope)
|
||||
.expect("Always to have a root scope")
|
||||
}
|
||||
|
||||
fn loop_state(&self) -> LoopState {
|
||||
self.scope_stack
|
||||
.last()
|
||||
.expect("Always to have a root scope")
|
||||
.1
|
||||
}
|
||||
|
||||
fn set_inside_loop(&mut self, state: LoopState) {
|
||||
self.scope_stack
|
||||
.last_mut()
|
||||
.expect("Always to have a root scope")
|
||||
.1 = state;
|
||||
}
|
||||
|
||||
fn push_scope(&mut self, node: NodeWithScopeRef) {
|
||||
let parent = self.current_scope();
|
||||
self.push_scope_with_parent(node, Some(parent));
|
||||
@@ -136,11 +164,11 @@ impl<'db> SemanticIndexBuilder<'db> {
|
||||
|
||||
debug_assert_eq!(ast_id_scope, file_scope_id);
|
||||
|
||||
self.scope_stack.push(file_scope_id);
|
||||
self.scope_stack.push((file_scope_id, LoopState::NotInLoop));
|
||||
}
|
||||
|
||||
fn pop_scope(&mut self) -> FileScopeId {
|
||||
let id = self.scope_stack.pop().expect("Root scope to be present");
|
||||
let (id, _) = self.scope_stack.pop().expect("Root scope to be present");
|
||||
let children_end = self.scopes.next_index();
|
||||
let scope = &mut self.scopes[id];
|
||||
scope.descendents = scope.descendents.start..children_end;
|
||||
@@ -286,10 +314,6 @@ impl<'db> SemanticIndexBuilder<'db> {
|
||||
self.current_assignments.last().copied()
|
||||
}
|
||||
|
||||
fn current_assignment_mut(&mut self) -> Option<&mut CurrentAssignment<'db>> {
|
||||
self.current_assignments.last_mut()
|
||||
}
|
||||
|
||||
fn add_pattern_constraint(
|
||||
&mut self,
|
||||
subject: &ast::Expr,
|
||||
@@ -658,7 +682,7 @@ where
|
||||
ast::Expr::List(_) | ast::Expr::Tuple(_) => {
|
||||
Some(CurrentAssignment::Assign {
|
||||
node,
|
||||
first: true,
|
||||
|
||||
unpack: Some(Unpack::new(
|
||||
self.db,
|
||||
self.file,
|
||||
@@ -672,11 +696,9 @@ where
|
||||
)),
|
||||
})
|
||||
}
|
||||
ast::Expr::Name(_) => Some(CurrentAssignment::Assign {
|
||||
node,
|
||||
unpack: None,
|
||||
first: false,
|
||||
}),
|
||||
ast::Expr::Name(_) => {
|
||||
Some(CurrentAssignment::Assign { node, unpack: None })
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
@@ -741,7 +763,22 @@ where
|
||||
let mut constraints = vec![constraint];
|
||||
self.visit_body(&node.body);
|
||||
let mut post_clauses: Vec<FlowSnapshot> = vec![];
|
||||
for clause in &node.elif_else_clauses {
|
||||
let elif_else_clauses = node
|
||||
.elif_else_clauses
|
||||
.iter()
|
||||
.map(|clause| (clause.test.as_ref(), clause.body.as_slice()));
|
||||
let has_else = node
|
||||
.elif_else_clauses
|
||||
.last()
|
||||
.is_some_and(|clause| clause.test.is_none());
|
||||
let elif_else_clauses = elif_else_clauses.chain(if has_else {
|
||||
// if there's an `else` clause already, we don't need to add another
|
||||
None
|
||||
} else {
|
||||
// if there's no `else` branch, we should add a no-op `else` branch
|
||||
Some((None, Default::default()))
|
||||
});
|
||||
for (clause_test, clause_body) in elif_else_clauses {
|
||||
// snapshot after every block except the last; the last one will just become
|
||||
// the state that we merge the other snapshots into
|
||||
post_clauses.push(self.flow_snapshot());
|
||||
@@ -751,24 +788,15 @@ where
|
||||
for constraint in &constraints {
|
||||
self.record_negated_constraint(*constraint);
|
||||
}
|
||||
if let Some(elif_test) = &clause.test {
|
||||
if let Some(elif_test) = clause_test {
|
||||
self.visit_expr(elif_test);
|
||||
constraints.push(self.record_expression_constraint(elif_test));
|
||||
}
|
||||
self.visit_body(&clause.body);
|
||||
self.visit_body(clause_body);
|
||||
}
|
||||
for post_clause_state in post_clauses {
|
||||
self.flow_merge(post_clause_state);
|
||||
}
|
||||
let has_else = node
|
||||
.elif_else_clauses
|
||||
.last()
|
||||
.is_some_and(|clause| clause.test.is_none());
|
||||
if !has_else {
|
||||
// if there's no else clause, then it's possible we took none of the branches,
|
||||
// and the pre_if state can reach here
|
||||
self.flow_merge(pre_if);
|
||||
}
|
||||
}
|
||||
ast::Stmt::While(ast::StmtWhile {
|
||||
test,
|
||||
@@ -785,7 +813,10 @@ where
|
||||
|
||||
// TODO: definitions created inside the body should be fully visible
|
||||
// to other statements/expressions inside the body --Alex/Carl
|
||||
let outer_loop_state = self.loop_state();
|
||||
self.set_inside_loop(LoopState::InLoop);
|
||||
self.visit_body(body);
|
||||
self.set_inside_loop(outer_loop_state);
|
||||
|
||||
// Get the break states from the body of this loop, and restore the saved outer
|
||||
// ones.
|
||||
@@ -824,7 +855,9 @@ where
|
||||
self.visit_body(body);
|
||||
}
|
||||
ast::Stmt::Break(_) => {
|
||||
self.loop_break_states.push(self.flow_snapshot());
|
||||
if self.loop_state().is_inside() {
|
||||
self.loop_break_states.push(self.flow_snapshot());
|
||||
}
|
||||
}
|
||||
|
||||
ast::Stmt::For(
|
||||
@@ -851,7 +884,10 @@ where
|
||||
// TODO: Definitions created by loop variables
|
||||
// (and definitions created inside the body)
|
||||
// are fully visible to other statements/expressions inside the body --Alex/Carl
|
||||
let outer_loop_state = self.loop_state();
|
||||
self.set_inside_loop(LoopState::InLoop);
|
||||
self.visit_body(body);
|
||||
self.set_inside_loop(outer_loop_state);
|
||||
|
||||
let break_states =
|
||||
std::mem::replace(&mut self.loop_break_states, saved_break_states);
|
||||
@@ -1040,18 +1076,13 @@ where
|
||||
|
||||
if is_definition {
|
||||
match self.current_assignment() {
|
||||
Some(CurrentAssignment::Assign {
|
||||
node,
|
||||
first,
|
||||
unpack,
|
||||
}) => {
|
||||
Some(CurrentAssignment::Assign { node, unpack }) => {
|
||||
self.add_definition(
|
||||
symbol,
|
||||
AssignmentDefinitionNodeRef {
|
||||
unpack,
|
||||
value: &node.value,
|
||||
name: name_node,
|
||||
first,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1102,11 +1133,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(CurrentAssignment::Assign { first, .. }) = self.current_assignment_mut()
|
||||
{
|
||||
*first = false;
|
||||
}
|
||||
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
ast::Expr::Named(node) => {
|
||||
@@ -1153,8 +1179,8 @@ where
|
||||
// AST inspection, so we can't simplify here, need to record test expression for
|
||||
// later checking)
|
||||
self.visit_expr(test);
|
||||
let constraint = self.record_expression_constraint(test);
|
||||
let pre_if = self.flow_snapshot();
|
||||
let constraint = self.record_expression_constraint(test);
|
||||
self.visit_expr(body);
|
||||
let post_body = self.flow_snapshot();
|
||||
self.flow_restore(pre_if);
|
||||
@@ -1313,7 +1339,6 @@ where
|
||||
enum CurrentAssignment<'a> {
|
||||
Assign {
|
||||
node: &'a ast::StmtAssign,
|
||||
first: bool,
|
||||
unpack: Option<Unpack<'a>>,
|
||||
},
|
||||
AnnAssign(&'a ast::StmtAnnAssign),
|
||||
|
||||
@@ -211,7 +211,6 @@ pub(crate) struct AssignmentDefinitionNodeRef<'a> {
|
||||
pub(crate) unpack: Option<Unpack<'a>>,
|
||||
pub(crate) value: &'a ast::Expr,
|
||||
pub(crate) name: &'a ast::ExprName,
|
||||
pub(crate) first: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
@@ -282,12 +281,10 @@ impl<'db> DefinitionNodeRef<'db> {
|
||||
unpack,
|
||||
value,
|
||||
name,
|
||||
first,
|
||||
}) => DefinitionKind::Assignment(AssignmentDefinitionKind {
|
||||
target: TargetKind::from(unpack),
|
||||
value: AstNodeRef::new(parsed.clone(), value),
|
||||
name: AstNodeRef::new(parsed, name),
|
||||
first,
|
||||
}),
|
||||
DefinitionNodeRef::AnnotatedAssignment(assign) => {
|
||||
DefinitionKind::AnnotatedAssignment(AstNodeRef::new(parsed, assign))
|
||||
@@ -374,7 +371,6 @@ impl<'db> DefinitionNodeRef<'db> {
|
||||
value: _,
|
||||
unpack: _,
|
||||
name,
|
||||
first: _,
|
||||
}) => name.into(),
|
||||
Self::AnnotatedAssignment(node) => node.into(),
|
||||
Self::AugmentedAssignment(node) => node.into(),
|
||||
@@ -591,7 +587,6 @@ pub struct AssignmentDefinitionKind<'db> {
|
||||
target: TargetKind<'db>,
|
||||
value: AstNodeRef<ast::Expr>,
|
||||
name: AstNodeRef<ast::ExprName>,
|
||||
first: bool,
|
||||
}
|
||||
|
||||
impl<'db> AssignmentDefinitionKind<'db> {
|
||||
@@ -606,10 +601,6 @@ impl<'db> AssignmentDefinitionKind<'db> {
|
||||
pub(crate) fn name(&self) -> &ast::ExprName {
|
||||
self.name.node()
|
||||
}
|
||||
|
||||
pub(crate) fn is_first(&self) -> bool {
|
||||
self.first
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
||||
@@ -400,7 +400,7 @@ pub(crate) struct ConstraintsIterator<'map, 'db> {
|
||||
constraint_ids: ConstraintIdIterator<'map>,
|
||||
}
|
||||
|
||||
impl<'map, 'db> Iterator for ConstraintsIterator<'map, 'db> {
|
||||
impl<'db> Iterator for ConstraintsIterator<'_, 'db> {
|
||||
type Item = Constraint<'db>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
@@ -424,7 +424,7 @@ impl DeclarationsIterator<'_, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'map, 'db> Iterator for DeclarationsIterator<'map, 'db> {
|
||||
impl<'db> Iterator for DeclarationsIterator<'_, 'db> {
|
||||
type Item = Definition<'db>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
|
||||
@@ -401,7 +401,7 @@ pub(super) struct DeclarationIdIterator<'a> {
|
||||
inner: DeclarationsIterator<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for DeclarationIdIterator<'a> {
|
||||
impl Iterator for DeclarationIdIterator<'_> {
|
||||
type Item = ScopedDefinitionId;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
|
||||
@@ -7,7 +7,6 @@ use ruff_db::files::File;
|
||||
use ruff_python_ast as ast;
|
||||
|
||||
pub(crate) use self::builder::{IntersectionBuilder, UnionBuilder};
|
||||
pub use self::diagnostic::{TypeCheckDiagnostic, TypeCheckDiagnostics};
|
||||
pub(crate) use self::display::TypeArrayDisplay;
|
||||
pub(crate) use self::infer::{
|
||||
infer_deferred_types, infer_definition_types, infer_expression_types, infer_scope_types,
|
||||
@@ -25,7 +24,9 @@ use crate::stdlib::{
|
||||
builtins_symbol, core_module_symbol, typing_extensions_symbol, CoreStdlibModule,
|
||||
};
|
||||
use crate::symbol::{Boundness, Symbol};
|
||||
use crate::types::diagnostic::TypeCheckDiagnosticsBuilder;
|
||||
use crate::types::diagnostic::{
|
||||
report_not_iterable, report_not_iterable_possibly_unbound, report_type_diagnostic,
|
||||
};
|
||||
use crate::types::mro::{ClassBase, Mro, MroError, MroIterator};
|
||||
use crate::types::narrow::narrowing_constraint;
|
||||
use crate::{Db, FxOrderSet, Module, Program};
|
||||
@@ -40,21 +41,20 @@ mod signatures;
|
||||
mod string_annotation;
|
||||
mod unpacker;
|
||||
|
||||
#[salsa::tracked(return_ref)]
|
||||
pub fn check_types(db: &dyn Db, file: File) -> TypeCheckDiagnostics {
|
||||
#[cfg(test)]
|
||||
mod property_tests;
|
||||
|
||||
#[salsa::tracked]
|
||||
pub fn check_types(db: &dyn Db, file: File) {
|
||||
let _span = tracing::trace_span!("check_types", file=?file.path(db)).entered();
|
||||
|
||||
tracing::debug!("Checking file '{path}'", path = file.path(db));
|
||||
|
||||
let index = semantic_index(db, file);
|
||||
let mut diagnostics = TypeCheckDiagnostics::default();
|
||||
|
||||
for scope_id in index.scope_ids() {
|
||||
let result = infer_scope_types(db, scope_id);
|
||||
diagnostics.extend(result.diagnostics());
|
||||
let _ = infer_scope_types(db, scope_id);
|
||||
}
|
||||
|
||||
diagnostics
|
||||
}
|
||||
|
||||
/// Infer the public type of a symbol (its type as seen from outside its scope).
|
||||
@@ -537,6 +537,19 @@ impl<'db> Type<'db> {
|
||||
.expect("Expected a Type::IntLiteral variant")
|
||||
}
|
||||
|
||||
pub const fn into_known_instance(self) -> Option<KnownInstanceType<'db>> {
|
||||
match self {
|
||||
Type::KnownInstance(known_instance) => Some(known_instance),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn expect_known_instance(self) -> KnownInstanceType<'db> {
|
||||
self.into_known_instance()
|
||||
.expect("Expected a Type::KnownInstance variant")
|
||||
}
|
||||
|
||||
pub const fn is_boolean_literal(&self) -> bool {
|
||||
matches!(self, Type::BooleanLiteral(..))
|
||||
}
|
||||
@@ -561,8 +574,11 @@ impl<'db> Type<'db> {
|
||||
Self::BytesLiteral(BytesLiteralType::new(db, bytes))
|
||||
}
|
||||
|
||||
pub fn tuple(db: &'db dyn Db, elements: &[Type<'db>]) -> Self {
|
||||
Self::Tuple(TupleType::new(db, elements))
|
||||
pub fn tuple<T: Into<Type<'db>>>(
|
||||
db: &'db dyn Db,
|
||||
elements: impl IntoIterator<Item = T>,
|
||||
) -> Self {
|
||||
TupleType::from_elements(db, elements)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
@@ -706,9 +722,6 @@ impl<'db> Type<'db> {
|
||||
(Type::KnownInstance(left), right) => {
|
||||
left.instance_fallback(db).is_subtype_of(db, right)
|
||||
}
|
||||
(left, Type::KnownInstance(right)) => {
|
||||
left.is_subtype_of(db, right.instance_fallback(db))
|
||||
}
|
||||
(Type::Instance(left), Type::Instance(right)) => left.is_instance_of(db, right.class),
|
||||
// TODO
|
||||
_ => false,
|
||||
@@ -1390,7 +1403,7 @@ impl<'db> Type<'db> {
|
||||
// `Any` is callable, and its return type is also `Any`.
|
||||
Type::Any => CallOutcome::callable(Type::Any),
|
||||
|
||||
Type::Todo(_) => CallOutcome::callable(todo_type!()),
|
||||
Type::Todo(_) => CallOutcome::callable(todo_type!("call todo")),
|
||||
|
||||
Type::Unknown => CallOutcome::callable(Type::Unknown),
|
||||
|
||||
@@ -1540,6 +1553,10 @@ impl<'db> Type<'db> {
|
||||
// TODO map this to a new `Type::TypeVar` variant
|
||||
Type::KnownInstance(KnownInstanceType::TypeVar(_)) => *self,
|
||||
Type::KnownInstance(KnownInstanceType::TypeAliasType(alias)) => alias.value_ty(db),
|
||||
Type::KnownInstance(KnownInstanceType::Never | KnownInstanceType::NoReturn) => {
|
||||
Type::Never
|
||||
}
|
||||
Type::KnownInstance(KnownInstanceType::LiteralString) => Type::LiteralString,
|
||||
_ => todo_type!(),
|
||||
}
|
||||
}
|
||||
@@ -1872,10 +1889,16 @@ impl<'db> KnownClass {
|
||||
pub enum KnownInstanceType<'db> {
|
||||
/// The symbol `typing.Literal` (which can also be found as `typing_extensions.Literal`)
|
||||
Literal,
|
||||
/// The symbol `typing.LiteralString` (which can also be found as `typing_extensions.LiteralString`)
|
||||
LiteralString,
|
||||
/// The symbol `typing.Optional` (which can also be found as `typing_extensions.Optional`)
|
||||
Optional,
|
||||
/// The symbol `typing.Union` (which can also be found as `typing_extensions.Union`)
|
||||
Union,
|
||||
/// The symbol `typing.NoReturn` (which can also be found as `typing_extensions.NoReturn`)
|
||||
NoReturn,
|
||||
/// The symbol `typing.Never` available since 3.11 (which can also be found as `typing_extensions.Never`)
|
||||
Never,
|
||||
/// A single instance of `typing.TypeVar`
|
||||
TypeVar(TypeVarInstance<'db>),
|
||||
/// A single instance of `typing.TypeAliasType` (PEP 695 type alias)
|
||||
@@ -1886,11 +1909,14 @@ pub enum KnownInstanceType<'db> {
|
||||
impl<'db> KnownInstanceType<'db> {
|
||||
pub const fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
KnownInstanceType::Literal => "Literal",
|
||||
KnownInstanceType::Optional => "Optional",
|
||||
KnownInstanceType::Union => "Union",
|
||||
KnownInstanceType::TypeVar(_) => "TypeVar",
|
||||
KnownInstanceType::TypeAliasType(_) => "TypeAliasType",
|
||||
Self::Literal => "Literal",
|
||||
Self::LiteralString => "LiteralString",
|
||||
Self::Optional => "Optional",
|
||||
Self::Union => "Union",
|
||||
Self::TypeVar(_) => "TypeVar",
|
||||
Self::NoReturn => "NoReturn",
|
||||
Self::Never => "Never",
|
||||
Self::TypeAliasType(_) => "TypeAliasType",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1898,9 +1924,12 @@ impl<'db> KnownInstanceType<'db> {
|
||||
pub const fn bool(self) -> Truthiness {
|
||||
match self {
|
||||
Self::Literal
|
||||
| Self::LiteralString
|
||||
| Self::Optional
|
||||
| Self::TypeVar(_)
|
||||
| Self::Union
|
||||
| Self::NoReturn
|
||||
| Self::Never
|
||||
| Self::TypeAliasType(_) => Truthiness::AlwaysTrue,
|
||||
}
|
||||
}
|
||||
@@ -1909,8 +1938,11 @@ impl<'db> KnownInstanceType<'db> {
|
||||
pub fn repr(self, db: &'db dyn Db) -> &'db str {
|
||||
match self {
|
||||
Self::Literal => "typing.Literal",
|
||||
Self::LiteralString => "typing.LiteralString",
|
||||
Self::Optional => "typing.Optional",
|
||||
Self::Union => "typing.Union",
|
||||
Self::NoReturn => "typing.NoReturn",
|
||||
Self::Never => "typing.Never",
|
||||
Self::TypeVar(typevar) => typevar.name(db),
|
||||
Self::TypeAliasType(_) => "typing.TypeAliasType",
|
||||
}
|
||||
@@ -1920,8 +1952,11 @@ impl<'db> KnownInstanceType<'db> {
|
||||
pub const fn class(self) -> KnownClass {
|
||||
match self {
|
||||
Self::Literal => KnownClass::SpecialForm,
|
||||
Self::LiteralString => KnownClass::SpecialForm,
|
||||
Self::Optional => KnownClass::SpecialForm,
|
||||
Self::Union => KnownClass::SpecialForm,
|
||||
Self::NoReturn => KnownClass::SpecialForm,
|
||||
Self::Never => KnownClass::SpecialForm,
|
||||
Self::TypeVar(_) => KnownClass::TypeVar,
|
||||
Self::TypeAliasType(_) => KnownClass::TypeAliasType,
|
||||
}
|
||||
@@ -1942,8 +1977,11 @@ impl<'db> KnownInstanceType<'db> {
|
||||
}
|
||||
match (module.name().as_str(), instance_name) {
|
||||
("typing" | "typing_extensions", "Literal") => Some(Self::Literal),
|
||||
("typing" | "typing_extensions", "LiteralString") => Some(Self::LiteralString),
|
||||
("typing" | "typing_extensions", "Optional") => Some(Self::Optional),
|
||||
("typing" | "typing_extensions", "Union") => Some(Self::Union),
|
||||
("typing" | "typing_extensions", "NoReturn") => Some(Self::NoReturn),
|
||||
("typing" | "typing_extensions", "Never") => Some(Self::Never),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -1951,23 +1989,6 @@ impl<'db> KnownInstanceType<'db> {
|
||||
fn member(self, db: &'db dyn Db, name: &str) -> Symbol<'db> {
|
||||
let ty = match (self, name) {
|
||||
(Self::TypeVar(typevar), "__name__") => Type::string_literal(db, typevar.name(db)),
|
||||
(Self::TypeVar(typevar), "__bound__") => typevar
|
||||
.upper_bound(db)
|
||||
.map(|ty| ty.to_meta_type(db))
|
||||
.unwrap_or_else(|| KnownClass::NoneType.to_instance(db)),
|
||||
(Self::TypeVar(typevar), "__constraints__") => {
|
||||
let tuple_elements: Vec<Type<'db>> = typevar
|
||||
.constraints(db)
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.map(|ty| ty.to_meta_type(db))
|
||||
.collect();
|
||||
Type::tuple(db, &tuple_elements)
|
||||
}
|
||||
(Self::TypeVar(typevar), "__default__") => typevar
|
||||
.default_ty(db)
|
||||
.map(|ty| ty.to_meta_type(db))
|
||||
.unwrap_or_else(|| KnownClass::NoDefaultType.to_instance(db)),
|
||||
(Self::TypeAliasType(alias), "__name__") => Type::string_literal(db, alias.name(db)),
|
||||
_ => return self.instance_fallback(db).member(db, name),
|
||||
};
|
||||
@@ -2000,6 +2021,7 @@ pub struct TypeVarInstance<'db> {
|
||||
}
|
||||
|
||||
impl<'db> TypeVarInstance<'db> {
|
||||
#[allow(unused)]
|
||||
pub(crate) fn upper_bound(self, db: &'db dyn Db) -> Option<Type<'db>> {
|
||||
if let Some(TypeVarBoundOrConstraints::UpperBound(ty)) = self.bound_or_constraints(db) {
|
||||
Some(ty)
|
||||
@@ -2008,7 +2030,8 @@ impl<'db> TypeVarInstance<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn constraints(self, db: &'db dyn Db) -> Option<&[Type<'db>]> {
|
||||
#[allow(unused)]
|
||||
pub(crate) fn constraints(self, db: &'db dyn Db) -> Option<&'db [Type<'db>]> {
|
||||
if let Some(TypeVarBoundOrConstraints::Constraints(tuple)) = self.bound_or_constraints(db) {
|
||||
Some(tuple.elements(db))
|
||||
} else {
|
||||
@@ -2105,19 +2128,21 @@ impl<'db> CallOutcome<'db> {
|
||||
}
|
||||
|
||||
/// Get the return type of the call, emitting default diagnostics if needed.
|
||||
fn unwrap_with_diagnostic<'a>(
|
||||
fn unwrap_with_diagnostic(
|
||||
&self,
|
||||
db: &'db dyn Db,
|
||||
file: File,
|
||||
node: ast::AnyNodeRef,
|
||||
diagnostics: &'a mut TypeCheckDiagnosticsBuilder<'db>,
|
||||
) -> Type<'db> {
|
||||
match self.return_ty_result(db, node, diagnostics) {
|
||||
match self.return_ty_result(db, file, node) {
|
||||
Ok(return_ty) => return_ty,
|
||||
Err(NotCallableError::Type {
|
||||
not_callable_ty,
|
||||
return_ty,
|
||||
}) => {
|
||||
diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
db,
|
||||
file,
|
||||
node,
|
||||
"call-non-callable",
|
||||
format_args!(
|
||||
@@ -2132,7 +2157,9 @@ impl<'db> CallOutcome<'db> {
|
||||
called_ty,
|
||||
return_ty,
|
||||
}) => {
|
||||
diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
db,
|
||||
file,
|
||||
node,
|
||||
"call-non-callable",
|
||||
format_args!(
|
||||
@@ -2148,7 +2175,9 @@ impl<'db> CallOutcome<'db> {
|
||||
called_ty,
|
||||
return_ty,
|
||||
}) => {
|
||||
diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
db,
|
||||
file,
|
||||
node,
|
||||
"call-non-callable",
|
||||
format_args!(
|
||||
@@ -2163,7 +2192,9 @@ impl<'db> CallOutcome<'db> {
|
||||
callable_ty: called_ty,
|
||||
return_ty,
|
||||
}) => {
|
||||
diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
db,
|
||||
file,
|
||||
node,
|
||||
"call-non-callable",
|
||||
format_args!(
|
||||
@@ -2177,11 +2208,11 @@ impl<'db> CallOutcome<'db> {
|
||||
}
|
||||
|
||||
/// Get the return type of the call as a result.
|
||||
fn return_ty_result<'a>(
|
||||
fn return_ty_result(
|
||||
&self,
|
||||
db: &'db dyn Db,
|
||||
file: File,
|
||||
node: ast::AnyNodeRef,
|
||||
diagnostics: &'a mut TypeCheckDiagnosticsBuilder<'db>,
|
||||
) -> Result<Type<'db>, NotCallableError<'db>> {
|
||||
match self {
|
||||
Self::Callable { return_ty } => Ok(*return_ty),
|
||||
@@ -2189,7 +2220,9 @@ impl<'db> CallOutcome<'db> {
|
||||
return_ty,
|
||||
revealed_ty,
|
||||
} => {
|
||||
diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
db,
|
||||
file,
|
||||
node,
|
||||
"revealed-type",
|
||||
format_args!("Revealed type is `{}`", revealed_ty.display(db)),
|
||||
@@ -2228,10 +2261,10 @@ impl<'db> CallOutcome<'db> {
|
||||
*return_ty
|
||||
} else {
|
||||
revealed = true;
|
||||
outcome.unwrap_with_diagnostic(db, node, diagnostics)
|
||||
outcome.unwrap_with_diagnostic(db, file, node)
|
||||
}
|
||||
}
|
||||
_ => outcome.unwrap_with_diagnostic(db, node, diagnostics),
|
||||
_ => outcome.unwrap_with_diagnostic(db, file, node),
|
||||
};
|
||||
union_builder = union_builder.add(return_ty);
|
||||
}
|
||||
@@ -2346,20 +2379,21 @@ enum IterationOutcome<'db> {
|
||||
impl<'db> IterationOutcome<'db> {
|
||||
fn unwrap_with_diagnostic(
|
||||
self,
|
||||
db: &dyn Db,
|
||||
file: File,
|
||||
iterable_node: ast::AnyNodeRef,
|
||||
diagnostics: &mut TypeCheckDiagnosticsBuilder<'db>,
|
||||
) -> Type<'db> {
|
||||
match self {
|
||||
Self::Iterable { element_ty } => element_ty,
|
||||
Self::NotIterable { not_iterable_ty } => {
|
||||
diagnostics.add_not_iterable(iterable_node, not_iterable_ty);
|
||||
report_not_iterable(db, file, iterable_node, not_iterable_ty);
|
||||
Type::Unknown
|
||||
}
|
||||
Self::PossiblyUnboundDunderIter {
|
||||
iterable_ty,
|
||||
element_ty,
|
||||
} => {
|
||||
diagnostics.add_not_iterable_possibly_unbound(iterable_node, iterable_ty);
|
||||
report_not_iterable_possibly_unbound(db, file, iterable_node, iterable_ty);
|
||||
element_ty
|
||||
}
|
||||
}
|
||||
@@ -2552,7 +2586,7 @@ impl<'db> Class<'db> {
|
||||
///
|
||||
/// Were this not a salsa query, then the calling query
|
||||
/// would depend on the class's AST and rerun for every change in that file.
|
||||
fn explicit_bases(self, db: &'db dyn Db) -> &[Type<'db>] {
|
||||
fn explicit_bases(self, db: &'db dyn Db) -> &'db [Type<'db>] {
|
||||
self.explicit_bases_query(db)
|
||||
}
|
||||
|
||||
@@ -2980,7 +3014,7 @@ pub struct SliceLiteralType<'db> {
|
||||
step: Option<i32>,
|
||||
}
|
||||
|
||||
impl<'db> SliceLiteralType<'db> {
|
||||
impl SliceLiteralType<'_> {
|
||||
fn as_tuple(self, db: &dyn Db) -> (Option<i32>, Option<i32>, Option<i32>) {
|
||||
(self.start(db), self.stop(db), self.step(db))
|
||||
}
|
||||
@@ -2992,6 +3026,23 @@ pub struct TupleType<'db> {
|
||||
}
|
||||
|
||||
impl<'db> TupleType<'db> {
|
||||
pub fn from_elements<T: Into<Type<'db>>>(
|
||||
db: &'db dyn Db,
|
||||
types: impl IntoIterator<Item = T>,
|
||||
) -> Type<'db> {
|
||||
let mut elements = vec![];
|
||||
|
||||
for ty in types {
|
||||
let ty = ty.into();
|
||||
if ty.is_never() {
|
||||
return Type::Never;
|
||||
}
|
||||
elements.push(ty);
|
||||
}
|
||||
|
||||
Type::Tuple(Self::new(db, elements.into_boxed_slice()))
|
||||
}
|
||||
|
||||
pub fn get(&self, db: &'db dyn Db, index: usize) -> Option<Type<'db>> {
|
||||
self.elements(db).get(index).copied()
|
||||
}
|
||||
@@ -3043,8 +3094,8 @@ pub(crate) mod tests {
|
||||
|
||||
/// A test representation of a type that can be transformed unambiguously into a real Type,
|
||||
/// given a db.
|
||||
#[derive(Debug, Clone)]
|
||||
enum Ty {
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) enum Ty {
|
||||
Never,
|
||||
Unknown,
|
||||
None,
|
||||
@@ -3068,7 +3119,7 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
impl Ty {
|
||||
fn into_type(self, db: &TestDb) -> Type<'_> {
|
||||
pub(crate) fn into_type(self, db: &TestDb) -> Type<'_> {
|
||||
match self {
|
||||
Ty::Never => Type::Never,
|
||||
Ty::Unknown => Type::Unknown,
|
||||
@@ -3099,13 +3150,21 @@ pub(crate) mod tests {
|
||||
builder.build()
|
||||
}
|
||||
Ty::Tuple(tys) => {
|
||||
let elements: Vec<Type> = tys.into_iter().map(|ty| ty.into_type(db)).collect();
|
||||
Type::tuple(db, &elements)
|
||||
let elements = tys.into_iter().map(|ty| ty.into_type(db));
|
||||
Type::tuple(db, elements)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test_case(Ty::Tuple(vec![Ty::Never]))]
|
||||
#[test_case(Ty::Tuple(vec![Ty::BuiltinInstance("str"), Ty::Never, Ty::BuiltinInstance("int")]))]
|
||||
#[test_case(Ty::Tuple(vec![Ty::Tuple(vec![Ty::Never])]))]
|
||||
fn tuple_containing_never_simplifies_to_never(ty: Ty) {
|
||||
let db = setup_db();
|
||||
assert_eq!(ty.into_type(&db), Type::Never);
|
||||
}
|
||||
|
||||
#[test_case(Ty::BuiltinInstance("str"), Ty::BuiltinInstance("object"))]
|
||||
#[test_case(Ty::BuiltinInstance("int"), Ty::BuiltinInstance("object"))]
|
||||
#[test_case(Ty::Unknown, Ty::IntLiteral(1))]
|
||||
@@ -3219,6 +3278,7 @@ pub(crate) mod tests {
|
||||
#[test_case(Ty::IntLiteral(1), Ty::Intersection{pos: vec![Ty::BuiltinInstance("int")], neg: vec![Ty::IntLiteral(1)]})]
|
||||
#[test_case(Ty::BuiltinClassLiteral("int"), Ty::BuiltinClassLiteral("object"))]
|
||||
#[test_case(Ty::BuiltinInstance("int"), Ty::BuiltinClassLiteral("int"))]
|
||||
#[test_case(Ty::TypingInstance("_SpecialForm"), Ty::TypingLiteral)]
|
||||
fn is_not_subtype_of(from: Ty, to: Ty) {
|
||||
let db = setup_db();
|
||||
assert!(!from.into_type(&db).is_subtype_of(&db, to.into_type(&db)));
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
use crate::types::{ClassLiteralType, Type};
|
||||
use crate::Db;
|
||||
use ruff_db::diagnostic::{Diagnostic, Severity};
|
||||
use ruff_db::diagnostic::{CompileDiagnostic, Diagnostic, Severity};
|
||||
use ruff_db::files::File;
|
||||
use ruff_python_ast::{self as ast, AnyNodeRef};
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Formatter;
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||
pub struct TypeCheckDiagnostic {
|
||||
pub(crate) struct TypeCheckDiagnostic {
|
||||
// TODO: Don't use string keys for rules
|
||||
pub(super) rule: String,
|
||||
pub(super) message: String,
|
||||
@@ -19,15 +15,15 @@ pub struct TypeCheckDiagnostic {
|
||||
}
|
||||
|
||||
impl TypeCheckDiagnostic {
|
||||
pub fn rule(&self) -> &str {
|
||||
pub(crate) fn rule(&self) -> &str {
|
||||
&self.rule
|
||||
}
|
||||
|
||||
pub fn message(&self) -> &str {
|
||||
pub(crate) fn message(&self) -> &str {
|
||||
&self.message
|
||||
}
|
||||
|
||||
pub fn file(&self) -> File {
|
||||
pub(crate) fn file(&self) -> File {
|
||||
self.file
|
||||
}
|
||||
}
|
||||
@@ -60,263 +56,209 @@ impl Ranged for TypeCheckDiagnostic {
|
||||
}
|
||||
}
|
||||
|
||||
/// A collection of type check diagnostics.
|
||||
///
|
||||
/// The diagnostics are wrapped in an `Arc` because they need to be cloned multiple times
|
||||
/// when going from `infer_expression` to `check_file`. We could consider
|
||||
/// making [`TypeCheckDiagnostic`] a Salsa struct to have them Arena-allocated (once the Tables refactor is done).
|
||||
/// Using Salsa struct does have the downside that it leaks the Salsa dependency into diagnostics and
|
||||
/// each Salsa-struct comes with an overhead.
|
||||
#[derive(Default, Eq, PartialEq)]
|
||||
pub struct TypeCheckDiagnostics {
|
||||
inner: Vec<std::sync::Arc<TypeCheckDiagnostic>>,
|
||||
}
|
||||
|
||||
impl TypeCheckDiagnostics {
|
||||
pub(super) fn push(&mut self, diagnostic: TypeCheckDiagnostic) {
|
||||
self.inner.push(Arc::new(diagnostic));
|
||||
}
|
||||
|
||||
pub(crate) fn shrink_to_fit(&mut self) {
|
||||
self.inner.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
|
||||
impl Extend<TypeCheckDiagnostic> for TypeCheckDiagnostics {
|
||||
fn extend<T: IntoIterator<Item = TypeCheckDiagnostic>>(&mut self, iter: T) {
|
||||
self.inner.extend(iter.into_iter().map(std::sync::Arc::new));
|
||||
}
|
||||
}
|
||||
|
||||
impl Extend<std::sync::Arc<TypeCheckDiagnostic>> for TypeCheckDiagnostics {
|
||||
fn extend<T: IntoIterator<Item = Arc<TypeCheckDiagnostic>>>(&mut self, iter: T) {
|
||||
self.inner.extend(iter);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Extend<&'a std::sync::Arc<TypeCheckDiagnostic>> for TypeCheckDiagnostics {
|
||||
fn extend<T: IntoIterator<Item = &'a Arc<TypeCheckDiagnostic>>>(&mut self, iter: T) {
|
||||
self.inner
|
||||
.extend(iter.into_iter().map(std::sync::Arc::clone));
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for TypeCheckDiagnostics {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
self.inner.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for TypeCheckDiagnostics {
|
||||
type Target = [std::sync::Arc<TypeCheckDiagnostic>];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for TypeCheckDiagnostics {
|
||||
type Item = Arc<TypeCheckDiagnostic>;
|
||||
type IntoIter = std::vec::IntoIter<std::sync::Arc<TypeCheckDiagnostic>>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.inner.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a TypeCheckDiagnostics {
|
||||
type Item = &'a Arc<TypeCheckDiagnostic>;
|
||||
type IntoIter = std::slice::Iter<'a, std::sync::Arc<TypeCheckDiagnostic>>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.inner.iter()
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct TypeCheckDiagnosticsBuilder<'db> {
|
||||
db: &'db dyn Db,
|
||||
/// Emit a diagnostic declaring that the object represented by `node` is not iterable
|
||||
pub(super) fn report_not_iterable(
|
||||
db: &dyn Db,
|
||||
file: File,
|
||||
diagnostics: TypeCheckDiagnostics,
|
||||
node: AnyNodeRef,
|
||||
not_iterable_ty: Type,
|
||||
) {
|
||||
report_type_diagnostic(
|
||||
db,
|
||||
file,
|
||||
node,
|
||||
"not-iterable",
|
||||
format_args!(
|
||||
"Object of type `{}` is not iterable",
|
||||
not_iterable_ty.display(db)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
impl<'db> TypeCheckDiagnosticsBuilder<'db> {
|
||||
pub(super) fn new(db: &'db dyn Db, file: File) -> Self {
|
||||
Self {
|
||||
db,
|
||||
/// Emit a diagnostic declaring that the object represented by `node` is not iterable
|
||||
/// because its `__iter__` method is possibly unbound.
|
||||
pub(super) fn report_not_iterable_possibly_unbound(
|
||||
db: &dyn Db,
|
||||
file: File,
|
||||
node: AnyNodeRef,
|
||||
element_ty: Type,
|
||||
) {
|
||||
report_type_diagnostic(
|
||||
db,
|
||||
file,
|
||||
node,
|
||||
"not-iterable",
|
||||
format_args!(
|
||||
"Object of type `{}` is not iterable because its `__iter__` method is possibly unbound",
|
||||
element_ty.display(db)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Emit a diagnostic declaring that an index is out of bounds for a tuple.
|
||||
pub(super) fn report_index_out_of_bounds(
|
||||
db: &dyn Db,
|
||||
file: File,
|
||||
kind: &'static str,
|
||||
node: AnyNodeRef,
|
||||
tuple_ty: Type,
|
||||
length: usize,
|
||||
index: i64,
|
||||
) {
|
||||
report_type_diagnostic(
|
||||
db,
|
||||
file,
|
||||
node,
|
||||
"index-out-of-bounds",
|
||||
format_args!(
|
||||
"Index {index} is out of bounds for {kind} `{}` with length {length}",
|
||||
tuple_ty.display(db)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Emit a diagnostic declaring that a type does not support subscripting.
|
||||
pub(super) fn report_non_subscriptable(
|
||||
db: &dyn Db,
|
||||
file: File,
|
||||
node: AnyNodeRef,
|
||||
non_subscriptable_ty: Type,
|
||||
method: &str,
|
||||
) {
|
||||
report_type_diagnostic(
|
||||
db,
|
||||
file,
|
||||
node,
|
||||
"non-subscriptable",
|
||||
format_args!(
|
||||
"Cannot subscript object of type `{}` with no `{method}` method",
|
||||
non_subscriptable_ty.display(db)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn report_unresolved_module<'a>(
|
||||
db: &dyn Db,
|
||||
file: File,
|
||||
import_node: impl Into<AnyNodeRef<'a>>,
|
||||
level: u32,
|
||||
module: Option<&str>,
|
||||
) {
|
||||
report_type_diagnostic(
|
||||
db,
|
||||
file,
|
||||
import_node.into(),
|
||||
"unresolved-import",
|
||||
format_args!(
|
||||
"Cannot resolve import `{}{}`",
|
||||
".".repeat(level as usize),
|
||||
module.unwrap_or_default()
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn report_slice_step_size_zero(db: &dyn Db, file: File, node: AnyNodeRef) {
|
||||
report_type_diagnostic(
|
||||
db,
|
||||
file,
|
||||
node,
|
||||
"zero-stepsize-in-slice",
|
||||
format_args!("Slice step size can not be zero"),
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn report_invalid_assignment(
|
||||
db: &dyn Db,
|
||||
file: File,
|
||||
node: AnyNodeRef,
|
||||
declared_ty: Type,
|
||||
assigned_ty: Type,
|
||||
) {
|
||||
match declared_ty {
|
||||
Type::ClassLiteral(ClassLiteralType { class }) => {
|
||||
report_type_diagnostic(db, file, node, "invalid-assignment", format_args!(
|
||||
"Implicit shadowing of class `{}`; annotate to make it explicit if this is intentional",
|
||||
class.name(db)));
|
||||
}
|
||||
Type::FunctionLiteral(function) => {
|
||||
report_type_diagnostic(db, file, node, "invalid-assignment", format_args!(
|
||||
"Implicit shadowing of function `{}`; annotate to make it explicit if this is intentional",
|
||||
function.name(db)));
|
||||
}
|
||||
_ => {
|
||||
report_type_diagnostic(
|
||||
db,
|
||||
file,
|
||||
node,
|
||||
"invalid-assignment",
|
||||
format_args!(
|
||||
"Object of type `{}` is not assignable to `{}`",
|
||||
assigned_ty.display(db),
|
||||
declared_ty.display(db),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn report_possibly_unresolved_reference(
|
||||
db: &dyn Db,
|
||||
file: File,
|
||||
expr_name_node: &ast::ExprName,
|
||||
) {
|
||||
let ast::ExprName { id, .. } = expr_name_node;
|
||||
|
||||
report_type_diagnostic(
|
||||
db,
|
||||
file,
|
||||
expr_name_node.into(),
|
||||
"possibly-unresolved-reference",
|
||||
format_args!("Name `{id}` used when possibly not defined"),
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn report_unresolved_reference(db: &dyn Db, file: File, expr_name_node: &ast::ExprName) {
|
||||
let ast::ExprName { id, .. } = expr_name_node;
|
||||
|
||||
report_type_diagnostic(
|
||||
db,
|
||||
file,
|
||||
expr_name_node.into(),
|
||||
"unresolved-reference",
|
||||
format_args!("Name `{id}` used when not defined"),
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns `true` if any diagnostic is enabled for this file.
|
||||
pub(crate) fn is_any_diagnostic_enabled(db: &dyn Db, file: File) -> bool {
|
||||
db.is_file_open(file)
|
||||
}
|
||||
|
||||
/// Reports a diagnostic for the given node and file if diagnostic reporting is enabled for this file.
|
||||
pub(crate) fn report_type_diagnostic(
|
||||
db: &dyn Db,
|
||||
file: File,
|
||||
node: AnyNodeRef,
|
||||
rule: &str,
|
||||
message: std::fmt::Arguments,
|
||||
) {
|
||||
if !is_any_diagnostic_enabled(db, file) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Don't emit the diagnostic if:
|
||||
// * The enclosing node contains any syntax errors
|
||||
// * The rule is disabled for this file. We probably want to introduce a new query that
|
||||
// returns a rule selector for a given file that respects the package's settings,
|
||||
// any global pragma comments in the file, and any per-file-ignores.
|
||||
|
||||
CompileDiagnostic::report(
|
||||
db.upcast(),
|
||||
TypeCheckDiagnostic {
|
||||
file,
|
||||
diagnostics: TypeCheckDiagnostics::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit a diagnostic declaring that the object represented by `node` is not iterable
|
||||
pub(super) fn add_not_iterable(&mut self, node: AnyNodeRef, not_iterable_ty: Type<'db>) {
|
||||
self.add(
|
||||
node,
|
||||
"not-iterable",
|
||||
format_args!(
|
||||
"Object of type `{}` is not iterable",
|
||||
not_iterable_ty.display(self.db)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Emit a diagnostic declaring that the object represented by `node` is not iterable
|
||||
/// because its `__iter__` method is possibly unbound.
|
||||
pub(super) fn add_not_iterable_possibly_unbound(
|
||||
&mut self,
|
||||
node: AnyNodeRef,
|
||||
element_ty: Type<'db>,
|
||||
) {
|
||||
self.add(
|
||||
node,
|
||||
"not-iterable",
|
||||
format_args!(
|
||||
"Object of type `{}` is not iterable because its `__iter__` method is possibly unbound",
|
||||
element_ty.display(self.db)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Emit a diagnostic declaring that an index is out of bounds for a tuple.
|
||||
pub(super) fn add_index_out_of_bounds(
|
||||
&mut self,
|
||||
kind: &'static str,
|
||||
node: AnyNodeRef,
|
||||
tuple_ty: Type<'db>,
|
||||
length: usize,
|
||||
index: i64,
|
||||
) {
|
||||
self.add(
|
||||
node,
|
||||
"index-out-of-bounds",
|
||||
format_args!(
|
||||
"Index {index} is out of bounds for {kind} `{}` with length {length}",
|
||||
tuple_ty.display(self.db)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Emit a diagnostic declaring that a type does not support subscripting.
|
||||
pub(super) fn add_non_subscriptable(
|
||||
&mut self,
|
||||
node: AnyNodeRef,
|
||||
non_subscriptable_ty: Type<'db>,
|
||||
method: &str,
|
||||
) {
|
||||
self.add(
|
||||
node,
|
||||
"non-subscriptable",
|
||||
format_args!(
|
||||
"Cannot subscript object of type `{}` with no `{method}` method",
|
||||
non_subscriptable_ty.display(self.db)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn add_unresolved_module(
|
||||
&mut self,
|
||||
import_node: impl Into<AnyNodeRef<'db>>,
|
||||
level: u32,
|
||||
module: Option<&str>,
|
||||
) {
|
||||
self.add(
|
||||
import_node.into(),
|
||||
"unresolved-import",
|
||||
format_args!(
|
||||
"Cannot resolve import `{}{}`",
|
||||
".".repeat(level as usize),
|
||||
module.unwrap_or_default()
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn add_slice_step_size_zero(&mut self, node: AnyNodeRef) {
|
||||
self.add(
|
||||
node,
|
||||
"zero-stepsize-in-slice",
|
||||
format_args!("Slice step size can not be zero"),
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn add_invalid_assignment(
|
||||
&mut self,
|
||||
node: AnyNodeRef,
|
||||
declared_ty: Type<'db>,
|
||||
assigned_ty: Type<'db>,
|
||||
) {
|
||||
match declared_ty {
|
||||
Type::ClassLiteral(ClassLiteralType { class }) => {
|
||||
self.add(node, "invalid-assignment", format_args!(
|
||||
"Implicit shadowing of class `{}`; annotate to make it explicit if this is intentional",
|
||||
class.name(self.db)));
|
||||
}
|
||||
Type::FunctionLiteral(function) => {
|
||||
self.add(node, "invalid-assignment", format_args!(
|
||||
"Implicit shadowing of function `{}`; annotate to make it explicit if this is intentional",
|
||||
function.name(self.db)));
|
||||
}
|
||||
_ => {
|
||||
self.add(
|
||||
node,
|
||||
"invalid-assignment",
|
||||
format_args!(
|
||||
"Object of type `{}` is not assignable to `{}`",
|
||||
assigned_ty.display(self.db),
|
||||
declared_ty.display(self.db),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn add_possibly_unresolved_reference(&mut self, expr_name_node: &ast::ExprName) {
|
||||
let ast::ExprName { id, .. } = expr_name_node;
|
||||
|
||||
self.add(
|
||||
expr_name_node.into(),
|
||||
"possibly-unresolved-reference",
|
||||
format_args!("Name `{id}` used when possibly not defined"),
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn add_unresolved_reference(&mut self, expr_name_node: &ast::ExprName) {
|
||||
let ast::ExprName { id, .. } = expr_name_node;
|
||||
|
||||
self.add(
|
||||
expr_name_node.into(),
|
||||
"unresolved-reference",
|
||||
format_args!("Name `{id}` used when not defined"),
|
||||
);
|
||||
}
|
||||
|
||||
/// Adds a new diagnostic.
|
||||
///
|
||||
/// The diagnostic does not get added if the rule isn't enabled for this file.
|
||||
pub(super) fn add(&mut self, node: AnyNodeRef, rule: &str, message: std::fmt::Arguments) {
|
||||
if !self.db.is_file_open(self.file) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Don't emit the diagnostic if:
|
||||
// * The enclosing node contains any syntax errors
|
||||
// * The rule is disabled for this file. We probably want to introduce a new query that
|
||||
// returns a rule selector for a given file that respects the package's settings,
|
||||
// any global pragma comments in the file, and any per-file-ignores.
|
||||
|
||||
self.diagnostics.push(TypeCheckDiagnostic {
|
||||
file: self.file,
|
||||
rule: rule.to_string(),
|
||||
message: message.to_string(),
|
||||
range: node.range(),
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn extend(&mut self, diagnostics: &TypeCheckDiagnostics) {
|
||||
self.diagnostics.extend(diagnostics);
|
||||
}
|
||||
|
||||
pub(super) fn finish(mut self) -> TypeCheckDiagnostics {
|
||||
self.diagnostics.shrink_to_fit();
|
||||
self.diagnostics
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -289,7 +289,7 @@ struct DisplayMaybeNegatedType<'db> {
|
||||
negated: bool,
|
||||
}
|
||||
|
||||
impl<'db> Display for DisplayMaybeNegatedType<'db> {
|
||||
impl Display for DisplayMaybeNegatedType<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
if self.negated {
|
||||
f.write_str("~")?;
|
||||
@@ -319,7 +319,7 @@ pub(crate) struct DisplayTypeArray<'b, 'db> {
|
||||
db: &'db dyn Db,
|
||||
}
|
||||
|
||||
impl<'db> Display for DisplayTypeArray<'_, 'db> {
|
||||
impl Display for DisplayTypeArray<'_, '_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.join(", ")
|
||||
.entries(self.types.iter().map(|ty| ty.display(self.db)))
|
||||
|
||||
@@ -48,7 +48,11 @@ use crate::semantic_index::semantic_index;
|
||||
use crate::semantic_index::symbol::{NodeWithScopeKind, NodeWithScopeRef, ScopeId};
|
||||
use crate::semantic_index::SemanticIndex;
|
||||
use crate::stdlib::builtins_module_scope;
|
||||
use crate::types::diagnostic::{TypeCheckDiagnostics, TypeCheckDiagnosticsBuilder};
|
||||
use crate::types::diagnostic::{
|
||||
report_index_out_of_bounds, report_invalid_assignment, report_non_subscriptable,
|
||||
report_possibly_unresolved_reference, report_slice_step_size_zero, report_type_diagnostic,
|
||||
report_unresolved_module, report_unresolved_reference,
|
||||
};
|
||||
use crate::types::mro::MroErrorKind;
|
||||
use crate::types::unpacker::{UnpackResult, Unpacker};
|
||||
use crate::types::{
|
||||
@@ -216,9 +220,6 @@ pub(crate) struct TypeInference<'db> {
|
||||
/// The definitions that are deferred.
|
||||
deferred: FxHashSet<Definition<'db>>,
|
||||
|
||||
/// The diagnostics for this region.
|
||||
diagnostics: TypeCheckDiagnostics,
|
||||
|
||||
/// The scope belong to this region.
|
||||
scope: ScopeId<'db>,
|
||||
}
|
||||
@@ -230,7 +231,7 @@ impl<'db> TypeInference<'db> {
|
||||
bindings: FxHashMap::default(),
|
||||
declarations: FxHashMap::default(),
|
||||
deferred: FxHashSet::default(),
|
||||
diagnostics: TypeCheckDiagnostics::default(),
|
||||
|
||||
scope,
|
||||
}
|
||||
}
|
||||
@@ -254,15 +255,10 @@ impl<'db> TypeInference<'db> {
|
||||
self.declarations[&definition]
|
||||
}
|
||||
|
||||
pub(crate) fn diagnostics(&self) -> &TypeCheckDiagnostics {
|
||||
&self.diagnostics
|
||||
}
|
||||
|
||||
fn shrink_to_fit(&mut self) {
|
||||
self.expressions.shrink_to_fit();
|
||||
self.bindings.shrink_to_fit();
|
||||
self.declarations.shrink_to_fit();
|
||||
self.diagnostics.shrink_to_fit();
|
||||
self.deferred.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
@@ -341,8 +337,6 @@ pub(super) struct TypeInferenceBuilder<'db> {
|
||||
/// expression could be deferred if the file has `from __future__ import annotations` import or
|
||||
/// is a stub file but we're still in a non-deferred region.
|
||||
deferred_state: DeferredExpressionState,
|
||||
|
||||
diagnostics: TypeCheckDiagnosticsBuilder<'db>,
|
||||
}
|
||||
|
||||
impl<'db> TypeInferenceBuilder<'db> {
|
||||
@@ -373,7 +367,6 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
file,
|
||||
deferred_state: DeferredExpressionState::None,
|
||||
types: TypeInference::empty(scope),
|
||||
diagnostics: TypeCheckDiagnosticsBuilder::new(db, file),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,7 +379,6 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
.extend(inference.declarations.iter());
|
||||
self.types.expressions.extend(inference.expressions.iter());
|
||||
self.types.deferred.extend(inference.deferred.iter());
|
||||
self.diagnostics.extend(&inference.diagnostics);
|
||||
}
|
||||
|
||||
fn scope(&self) -> ScopeId<'db> {
|
||||
@@ -501,7 +493,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
for (class, class_node) in class_definitions {
|
||||
// (1) Check that the class does not have a cyclic definition
|
||||
if class.is_cyclically_defined(self.db) {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
class_node.into(),
|
||||
"cyclic-class-def",
|
||||
format_args!(
|
||||
@@ -521,7 +515,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
MroErrorKind::DuplicateBases(duplicates) => {
|
||||
let base_nodes = class_node.bases();
|
||||
for (index, duplicate) in duplicates {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
(&base_nodes[*index]).into(),
|
||||
"duplicate-base",
|
||||
format_args!("Duplicate base class `{}`", duplicate.name(self.db)),
|
||||
@@ -531,7 +527,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
MroErrorKind::InvalidBases(bases) => {
|
||||
let base_nodes = class_node.bases();
|
||||
for (index, base_ty) in bases {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
(&base_nodes[*index]).into(),
|
||||
"invalid-base",
|
||||
format_args!(
|
||||
@@ -541,7 +539,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
);
|
||||
}
|
||||
}
|
||||
MroErrorKind::UnresolvableMro { bases_list } => self.diagnostics.add(
|
||||
MroErrorKind::UnresolvableMro { bases_list } => report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
class_node.into(),
|
||||
"inconsistent-mro",
|
||||
format_args!(
|
||||
@@ -571,7 +571,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
} => {
|
||||
let node = class_node.into();
|
||||
if *candidate1_is_base_class {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
node,
|
||||
"conflicting-metaclass",
|
||||
format_args!(
|
||||
@@ -586,7 +588,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
)
|
||||
);
|
||||
} else {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
node,
|
||||
"conflicting-metaclass",
|
||||
format_args!(
|
||||
@@ -732,7 +736,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
_ => return,
|
||||
};
|
||||
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
expr.into(),
|
||||
"division-by-zero",
|
||||
format_args!(
|
||||
@@ -757,7 +763,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
// TODO point out the conflicting declarations in the diagnostic?
|
||||
let symbol_table = self.index.symbol_table(binding.file_scope(self.db));
|
||||
let symbol_name = symbol_table.symbol(binding.symbol(self.db)).name();
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
node,
|
||||
"conflicting-declarations",
|
||||
format_args!(
|
||||
@@ -769,8 +777,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
},
|
||||
);
|
||||
if !bound_ty.is_assignable_to(self.db, declared_ty) {
|
||||
self.diagnostics
|
||||
.add_invalid_assignment(node, declared_ty, bound_ty);
|
||||
report_invalid_assignment(self.db, self.file, node, declared_ty, bound_ty);
|
||||
// allow declarations to override inference in case of invalid assignment
|
||||
bound_ty = declared_ty;
|
||||
};
|
||||
@@ -787,7 +794,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
let ty = if inferred_ty.is_assignable_to(self.db, ty) {
|
||||
ty
|
||||
} else {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
node,
|
||||
"invalid-declaration",
|
||||
format_args!(
|
||||
@@ -813,8 +822,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
let inferred_ty = if inferred_ty.is_assignable_to(self.db, declared_ty) {
|
||||
inferred_ty
|
||||
} else {
|
||||
self.diagnostics
|
||||
.add_invalid_assignment(node, declared_ty, inferred_ty);
|
||||
report_invalid_assignment(self.db, self.file, node, declared_ty, inferred_ty);
|
||||
// if the assignment is invalid, fall back to assuming the annotation is correct
|
||||
declared_ty
|
||||
};
|
||||
@@ -1297,7 +1305,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
// TODO: Make use of Protocols when we support it (the manager be assignable to `contextlib.AbstractContextManager`).
|
||||
match (enter, exit) {
|
||||
(Symbol::Unbound, Symbol::Unbound) => {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
context_expression.into(),
|
||||
"invalid-context-manager",
|
||||
format_args!(
|
||||
@@ -1308,7 +1318,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
Type::Unknown
|
||||
}
|
||||
(Symbol::Unbound, _) => {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
context_expression.into(),
|
||||
"invalid-context-manager",
|
||||
format_args!(
|
||||
@@ -1320,7 +1332,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
}
|
||||
(Symbol::Type(enter_ty, enter_boundness), exit) => {
|
||||
if enter_boundness == Boundness::PossiblyUnbound {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
context_expression.into(),
|
||||
"invalid-context-manager",
|
||||
format_args!(
|
||||
@@ -1332,9 +1346,11 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
|
||||
let target_ty = enter_ty
|
||||
.call(self.db, &[context_expression_ty])
|
||||
.return_ty_result(self.db, context_expression.into(), &mut self.diagnostics)
|
||||
.return_ty_result(self.db, self.file, context_expression.into(), )
|
||||
.unwrap_or_else(|err| {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
context_expression.into(),
|
||||
"invalid-context-manager",
|
||||
format_args!("
|
||||
@@ -1346,7 +1362,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
|
||||
match exit {
|
||||
Symbol::Unbound => {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
context_expression.into(),
|
||||
"invalid-context-manager",
|
||||
format_args!(
|
||||
@@ -1359,7 +1377,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
// TODO: Use the `exit_ty` to determine if any raised exception is suppressed.
|
||||
|
||||
if exit_boundness == Boundness::PossiblyUnbound {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
context_expression.into(),
|
||||
"invalid-context-manager",
|
||||
format_args!(
|
||||
@@ -1379,14 +1399,12 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
Type::none(self.db),
|
||||
],
|
||||
)
|
||||
.return_ty_result(
|
||||
self.db,
|
||||
context_expression.into(),
|
||||
&mut self.diagnostics,
|
||||
)
|
||||
.return_ty_result(self.db, self.file, context_expression.into())
|
||||
.is_err()
|
||||
{
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
context_expression.into(),
|
||||
"invalid-context-manager",
|
||||
format_args!(
|
||||
@@ -1466,7 +1484,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
let bound_or_constraint = match bound.as_deref() {
|
||||
Some(expr @ ast::Expr::Tuple(ast::ExprTuple { elts, .. })) => {
|
||||
if elts.len() < 2 {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
expr.into(),
|
||||
"invalid-typevar-constraints",
|
||||
format_args!("TypeVar must have at least two constrained types"),
|
||||
@@ -1663,12 +1683,6 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
let target_ty = match assignment.target() {
|
||||
TargetKind::Sequence(unpack) => {
|
||||
let unpacked = infer_unpack_types(self.db, unpack);
|
||||
// Only copy the diagnostics if this is the first assignment to avoid duplicating the
|
||||
// unpack assignments.
|
||||
if assignment.is_first() {
|
||||
self.diagnostics.extend(unpacked.diagnostics());
|
||||
}
|
||||
|
||||
unpacked.get(name_ast_id).unwrap_or(Type::Unknown)
|
||||
}
|
||||
TargetKind::Name => value_ty,
|
||||
@@ -1777,12 +1791,14 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
let call = class_member.call(self.db, &[target_type, value_type]);
|
||||
let augmented_return_ty = match call.return_ty_result(
|
||||
self.db,
|
||||
self.file,
|
||||
AnyNodeRef::StmtAugAssign(assignment),
|
||||
&mut self.diagnostics,
|
||||
) {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
assignment.into(),
|
||||
"unsupported-operator",
|
||||
format_args!(
|
||||
@@ -1803,7 +1819,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
|
||||
let binary_return_ty = self.infer_binary_expression_type(left_ty, right_ty, op)
|
||||
.unwrap_or_else(|| {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
assignment.into(),
|
||||
"unsupported-operator",
|
||||
format_args!(
|
||||
@@ -1832,7 +1850,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
|
||||
self.infer_binary_expression_type(left_ty, right_ty, op)
|
||||
.unwrap_or_else(|| {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
assignment.into(),
|
||||
"unsupported-operator",
|
||||
format_args!(
|
||||
@@ -1919,7 +1939,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
} else {
|
||||
iterable_ty
|
||||
.iterate(self.db)
|
||||
.unwrap_with_diagnostic(iterable.into(), &mut self.diagnostics)
|
||||
.unwrap_with_diagnostic(self.db, self.file, iterable.into())
|
||||
};
|
||||
|
||||
self.store_expression_type(target, loop_var_value_ty);
|
||||
@@ -1958,7 +1978,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
if let Some(module) = self.module_ty_from_name(&module_name) {
|
||||
module
|
||||
} else {
|
||||
self.diagnostics.add_unresolved_module(alias, 0, Some(name));
|
||||
report_unresolved_module(self.db, self.file, alias, 0, Some(name));
|
||||
Type::Unknown
|
||||
}
|
||||
} else {
|
||||
@@ -2086,7 +2106,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
match module_ty.member(self.db, &ast::name::Name::new(&name.id)) {
|
||||
Symbol::Type(ty, boundness) => {
|
||||
if boundness == Boundness::PossiblyUnbound {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
AnyNodeRef::Alias(alias),
|
||||
"possibly-unbound-import",
|
||||
format_args!("Member `{name}` of module `{module_name}` is possibly unbound",),
|
||||
@@ -2096,7 +2118,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
ty
|
||||
}
|
||||
Symbol::Unbound => {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
AnyNodeRef::Alias(alias),
|
||||
"unresolved-import",
|
||||
format_args!("Module `{module_name}` has no member `{name}`",),
|
||||
@@ -2105,8 +2129,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.diagnostics
|
||||
.add_unresolved_module(import_from, *level, module);
|
||||
report_unresolved_module(self.db, self.file, import_from, *level, module);
|
||||
Type::Unknown
|
||||
}
|
||||
}
|
||||
@@ -2120,8 +2143,8 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
"Relative module resolution `{}` failed: too many leading dots",
|
||||
format_import_from_module(*level, module),
|
||||
);
|
||||
self.diagnostics
|
||||
.add_unresolved_module(import_from, *level, module);
|
||||
|
||||
report_unresolved_module(self.db, self.file, import_from, *level, module);
|
||||
Type::Unknown
|
||||
}
|
||||
Err(ModuleNameResolutionError::UnknownCurrentModule) => {
|
||||
@@ -2130,8 +2153,8 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
format_import_from_module(*level, module),
|
||||
self.file.path(self.db)
|
||||
);
|
||||
self.diagnostics
|
||||
.add_unresolved_module(import_from, *level, module);
|
||||
|
||||
report_unresolved_module(self.db, self.file, import_from, *level, module);
|
||||
Type::Unknown
|
||||
}
|
||||
};
|
||||
@@ -2596,7 +2619,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
} else {
|
||||
iterable_ty
|
||||
.iterate(self.db)
|
||||
.unwrap_with_diagnostic(iterable.into(), &mut self.diagnostics)
|
||||
.unwrap_with_diagnostic(self.db, self.file, iterable.into())
|
||||
};
|
||||
|
||||
self.types.expressions.insert(
|
||||
@@ -2697,7 +2720,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
let function_type = self.infer_expression(func);
|
||||
function_type
|
||||
.call(self.db, arg_types.as_slice())
|
||||
.unwrap_with_diagnostic(self.db, func.as_ref().into(), &mut self.diagnostics)
|
||||
.unwrap_with_diagnostic(self.db, self.file, func.as_ref().into())
|
||||
}
|
||||
|
||||
fn infer_starred_expression(&mut self, starred: &ast::ExprStarred) -> Type<'db> {
|
||||
@@ -2708,9 +2731,11 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
} = starred;
|
||||
|
||||
let iterable_ty = self.infer_expression(value);
|
||||
iterable_ty
|
||||
.iterate(self.db)
|
||||
.unwrap_with_diagnostic(value.as_ref().into(), &mut self.diagnostics);
|
||||
iterable_ty.iterate(self.db).unwrap_with_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
value.as_ref().into(),
|
||||
);
|
||||
|
||||
// TODO
|
||||
todo_type!()
|
||||
@@ -2729,9 +2754,11 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
let ast::ExprYieldFrom { range: _, value } = yield_from;
|
||||
|
||||
let iterable_ty = self.infer_expression(value);
|
||||
iterable_ty
|
||||
.iterate(self.db)
|
||||
.unwrap_with_diagnostic(value.as_ref().into(), &mut self.diagnostics);
|
||||
iterable_ty.iterate(self.db).unwrap_with_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
value.as_ref().into(),
|
||||
);
|
||||
|
||||
// TODO get type from `ReturnType` of generator
|
||||
todo_type!()
|
||||
@@ -2803,7 +2830,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
{
|
||||
let mut builtins_symbol = builtins_symbol(self.db, name);
|
||||
if builtins_symbol.is_unbound() && name == "reveal_type" {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
name_node.into(),
|
||||
"undefined-reveal",
|
||||
format_args!(
|
||||
@@ -2858,7 +2887,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
match self.lookup_name(name) {
|
||||
Symbol::Type(looked_up_ty, looked_up_boundness) => {
|
||||
if looked_up_boundness == Boundness::PossiblyUnbound {
|
||||
self.diagnostics.add_possibly_unresolved_reference(name);
|
||||
report_possibly_unresolved_reference(self.db, self.file, name);
|
||||
}
|
||||
|
||||
bindings_ty
|
||||
@@ -2867,9 +2896,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
}
|
||||
Symbol::Unbound => {
|
||||
if bindings_ty.is_some() {
|
||||
self.diagnostics.add_possibly_unresolved_reference(name);
|
||||
report_possibly_unresolved_reference(self.db, self.file, name);
|
||||
} else {
|
||||
self.diagnostics.add_unresolved_reference(name);
|
||||
report_unresolved_reference(self.db, self.file, name);
|
||||
}
|
||||
bindings_ty.unwrap_or(Type::Unknown)
|
||||
}
|
||||
@@ -2900,7 +2929,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
match value_ty.member(self.db, &attr.id) {
|
||||
Symbol::Type(member_ty, boundness) => {
|
||||
if boundness == Boundness::PossiblyUnbound {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
attribute.into(),
|
||||
"possibly-unbound-attribute",
|
||||
format_args!(
|
||||
@@ -2914,7 +2945,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
member_ty
|
||||
}
|
||||
Symbol::Unbound => {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
attribute.into(),
|
||||
"unresolved-attribute",
|
||||
format_args!(
|
||||
@@ -2988,14 +3021,13 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
{
|
||||
let call = class_member.call(self.db, &[operand_type]);
|
||||
|
||||
match call.return_ty_result(
|
||||
self.db,
|
||||
AnyNodeRef::ExprUnaryOp(unary),
|
||||
&mut self.diagnostics,
|
||||
) {
|
||||
match call.return_ty_result(self.db, self.file, AnyNodeRef::ExprUnaryOp(unary))
|
||||
{
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
unary.into(),
|
||||
"unsupported-operator",
|
||||
format_args!(
|
||||
@@ -3007,7 +3039,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
unary.into(),
|
||||
"unsupported-operator",
|
||||
format_args!(
|
||||
@@ -3048,7 +3082,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
|
||||
self.infer_binary_expression_type(left_ty, right_ty, *op)
|
||||
.unwrap_or_else(|| {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
binary.into(),
|
||||
"unsupported-operator",
|
||||
format_args!(
|
||||
@@ -3358,7 +3394,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
self.infer_binary_type_comparison(left_ty, *op, right_ty)
|
||||
.unwrap_or_else(|error| {
|
||||
// Handle unsupported operators (diagnostic, `bool`/`Unknown` outcome)
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
AnyNodeRef::ExprCompare(compare),
|
||||
"unsupported-operator",
|
||||
format_args!(
|
||||
@@ -3904,7 +3942,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
.py_index(i32::try_from(int).expect("checked in branch arm"))
|
||||
.copied()
|
||||
.unwrap_or_else(|_| {
|
||||
self.diagnostics.add_index_out_of_bounds(
|
||||
report_index_out_of_bounds(
|
||||
self.db,
|
||||
self.file,
|
||||
"tuple",
|
||||
value_node.into(),
|
||||
value_ty,
|
||||
@@ -3923,7 +3963,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
let new_elements: Vec<_> = new_elements.copied().collect();
|
||||
Type::tuple(self.db, &new_elements)
|
||||
} else {
|
||||
self.diagnostics.add_slice_step_size_zero(value_node.into());
|
||||
report_slice_step_size_zero(self.db, self.file, value_node.into());
|
||||
Type::Unknown
|
||||
}
|
||||
}
|
||||
@@ -3937,7 +3977,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
.py_index(i32::try_from(int).expect("checked in branch arm"))
|
||||
.map(|ch| Type::string_literal(self.db, &ch.to_string()))
|
||||
.unwrap_or_else(|_| {
|
||||
self.diagnostics.add_index_out_of_bounds(
|
||||
report_index_out_of_bounds(
|
||||
self.db,
|
||||
self.file,
|
||||
"string",
|
||||
value_node.into(),
|
||||
value_ty,
|
||||
@@ -3957,7 +3999,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
let literal: String = new_chars.collect();
|
||||
Type::string_literal(self.db, &literal)
|
||||
} else {
|
||||
self.diagnostics.add_slice_step_size_zero(value_node.into());
|
||||
report_slice_step_size_zero(self.db, self.file, value_node.into());
|
||||
Type::Unknown
|
||||
};
|
||||
result
|
||||
@@ -3972,7 +4014,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
.py_index(i32::try_from(int).expect("checked in branch arm"))
|
||||
.map(|byte| Type::bytes_literal(self.db, &[*byte]))
|
||||
.unwrap_or_else(|_| {
|
||||
self.diagnostics.add_index_out_of_bounds(
|
||||
report_index_out_of_bounds(
|
||||
self.db,
|
||||
self.file,
|
||||
"bytes literal",
|
||||
value_node.into(),
|
||||
value_ty,
|
||||
@@ -3991,7 +4035,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
let new_bytes: Vec<u8> = new_bytes.copied().collect();
|
||||
Type::bytes_literal(self.db, &new_bytes)
|
||||
} else {
|
||||
self.diagnostics.add_slice_step_size_zero(value_node.into());
|
||||
report_slice_step_size_zero(self.db, self.file, value_node.into());
|
||||
Type::Unknown
|
||||
}
|
||||
}
|
||||
@@ -4015,7 +4059,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
Symbol::Unbound => {}
|
||||
Symbol::Type(dunder_getitem_method, boundness) => {
|
||||
if boundness == Boundness::PossiblyUnbound {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
value_node.into(),
|
||||
"call-possibly-unbound-method",
|
||||
format_args!(
|
||||
@@ -4027,9 +4073,11 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
|
||||
return dunder_getitem_method
|
||||
.call(self.db, &[slice_ty])
|
||||
.return_ty_result(self.db, value_node.into(), &mut self.diagnostics)
|
||||
.return_ty_result(self.db, self.file, value_node.into())
|
||||
.unwrap_or_else(|err| {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
value_node.into(),
|
||||
"call-non-callable",
|
||||
format_args!(
|
||||
@@ -4059,7 +4107,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
Symbol::Unbound => {}
|
||||
Symbol::Type(ty, boundness) => {
|
||||
if boundness == Boundness::PossiblyUnbound {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
value_node.into(),
|
||||
"call-possibly-unbound-method",
|
||||
format_args!(
|
||||
@@ -4071,9 +4121,11 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
|
||||
return ty
|
||||
.call(self.db, &[slice_ty])
|
||||
.return_ty_result(self.db, value_node.into(), &mut self.diagnostics)
|
||||
.return_ty_result(self.db, self.file, value_node.into())
|
||||
.unwrap_or_else(|err| {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
value_node.into(),
|
||||
"call-non-callable",
|
||||
format_args!(
|
||||
@@ -4092,13 +4144,17 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
return KnownClass::GenericAlias.to_instance(self.db);
|
||||
}
|
||||
|
||||
self.diagnostics.add_non_subscriptable(
|
||||
report_non_subscriptable(
|
||||
self.db,
|
||||
self.file,
|
||||
value_node.into(),
|
||||
value_ty,
|
||||
"__class_getitem__",
|
||||
);
|
||||
} else {
|
||||
self.diagnostics.add_non_subscriptable(
|
||||
report_non_subscriptable(
|
||||
self.db,
|
||||
self.file,
|
||||
value_node.into(),
|
||||
value_ty,
|
||||
"__getitem__",
|
||||
@@ -4170,7 +4226,6 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
|
||||
pub(super) fn finish(mut self) -> TypeInference<'db> {
|
||||
self.infer_region();
|
||||
self.types.diagnostics = self.diagnostics.finish();
|
||||
self.types.shrink_to_fit();
|
||||
self.types
|
||||
}
|
||||
@@ -4215,7 +4270,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
ast::Expr::Starred(starred) => self.infer_starred_expression(starred),
|
||||
|
||||
ast::Expr::BytesLiteral(bytes) => {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
bytes.into(),
|
||||
"annotation-byte-string",
|
||||
format_args!("Type expressions cannot use bytes literal"),
|
||||
@@ -4224,11 +4281,14 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
}
|
||||
|
||||
ast::Expr::FString(fstring) => {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
fstring.into(),
|
||||
"annotation-f-string",
|
||||
format_args!("Type expressions cannot use f-strings"),
|
||||
);
|
||||
self.infer_fstring_expression(fstring);
|
||||
Type::Unknown
|
||||
}
|
||||
|
||||
@@ -4252,10 +4312,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
DeferredExpressionState::InStringAnnotation,
|
||||
)
|
||||
}
|
||||
Err(diagnostics) => {
|
||||
self.diagnostics.extend(&diagnostics);
|
||||
Type::Unknown
|
||||
}
|
||||
Err(()) => Type::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4374,6 +4431,10 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
todo_type!()
|
||||
}
|
||||
|
||||
// Avoid inferring the types of invalid type expressions that have been parsed from a
|
||||
// string annotation, as they are not present in the semantic index.
|
||||
_ if self.deferred_state.in_string_annotation() => Type::Unknown,
|
||||
|
||||
// Forms which are invalid in the context of annotation expressions: we infer their
|
||||
// nested expressions as normal expressions, but the type of the top-level expression is
|
||||
// always `Type::Unknown` in these cases.
|
||||
@@ -4457,7 +4518,6 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
self.infer_slice_expression(slice);
|
||||
Type::Unknown
|
||||
}
|
||||
|
||||
ast::Expr::IpyEscapeCommand(_) => todo!("Implement Ipy escape command support"),
|
||||
}
|
||||
}
|
||||
@@ -4472,10 +4532,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
DeferredExpressionState::InStringAnnotation,
|
||||
)
|
||||
}
|
||||
Err(diagnostics) => {
|
||||
self.diagnostics.extend(&diagnostics);
|
||||
Type::Unknown
|
||||
}
|
||||
Err(()) => Type::Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4533,7 +4590,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
if element_could_alter_type_of_whole_tuple(single_element, single_element_ty) {
|
||||
todo_type!()
|
||||
} else {
|
||||
Type::tuple(self.db, &[single_element_ty])
|
||||
Type::tuple(self.db, [single_element_ty])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4572,7 +4629,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
|
||||
match value_ty {
|
||||
Type::KnownInstance(known_instance) => {
|
||||
self.infer_parameterized_known_instance_type_expression(known_instance, slice)
|
||||
self.infer_parameterized_known_instance_type_expression(subscript, known_instance)
|
||||
}
|
||||
_ => {
|
||||
self.infer_type_expression(slice);
|
||||
@@ -4583,15 +4640,18 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
|
||||
fn infer_parameterized_known_instance_type_expression(
|
||||
&mut self,
|
||||
subscript: &ast::ExprSubscript,
|
||||
known_instance: KnownInstanceType,
|
||||
parameters: &ast::Expr,
|
||||
) -> Type<'db> {
|
||||
let parameters = &*subscript.slice;
|
||||
match known_instance {
|
||||
KnownInstanceType::Literal => match self.infer_literal_parameter_type(parameters) {
|
||||
Ok(ty) => ty,
|
||||
Err(nodes) => {
|
||||
for node in nodes {
|
||||
self.diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
node.into(),
|
||||
"invalid-literal-parameter",
|
||||
format_args!(
|
||||
@@ -4626,6 +4686,32 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
self.infer_type_expression(parameters);
|
||||
todo_type!("generic type alias")
|
||||
}
|
||||
KnownInstanceType::NoReturn | KnownInstanceType::Never => {
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
subscript.into(),
|
||||
"invalid-type-parameter",
|
||||
format_args!(
|
||||
"Type `{}` expected no type parameter",
|
||||
known_instance.repr(self.db)
|
||||
),
|
||||
);
|
||||
Type::Unknown
|
||||
}
|
||||
KnownInstanceType::LiteralString => {
|
||||
report_type_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
subscript.into(),
|
||||
"invalid-type-parameter",
|
||||
format_args!(
|
||||
"Type `{}` expected no type parameter. Did you mean to use `Literal[...]` instead?",
|
||||
known_instance.repr(self.db)
|
||||
),
|
||||
);
|
||||
Type::Unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5011,8 +5097,6 @@ fn perform_membership_test_comparison<'db>(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use anyhow::Context;
|
||||
|
||||
use crate::db::tests::TestDb;
|
||||
use crate::program::{Program, SearchPathSettings};
|
||||
use crate::python_version::PythonVersion;
|
||||
@@ -5021,6 +5105,8 @@ mod tests {
|
||||
use crate::semantic_index::{global_scope, semantic_index, symbol_table, use_def_map};
|
||||
use crate::types::check_types;
|
||||
use crate::{HasTy, ProgramSettings, SemanticModel};
|
||||
use anyhow::Context;
|
||||
use ruff_db::diagnostic::{CompileDiagnostic, Diagnostic};
|
||||
use ruff_db::files::{system_path_to_file, File};
|
||||
use ruff_db::parsed::parsed_module;
|
||||
use ruff_db::system::{DbWithTestSystem, SystemPathBuf};
|
||||
@@ -5122,22 +5208,18 @@ mod tests {
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn assert_diagnostic_messages(diagnostics: &TypeCheckDiagnostics, expected: &[&str]) {
|
||||
let messages: Vec<&str> = diagnostics
|
||||
fn assert_file_diagnostics(db: &TestDb, filename: &str, expected: &[&str]) {
|
||||
let file = system_path_to_file(db, filename).unwrap();
|
||||
|
||||
let diagnostics = check_types::accumulated::<CompileDiagnostic>(db, file);
|
||||
|
||||
let messages: Vec<_> = diagnostics
|
||||
.iter()
|
||||
.map(|diagnostic| diagnostic.message())
|
||||
.collect();
|
||||
assert_eq!(&messages, expected);
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn assert_file_diagnostics(db: &TestDb, filename: &str, expected: &[&str]) {
|
||||
let file = system_path_to_file(db, filename).unwrap();
|
||||
let diagnostics = check_types(db, file);
|
||||
|
||||
assert_diagnostic_messages(diagnostics, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_import_with_no_module_name() -> anyhow::Result<()> {
|
||||
// This test checks that invalid syntax in a `StmtImportFrom` node
|
||||
@@ -5922,7 +6004,15 @@ mod tests {
|
||||
assert!(z.is_unbound());
|
||||
|
||||
// (There is a diagnostic for invalid syntax that's emitted, but it's not listed by `assert_file_diagnostics`)
|
||||
assert_file_diagnostics(&db, "src/a.py", &["Name `z` used when not defined"]);
|
||||
assert_file_diagnostics(
|
||||
&db,
|
||||
"src/a.py",
|
||||
&[
|
||||
"Expected an identifier, but found a keyword 'in' that cannot be used here",
|
||||
"Expected 'in', found name",
|
||||
"Name `z` used when not defined",
|
||||
],
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -5970,7 +6060,11 @@ mod tests {
|
||||
"src/a.py",
|
||||
&["foo", "<listcomp>"],
|
||||
"x",
|
||||
"@Todo(async iterables/iterators)",
|
||||
if cfg!(debug_assertions) {
|
||||
"@Todo(async iterables/iterators)"
|
||||
} else {
|
||||
"@Todo"
|
||||
},
|
||||
);
|
||||
|
||||
Ok(())
|
||||
@@ -6000,7 +6094,11 @@ mod tests {
|
||||
"src/a.py",
|
||||
&["foo", "<listcomp>"],
|
||||
"x",
|
||||
"@Todo(async iterables/iterators)",
|
||||
if cfg!(debug_assertions) {
|
||||
"@Todo(async iterables/iterators)"
|
||||
} else {
|
||||
"@Todo"
|
||||
},
|
||||
);
|
||||
|
||||
Ok(())
|
||||
@@ -6035,6 +6133,72 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pep695_type_params() {
|
||||
let mut db = setup_db();
|
||||
|
||||
db.write_dedented(
|
||||
"src/a.py",
|
||||
"
|
||||
def f[T, U: A, V: (A, B), W = A, X: A = A1, Y: (int,)]():
|
||||
pass
|
||||
|
||||
class A: ...
|
||||
class B: ...
|
||||
class A1(A): ...
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let check_typevar = |var: &'static str,
|
||||
upper_bound: Option<&'static str>,
|
||||
constraints: Option<&[&'static str]>,
|
||||
default: Option<&'static str>| {
|
||||
let var_ty = get_symbol(&db, "src/a.py", &["f"], var).expect_type();
|
||||
assert_eq!(var_ty.display(&db).to_string(), var);
|
||||
|
||||
let expected_name_ty = format!(r#"Literal["{var}"]"#);
|
||||
let name_ty = var_ty.member(&db, "__name__").expect_type();
|
||||
assert_eq!(name_ty.display(&db).to_string(), expected_name_ty);
|
||||
|
||||
let KnownInstanceType::TypeVar(typevar) = var_ty.expect_known_instance() else {
|
||||
panic!("expected TypeVar");
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
typevar
|
||||
.upper_bound(&db)
|
||||
.map(|ty| ty.display(&db).to_string()),
|
||||
upper_bound.map(std::borrow::ToOwned::to_owned)
|
||||
);
|
||||
assert_eq!(
|
||||
typevar.constraints(&db).map(|tys| tys
|
||||
.iter()
|
||||
.map(|ty| ty.display(&db).to_string())
|
||||
.collect::<Vec<_>>()),
|
||||
constraints.map(|strings| strings
|
||||
.iter()
|
||||
.map(std::string::ToString::to_string)
|
||||
.collect::<Vec<_>>())
|
||||
);
|
||||
assert_eq!(
|
||||
typevar
|
||||
.default_ty(&db)
|
||||
.map(|ty| ty.display(&db).to_string()),
|
||||
default.map(std::borrow::ToOwned::to_owned)
|
||||
);
|
||||
};
|
||||
|
||||
check_typevar("T", None, None, None);
|
||||
check_typevar("U", Some("A"), None, None);
|
||||
check_typevar("V", None, Some(&["A", "B"]), None);
|
||||
check_typevar("W", None, None, Some("A"));
|
||||
check_typevar("X", Some("A"), None, Some("A1"));
|
||||
|
||||
// a typevar with less than two constraints is treated as unconstrained
|
||||
check_typevar("Y", None, None, None);
|
||||
}
|
||||
|
||||
// Incremental inference tests
|
||||
|
||||
fn first_public_binding<'db>(db: &'db TestDb, file: File, name: &str) -> Definition<'db> {
|
||||
|
||||
@@ -374,7 +374,10 @@ impl<'db> ClassBase<'db> {
|
||||
KnownInstanceType::TypeVar(_)
|
||||
| KnownInstanceType::TypeAliasType(_)
|
||||
| KnownInstanceType::Literal
|
||||
| KnownInstanceType::LiteralString
|
||||
| KnownInstanceType::Union
|
||||
| KnownInstanceType::NoReturn
|
||||
| KnownInstanceType::Never
|
||||
| KnownInstanceType::Optional => None,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ pub(crate) fn narrowing_constraint<'db>(
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::ref_option)]
|
||||
#[salsa::tracked(return_ref)]
|
||||
fn all_narrowing_constraints_for_pattern<'db>(
|
||||
db: &'db dyn Db,
|
||||
@@ -62,6 +63,7 @@ fn all_narrowing_constraints_for_pattern<'db>(
|
||||
NarrowingConstraintsBuilder::new(db, ConstraintNode::Pattern(pattern), true).finish()
|
||||
}
|
||||
|
||||
#[allow(clippy::ref_option)]
|
||||
#[salsa::tracked(return_ref)]
|
||||
fn all_narrowing_constraints_for_expression<'db>(
|
||||
db: &'db dyn Db,
|
||||
@@ -70,6 +72,7 @@ fn all_narrowing_constraints_for_expression<'db>(
|
||||
NarrowingConstraintsBuilder::new(db, ConstraintNode::Expression(expression), true).finish()
|
||||
}
|
||||
|
||||
#[allow(clippy::ref_option)]
|
||||
#[salsa::tracked(return_ref)]
|
||||
fn all_negative_narrowing_constraints_for_expression<'db>(
|
||||
db: &'db dyn Db,
|
||||
@@ -291,8 +294,15 @@ impl<'db> NarrowingConstraintsBuilder<'db> {
|
||||
.chain(comparators)
|
||||
.tuple_windows::<(&ruff_python_ast::Expr, &ruff_python_ast::Expr)>();
|
||||
let mut constraints = NarrowingConstraints::default();
|
||||
|
||||
let mut last_rhs_ty: Option<Type> = None;
|
||||
|
||||
for (op, (left, right)) in std::iter::zip(&**ops, comparator_tuples) {
|
||||
let lhs_ty = last_rhs_ty.unwrap_or_else(|| {
|
||||
inference.expression_ty(left.scoped_expression_id(self.db, scope))
|
||||
});
|
||||
let rhs_ty = inference.expression_ty(right.scoped_expression_id(self.db, scope));
|
||||
last_rhs_ty = Some(rhs_ty);
|
||||
|
||||
match left {
|
||||
ast::Expr::Name(ast::ExprName {
|
||||
@@ -327,6 +337,9 @@ impl<'db> NarrowingConstraintsBuilder<'db> {
|
||||
constraints.insert(symbol, ty);
|
||||
}
|
||||
}
|
||||
ast::CmpOp::Eq if lhs_ty.is_literal_string() => {
|
||||
constraints.insert(symbol, rhs_ty);
|
||||
}
|
||||
_ => {
|
||||
// TODO other comparison types
|
||||
}
|
||||
@@ -385,46 +398,58 @@ impl<'db> NarrowingConstraintsBuilder<'db> {
|
||||
let scope = self.scope();
|
||||
let inference = infer_expression_types(self.db, expression);
|
||||
|
||||
let callable_ty =
|
||||
inference.expression_ty(expr_call.func.scoped_expression_id(self.db, scope));
|
||||
|
||||
// TODO: add support for PEP 604 union types on the right hand side of `isinstance`
|
||||
// and `issubclass`, for example `isinstance(x, str | (int | float))`.
|
||||
match inference
|
||||
.expression_ty(expr_call.func.scoped_expression_id(self.db, scope))
|
||||
.into_function_literal()
|
||||
.and_then(|f| f.known(self.db))
|
||||
.and_then(KnownFunction::constraint_function)
|
||||
{
|
||||
Some(function) if expr_call.arguments.keywords.is_empty() => {
|
||||
if let [ast::Expr::Name(ast::ExprName { id, .. }), class_info] =
|
||||
match callable_ty {
|
||||
Type::FunctionLiteral(function_type) if expr_call.arguments.keywords.is_empty() => {
|
||||
let function = function_type
|
||||
.known(self.db)
|
||||
.and_then(KnownFunction::constraint_function)?;
|
||||
|
||||
let [ast::Expr::Name(ast::ExprName { id, .. }), class_info] =
|
||||
&*expr_call.arguments.args
|
||||
{
|
||||
let symbol = self.symbols().symbol_id_by_name(id).unwrap();
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let class_info_ty =
|
||||
inference.expression_ty(class_info.scoped_expression_id(self.db, scope));
|
||||
let symbol = self.symbols().symbol_id_by_name(id).unwrap();
|
||||
|
||||
let to_constraint = match function {
|
||||
KnownConstraintFunction::IsInstance => {
|
||||
|class_literal: ClassLiteralType<'db>| {
|
||||
Type::instance(class_literal.class)
|
||||
}
|
||||
let class_info_ty =
|
||||
inference.expression_ty(class_info.scoped_expression_id(self.db, scope));
|
||||
|
||||
let to_constraint = match function {
|
||||
KnownConstraintFunction::IsInstance => {
|
||||
|class_literal: ClassLiteralType<'db>| Type::instance(class_literal.class)
|
||||
}
|
||||
KnownConstraintFunction::IsSubclass => {
|
||||
|class_literal: ClassLiteralType<'db>| {
|
||||
Type::subclass_of(class_literal.class)
|
||||
}
|
||||
KnownConstraintFunction::IsSubclass => {
|
||||
|class_literal: ClassLiteralType<'db>| {
|
||||
Type::subclass_of(class_literal.class)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
generate_classinfo_constraint(self.db, &class_info_ty, to_constraint).map(
|
||||
|constraint| {
|
||||
let mut constraints = NarrowingConstraints::default();
|
||||
constraints.insert(symbol, constraint.negate_if(self.db, !is_positive));
|
||||
constraints
|
||||
},
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
generate_classinfo_constraint(self.db, &class_info_ty, to_constraint).map(
|
||||
|constraint| {
|
||||
let mut constraints = NarrowingConstraints::default();
|
||||
constraints.insert(symbol, constraint.negate_if(self.db, !is_positive));
|
||||
constraints
|
||||
},
|
||||
)
|
||||
}
|
||||
// for the expression `bool(E)`, we further narrow the type based on `E`
|
||||
Type::ClassLiteral(class_type)
|
||||
if expr_call.arguments.args.len() == 1
|
||||
&& expr_call.arguments.keywords.is_empty()
|
||||
&& class_type.class.is_known(self.db, KnownClass::Bool) =>
|
||||
{
|
||||
self.evaluate_expression_node_constraint(
|
||||
&expr_call.arguments.args[0],
|
||||
expression,
|
||||
is_positive,
|
||||
)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
|
||||
237
crates/red_knot_python_semantic/src/types/property_tests.rs
Normal file
237
crates/red_knot_python_semantic/src/types/property_tests.rs
Normal file
@@ -0,0 +1,237 @@
|
||||
//! This module contains quickcheck-based property tests for `Type`s.
|
||||
//!
|
||||
//! These tests are disabled by default, as they are non-deterministic and slow. You can
|
||||
//! run them explicitly using:
|
||||
//!
|
||||
//! ```sh
|
||||
//! cargo test -p red_knot_python_semantic -- --ignored types::property_tests::stable
|
||||
//! ```
|
||||
//!
|
||||
//! The number of tests (default: 100) can be controlled by setting the `QUICKCHECK_TESTS`
|
||||
//! environment variable. For example:
|
||||
//!
|
||||
//! ```sh
|
||||
//! QUICKCHECK_TESTS=10000 cargo test …
|
||||
//! ```
|
||||
//!
|
||||
//! If you want to run these tests for a longer period of time, it's advisable to run them
|
||||
//! in release mode. As some tests are slower than others, it's advisable to run them in a
|
||||
//! loop until they fail:
|
||||
//!
|
||||
//! ```sh
|
||||
//! export QUICKCHECK_TESTS=100000
|
||||
//! while cargo test --release -p red_knot_python_semantic -- \
|
||||
//! --ignored types::property_tests::stable; do :; done
|
||||
//! ```
|
||||
|
||||
use std::sync::{Arc, Mutex, MutexGuard, OnceLock};
|
||||
|
||||
use super::tests::{setup_db, Ty};
|
||||
use crate::db::tests::TestDb;
|
||||
use crate::types::KnownClass;
|
||||
use quickcheck::{Arbitrary, Gen};
|
||||
|
||||
fn arbitrary_core_type(g: &mut Gen) -> Ty {
|
||||
// We could select a random integer here, but this would make it much less
|
||||
// likely to explore interesting edge cases:
|
||||
let int_lit = Ty::IntLiteral(*g.choose(&[-2, -1, 0, 1, 2]).unwrap());
|
||||
let bool_lit = Ty::BooleanLiteral(bool::arbitrary(g));
|
||||
g.choose(&[
|
||||
Ty::Never,
|
||||
Ty::Unknown,
|
||||
Ty::None,
|
||||
Ty::Any,
|
||||
int_lit,
|
||||
bool_lit,
|
||||
Ty::StringLiteral(""),
|
||||
Ty::StringLiteral("a"),
|
||||
Ty::LiteralString,
|
||||
Ty::BytesLiteral(""),
|
||||
Ty::BytesLiteral("\x00"),
|
||||
Ty::KnownClassInstance(KnownClass::Object),
|
||||
Ty::KnownClassInstance(KnownClass::Str),
|
||||
Ty::KnownClassInstance(KnownClass::Int),
|
||||
Ty::KnownClassInstance(KnownClass::Bool),
|
||||
Ty::KnownClassInstance(KnownClass::List),
|
||||
Ty::KnownClassInstance(KnownClass::Tuple),
|
||||
Ty::KnownClassInstance(KnownClass::FunctionType),
|
||||
Ty::KnownClassInstance(KnownClass::SpecialForm),
|
||||
Ty::KnownClassInstance(KnownClass::TypeVar),
|
||||
Ty::KnownClassInstance(KnownClass::TypeAliasType),
|
||||
Ty::KnownClassInstance(KnownClass::NoDefaultType),
|
||||
Ty::TypingLiteral,
|
||||
Ty::BuiltinClassLiteral("str"),
|
||||
Ty::BuiltinClassLiteral("int"),
|
||||
Ty::BuiltinClassLiteral("bool"),
|
||||
Ty::BuiltinClassLiteral("object"),
|
||||
])
|
||||
.unwrap()
|
||||
.clone()
|
||||
}
|
||||
|
||||
/// Constructs an arbitrary type.
|
||||
///
|
||||
/// The `size` parameter controls the depth of the type tree. For example,
|
||||
/// a simple type like `int` has a size of 0, `Union[int, str]` has a size
|
||||
/// of 1, `tuple[int, Union[str, bytes]]` has a size of 2, etc.
|
||||
fn arbitrary_type(g: &mut Gen, size: u32) -> Ty {
|
||||
if size == 0 {
|
||||
arbitrary_core_type(g)
|
||||
} else {
|
||||
match u32::arbitrary(g) % 4 {
|
||||
0 => arbitrary_core_type(g),
|
||||
1 => Ty::Union(
|
||||
(0..*g.choose(&[2, 3]).unwrap())
|
||||
.map(|_| arbitrary_type(g, size - 1))
|
||||
.collect(),
|
||||
),
|
||||
2 => Ty::Tuple(
|
||||
(0..*g.choose(&[0, 1, 2]).unwrap())
|
||||
.map(|_| arbitrary_type(g, size - 1))
|
||||
.collect(),
|
||||
),
|
||||
3 => Ty::Intersection {
|
||||
pos: (0..*g.choose(&[0, 1, 2]).unwrap())
|
||||
.map(|_| arbitrary_type(g, size - 1))
|
||||
.collect(),
|
||||
neg: (0..*g.choose(&[0, 1, 2]).unwrap())
|
||||
.map(|_| arbitrary_type(g, size - 1))
|
||||
.collect(),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Ty {
|
||||
fn arbitrary(g: &mut Gen) -> Ty {
|
||||
const MAX_SIZE: u32 = 2;
|
||||
arbitrary_type(g, MAX_SIZE)
|
||||
}
|
||||
|
||||
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
|
||||
// This is incredibly naive. We can do much better here by
|
||||
// trying various subsets of the elements in unions, tuples,
|
||||
// and intersections. For now, we only try to shrink by
|
||||
// reducing unions/tuples/intersections to a single element.
|
||||
match self.clone() {
|
||||
Ty::Union(types) => Box::new(types.into_iter()),
|
||||
Ty::Tuple(types) => Box::new(types.into_iter()),
|
||||
Ty::Intersection { pos, neg } => Box::new(pos.into_iter().chain(neg)),
|
||||
_ => Box::new(std::iter::empty()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static CACHED_DB: OnceLock<Arc<Mutex<TestDb>>> = OnceLock::new();
|
||||
|
||||
fn get_cached_db() -> MutexGuard<'static, TestDb> {
|
||||
let db = CACHED_DB.get_or_init(|| Arc::new(Mutex::new(setup_db())));
|
||||
db.lock().unwrap()
|
||||
}
|
||||
|
||||
/// A macro to define a property test for types.
|
||||
///
|
||||
/// The `$test_name` identifier specifies the name of the test function. The `$db` identifier
|
||||
/// is used to refer to the salsa database in the property to be tested. The actual property is
|
||||
/// specified using the syntax:
|
||||
///
|
||||
/// forall types t1, t2, ..., tn . <property>`
|
||||
///
|
||||
/// where `t1`, `t2`, ..., `tn` are identifiers that represent arbitrary types, and `<property>`
|
||||
/// is an expression using these identifiers.
|
||||
///
|
||||
macro_rules! type_property_test {
|
||||
($test_name:ident, $db:ident, forall types $($types:ident),+ . $property:expr) => {
|
||||
#[quickcheck_macros::quickcheck]
|
||||
#[ignore]
|
||||
fn $test_name($($types: crate::types::tests::Ty),+) -> bool {
|
||||
let db_cached = super::get_cached_db();
|
||||
let $db = &*db_cached;
|
||||
$(let $types = $types.into_type($db);)+
|
||||
|
||||
$property
|
||||
}
|
||||
};
|
||||
// A property test with a logical implication.
|
||||
($name:ident, $db:ident, forall types $($types:ident),+ . $premise:expr => $conclusion:expr) => {
|
||||
type_property_test!($name, $db, forall types $($types),+ . !($premise) || ($conclusion));
|
||||
};
|
||||
}
|
||||
|
||||
mod stable {
|
||||
// `T` is equivalent to itself.
|
||||
type_property_test!(
|
||||
equivalent_to_is_reflexive, db,
|
||||
forall types t. t.is_equivalent_to(db, t)
|
||||
);
|
||||
|
||||
// `T` is a subtype of itself.
|
||||
type_property_test!(
|
||||
subtype_of_is_reflexive, db,
|
||||
forall types t. t.is_subtype_of(db, t)
|
||||
);
|
||||
|
||||
// `S <: T` and `T <: U` implies that `S <: U`.
|
||||
type_property_test!(
|
||||
subtype_of_is_transitive, db,
|
||||
forall types s, t, u. s.is_subtype_of(db, t) && t.is_subtype_of(db, u) => s.is_subtype_of(db, u)
|
||||
);
|
||||
|
||||
// `T` is not disjoint from itself, unless `T` is `Never`.
|
||||
type_property_test!(
|
||||
disjoint_from_is_irreflexive, db,
|
||||
forall types t. t.is_disjoint_from(db, t) => t.is_never()
|
||||
);
|
||||
|
||||
// `S` is disjoint from `T` implies that `T` is disjoint from `S`.
|
||||
type_property_test!(
|
||||
disjoint_from_is_symmetric, db,
|
||||
forall types s, t. s.is_disjoint_from(db, t) == t.is_disjoint_from(db, s)
|
||||
);
|
||||
|
||||
// `S <: T` implies that `S` is not disjoint from `T`, unless `S` is `Never`.
|
||||
type_property_test!(
|
||||
subtype_of_implies_not_disjoint_from, db,
|
||||
forall types s, t. s.is_subtype_of(db, t) => !s.is_disjoint_from(db, t) || s.is_never()
|
||||
);
|
||||
|
||||
// `T` can be assigned to itself.
|
||||
type_property_test!(
|
||||
assignable_to_is_reflexive, db,
|
||||
forall types t. t.is_assignable_to(db, t)
|
||||
);
|
||||
|
||||
// `S <: T` implies that `S` can be assigned to `T`.
|
||||
type_property_test!(
|
||||
subtype_of_implies_assignable_to, db,
|
||||
forall types s, t. s.is_subtype_of(db, t) => s.is_assignable_to(db, t)
|
||||
);
|
||||
|
||||
// If `T` is a singleton, it is also single-valued.
|
||||
type_property_test!(
|
||||
singleton_implies_single_valued, db,
|
||||
forall types t. t.is_singleton(db) => t.is_single_valued(db)
|
||||
);
|
||||
}
|
||||
|
||||
/// This module contains property tests that currently lead to many false positives.
|
||||
///
|
||||
/// The reason for this is our insufficient understanding of equivalence of types. For
|
||||
/// example, we currently consider `int | str` and `str | int` to be different types.
|
||||
/// Similar issues exist for intersection types. Once this is resolved, we can move these
|
||||
/// tests to the `stable` section. In the meantime, it can still be useful to run these
|
||||
/// tests (using [`types::property_tests::flaky`]), to see if there are any new obvious bugs.
|
||||
mod flaky {
|
||||
// `S <: T` and `T <: S` implies that `S` is equivalent to `T`.
|
||||
type_property_test!(
|
||||
subtype_of_is_antisymmetric, db,
|
||||
forall types s, t. s.is_subtype_of(db, t) && t.is_subtype_of(db, s) => s.is_equivalent_to(db, t)
|
||||
);
|
||||
|
||||
// Negating `T` twice is equivalent to `T`.
|
||||
type_property_test!(
|
||||
double_negation_is_identity, db,
|
||||
forall types t. t.negate(db).negate(db).is_equivalent_to(db, t)
|
||||
);
|
||||
}
|
||||
@@ -5,10 +5,10 @@ use ruff_python_ast::{self as ast, ModExpression, StringFlags};
|
||||
use ruff_python_parser::{parse_expression_range, Parsed};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::types::diagnostic::{TypeCheckDiagnostics, TypeCheckDiagnosticsBuilder};
|
||||
use crate::types::diagnostic::report_type_diagnostic;
|
||||
use crate::Db;
|
||||
|
||||
type AnnotationParseResult = Result<Parsed<ModExpression>, TypeCheckDiagnostics>;
|
||||
type AnnotationParseResult = Result<Parsed<ModExpression>, ()>;
|
||||
|
||||
/// Parses the given expression as a string annotation.
|
||||
pub(crate) fn parse_string_annotation(
|
||||
@@ -20,12 +20,13 @@ pub(crate) fn parse_string_annotation(
|
||||
|
||||
let source = source_text(db.upcast(), file);
|
||||
let node_text = &source[string_expr.range()];
|
||||
let mut diagnostics = TypeCheckDiagnosticsBuilder::new(db, file);
|
||||
|
||||
if let [string_literal] = string_expr.value.as_slice() {
|
||||
let prefix = string_literal.flags.prefix();
|
||||
if prefix.is_raw() {
|
||||
diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
db,
|
||||
file,
|
||||
string_literal.into(),
|
||||
"annotation-raw-string",
|
||||
format_args!("Type expressions cannot use raw string literal"),
|
||||
@@ -49,7 +50,9 @@ pub(crate) fn parse_string_annotation(
|
||||
// ```
|
||||
match parse_expression_range(source.as_str(), range_excluding_quotes) {
|
||||
Ok(parsed) => return Ok(parsed),
|
||||
Err(parse_error) => diagnostics.add(
|
||||
Err(parse_error) => report_type_diagnostic(
|
||||
db,
|
||||
file,
|
||||
string_literal.into(),
|
||||
"forward-annotation-syntax-error",
|
||||
format_args!("Syntax error in forward annotation: {}", parse_error.error),
|
||||
@@ -58,7 +61,9 @@ pub(crate) fn parse_string_annotation(
|
||||
} else {
|
||||
// The raw contents of the string doesn't match the parsed content. This could be the
|
||||
// case for annotations that contain escape sequences.
|
||||
diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
db,
|
||||
file,
|
||||
string_expr.into(),
|
||||
"annotation-escape-character",
|
||||
format_args!("Type expressions cannot contain escape characters"),
|
||||
@@ -66,12 +71,14 @@ pub(crate) fn parse_string_annotation(
|
||||
}
|
||||
} else {
|
||||
// String is implicitly concatenated.
|
||||
diagnostics.add(
|
||||
report_type_diagnostic(
|
||||
db,
|
||||
file,
|
||||
string_expr.into(),
|
||||
"annotation-implicit-concat",
|
||||
format_args!("Type expressions cannot span multiple string literals"),
|
||||
);
|
||||
}
|
||||
|
||||
Err(diagnostics.finish())
|
||||
Err(())
|
||||
}
|
||||
|
||||
@@ -6,22 +6,22 @@ use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::semantic_index::ast_ids::{HasScopedExpressionId, ScopedExpressionId};
|
||||
use crate::semantic_index::symbol::ScopeId;
|
||||
use crate::types::{todo_type, Type, TypeCheckDiagnostics, TypeCheckDiagnosticsBuilder};
|
||||
use crate::types::{todo_type, Type};
|
||||
use crate::Db;
|
||||
|
||||
/// Unpacks the value expression type to their respective targets.
|
||||
pub(crate) struct Unpacker<'db> {
|
||||
db: &'db dyn Db,
|
||||
file: File,
|
||||
targets: FxHashMap<ScopedExpressionId, Type<'db>>,
|
||||
diagnostics: TypeCheckDiagnosticsBuilder<'db>,
|
||||
}
|
||||
|
||||
impl<'db> Unpacker<'db> {
|
||||
pub(crate) fn new(db: &'db dyn Db, file: File) -> Self {
|
||||
Self {
|
||||
db,
|
||||
file,
|
||||
targets: FxHashMap::default(),
|
||||
diagnostics: TypeCheckDiagnosticsBuilder::new(db, file),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ impl<'db> Unpacker<'db> {
|
||||
// there would be a cost and it's not clear that it's worth it.
|
||||
let value_ty = Type::tuple(
|
||||
self.db,
|
||||
&vec![Type::LiteralString; string_literal_ty.len(self.db)],
|
||||
std::iter::repeat(Type::LiteralString).take(string_literal_ty.len(self.db)),
|
||||
);
|
||||
self.unpack(target, value_ty, scope);
|
||||
}
|
||||
@@ -103,9 +103,11 @@ impl<'db> Unpacker<'db> {
|
||||
let value_ty = if value_ty.is_literal_string() {
|
||||
Type::LiteralString
|
||||
} else {
|
||||
value_ty
|
||||
.iterate(self.db)
|
||||
.unwrap_with_diagnostic(AnyNodeRef::from(target), &mut self.diagnostics)
|
||||
value_ty.iterate(self.db).unwrap_with_diagnostic(
|
||||
self.db,
|
||||
self.file,
|
||||
AnyNodeRef::from(target),
|
||||
)
|
||||
};
|
||||
for element in elts {
|
||||
self.unpack(element, value_ty, scope);
|
||||
@@ -119,7 +121,6 @@ impl<'db> Unpacker<'db> {
|
||||
pub(crate) fn finish(mut self) -> UnpackResult<'db> {
|
||||
self.targets.shrink_to_fit();
|
||||
UnpackResult {
|
||||
diagnostics: self.diagnostics.finish(),
|
||||
targets: self.targets,
|
||||
}
|
||||
}
|
||||
@@ -128,15 +129,10 @@ impl<'db> Unpacker<'db> {
|
||||
#[derive(Debug, Default, PartialEq, Eq)]
|
||||
pub(crate) struct UnpackResult<'db> {
|
||||
targets: FxHashMap<ScopedExpressionId, Type<'db>>,
|
||||
diagnostics: TypeCheckDiagnostics,
|
||||
}
|
||||
|
||||
impl<'db> UnpackResult<'db> {
|
||||
pub(crate) fn get(&self, expr_id: ScopedExpressionId) -> Option<Type<'db>> {
|
||||
self.targets.get(&expr_id).copied()
|
||||
}
|
||||
|
||||
pub(crate) fn diagnostics(&self) -> &TypeCheckDiagnostics {
|
||||
&self.diagnostics
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::ffi::OsStr;
|
||||
use std::path::Path;
|
||||
|
||||
use camino::Utf8Path;
|
||||
use dir_test::{dir_test, Fixture};
|
||||
use std::path::Path;
|
||||
|
||||
/// See `crates/red_knot_test/README.md` for documentation on these tests.
|
||||
#[dir_test(
|
||||
@@ -10,16 +9,46 @@ use dir_test::{dir_test, Fixture};
|
||||
)]
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
fn mdtest(fixture: Fixture<&str>) {
|
||||
let fixture_path = Path::new(fixture.path());
|
||||
let fixture_path = Utf8Path::new(fixture.path());
|
||||
let crate_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
|
||||
let workspace_root = crate_dir.parent().and_then(Path::parent).unwrap();
|
||||
let workspace_root = crate_dir.ancestors().nth(2).unwrap();
|
||||
|
||||
let long_title = fixture_path
|
||||
.strip_prefix(workspace_root)
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap();
|
||||
let short_title = fixture_path.file_name().and_then(OsStr::to_str).unwrap();
|
||||
let long_title = fixture_path.strip_prefix(workspace_root).unwrap();
|
||||
let short_title = fixture_path.file_name().unwrap();
|
||||
|
||||
red_knot_test::run(fixture_path, long_title, short_title);
|
||||
let test_name = test_name("mdtest", fixture_path);
|
||||
|
||||
red_knot_test::run(fixture_path, long_title.as_str(), short_title, &test_name);
|
||||
}
|
||||
|
||||
/// Constructs the test name used for individual markdown files
|
||||
///
|
||||
/// This code is copied from <https://github.com/fe-lang/dir-test/blob/1c0f41c480a3490bc2653a043ff6e3f8085a1f47/macros/src/lib.rs#L104-L138>
|
||||
/// and should be updated if they diverge
|
||||
fn test_name(test_func_name: &str, fixture_path: &Utf8Path) -> String {
|
||||
assert!(fixture_path.is_file());
|
||||
|
||||
let dir_path = format!("{}/resources/mdtest", std::env!("CARGO_MANIFEST_DIR"));
|
||||
let rel_path = fixture_path.strip_prefix(dir_path).unwrap();
|
||||
assert!(rel_path.is_relative());
|
||||
|
||||
let mut test_name = test_func_name.to_owned();
|
||||
test_name.push_str("__");
|
||||
|
||||
for component in rel_path.parent().unwrap().components() {
|
||||
let component = component
|
||||
.as_str()
|
||||
.replace(|c: char| c.is_ascii_punctuation(), "_");
|
||||
test_name.push_str(&component);
|
||||
test_name.push('_');
|
||||
}
|
||||
|
||||
test_name.push_str(
|
||||
&rel_path
|
||||
.file_stem()
|
||||
.unwrap()
|
||||
.replace(|c: char| c.is_ascii_punctuation(), "_"),
|
||||
);
|
||||
|
||||
test_name
|
||||
}
|
||||
|
||||
@@ -91,11 +91,11 @@ fn background_request_task<'a, R: traits::BackgroundDocumentRequestHandler>(
|
||||
let db = match path {
|
||||
AnySystemPath::System(path) => {
|
||||
match session.workspace_db_for_path(path.as_std_path()) {
|
||||
Some(db) => db.snapshot(),
|
||||
None => session.default_workspace_db().snapshot(),
|
||||
Some(db) => db.clone(),
|
||||
None => session.default_workspace_db().clone(),
|
||||
}
|
||||
}
|
||||
AnySystemPath::SystemVirtual(_) => session.default_workspace_db().snapshot(),
|
||||
AnySystemPath::SystemVirtual(_) => session.default_workspace_db().clone(),
|
||||
};
|
||||
|
||||
let Some(snapshot) = session.take_snapshot(url) else {
|
||||
|
||||
@@ -26,7 +26,7 @@ pub(crate) struct Requester<'s> {
|
||||
response_handlers: FxHashMap<lsp_server::RequestId, ResponseBuilder<'s>>,
|
||||
}
|
||||
|
||||
impl<'s> Client<'s> {
|
||||
impl Client<'_> {
|
||||
pub(super) fn new(sender: ClientSender) -> Self {
|
||||
Self {
|
||||
notifier: Notifier(sender.clone()),
|
||||
|
||||
@@ -20,6 +20,7 @@ ruff_source_file = { workspace = true }
|
||||
ruff_text_size = { workspace = true }
|
||||
|
||||
anyhow = { workspace = true }
|
||||
camino = { workspace = true }
|
||||
colored = { workspace = true }
|
||||
memchr = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
|
||||
@@ -184,8 +184,11 @@ The tests are run independently, in independent in-memory file systems and with
|
||||
[Salsa](https://github.com/salsa-rs/salsa) databases. This means that each is a from-scratch run of
|
||||
the type checker, with no data persisting from any previous test.
|
||||
|
||||
Due to `cargo test` limitations, an entire test suite (Markdown file) is run as a single Rust test,
|
||||
so it's not possible to select individual tests within it to run.
|
||||
It is possible to filter to individual tests within a single markdown file using the
|
||||
`MDTEST_TEST_FILTER` environment variable. This variable will match any tests which contain the
|
||||
value as a case-sensitive substring in its name. An example test name is
|
||||
`unpacking.md - Unpacking - Tuple - Multiple assignment`, which contains the name of the markdown
|
||||
file and its parent headers joined together with hyphens.
|
||||
|
||||
## Structured test suites
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ use ruff_db::vendored::VendoredFileSystem;
|
||||
use ruff_db::{Db as SourceDb, Upcast};
|
||||
|
||||
#[salsa::db]
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct Db {
|
||||
workspace_root: SystemPathBuf,
|
||||
storage: salsa::Storage<Self>,
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
use camino::Utf8Path;
|
||||
use colored::Colorize;
|
||||
use parser as test_parser;
|
||||
use red_knot_python_semantic::types::check_types;
|
||||
use ruff_db::diagnostic::{Diagnostic, ParseDiagnostic};
|
||||
use ruff_db::diagnostic::{CompileDiagnostic, Diagnostic};
|
||||
use ruff_db::files::{system_path_to_file, File, Files};
|
||||
use ruff_db::parsed::parsed_module;
|
||||
use ruff_db::system::{DbWithTestSystem, SystemPathBuf};
|
||||
use ruff_source_file::LineIndex;
|
||||
use ruff_text_size::TextSize;
|
||||
use std::path::Path;
|
||||
|
||||
mod assertion;
|
||||
mod db;
|
||||
@@ -15,23 +14,30 @@ mod diagnostic;
|
||||
mod matcher;
|
||||
mod parser;
|
||||
|
||||
const MDTEST_TEST_FILTER: &str = "MDTEST_TEST_FILTER";
|
||||
|
||||
/// Run `path` as a markdown test suite with given `title`.
|
||||
///
|
||||
/// Panic on test failure, and print failure details.
|
||||
#[allow(clippy::print_stdout)]
|
||||
pub fn run(path: &Path, long_title: &str, short_title: &str) {
|
||||
pub fn run(path: &Utf8Path, long_title: &str, short_title: &str, test_name: &str) {
|
||||
let source = std::fs::read_to_string(path).unwrap();
|
||||
let suite = match test_parser::parse(short_title, &source) {
|
||||
Ok(suite) => suite,
|
||||
Err(err) => {
|
||||
panic!("Error parsing `{}`: {err}", path.to_str().unwrap())
|
||||
panic!("Error parsing `{path}`: {err}")
|
||||
}
|
||||
};
|
||||
|
||||
let mut db = db::Db::setup(SystemPathBuf::from("/src"));
|
||||
|
||||
let filter = std::env::var(MDTEST_TEST_FILTER).ok();
|
||||
let mut any_failures = false;
|
||||
for test in suite.tests() {
|
||||
if filter.as_ref().is_some_and(|f| !test.name().contains(f)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove all files so that the db is in a "fresh" state.
|
||||
db.memory_file_system().remove_all();
|
||||
Files::sync_all(&mut db);
|
||||
@@ -54,6 +60,15 @@ pub fn run(path: &Path, long_title: &str, short_title: &str) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!(
|
||||
"\nTo rerun this specific test, set the environment variable: {MDTEST_TEST_FILTER}=\"{}\"",
|
||||
test.name()
|
||||
);
|
||||
println!(
|
||||
"{MDTEST_TEST_FILTER}=\"{}\" cargo test -p red_knot_python_semantic --test mdtest -- {test_name}",
|
||||
test.name()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,24 +101,9 @@ fn run_test(db: &mut db::Db, test: &parser::MarkdownTest) -> Result<(), Failures
|
||||
let failures: Failures = test_files
|
||||
.into_iter()
|
||||
.filter_map(|test_file| {
|
||||
let parsed = parsed_module(db, test_file.file);
|
||||
|
||||
let mut diagnostics: Vec<Box<_>> = parsed
|
||||
.errors()
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|error| {
|
||||
let diagnostic: Box<dyn Diagnostic> =
|
||||
Box::new(ParseDiagnostic::new(test_file.file, error));
|
||||
diagnostic
|
||||
})
|
||||
.collect();
|
||||
|
||||
let type_diagnostics = check_types(db, test_file.file);
|
||||
diagnostics.extend(type_diagnostics.into_iter().map(|diagnostic| {
|
||||
let diagnostic: Box<dyn Diagnostic> = Box::new((*diagnostic).clone());
|
||||
diagnostic
|
||||
}));
|
||||
let mut diagnostics = check_types::accumulated::<CompileDiagnostic>(db, test_file.file);
|
||||
// Filter out diagnostics that are not related to the current file.
|
||||
diagnostics.retain(|diagnostic| diagnostic.file() == test_file.file);
|
||||
|
||||
match matcher::match_file(db, test_file.file, diagnostics) {
|
||||
Ok(()) => None,
|
||||
|
||||
@@ -180,6 +180,16 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Discard `@Todo`-type metadata from expected types, which is not available
|
||||
/// when running in release mode.
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn discard_todo_metadata(ty: &str) -> std::borrow::Cow<'_, str> {
|
||||
static TODO_METADATA_REGEX: std::sync::LazyLock<regex::Regex> =
|
||||
std::sync::LazyLock::new(|| regex::Regex::new(r"@Todo\([^)]*\)").unwrap());
|
||||
|
||||
TODO_METADATA_REGEX.replace_all(ty, "@Todo")
|
||||
}
|
||||
|
||||
struct Matcher {
|
||||
line_index: LineIndex,
|
||||
source: SourceText,
|
||||
@@ -276,6 +286,9 @@ impl Matcher {
|
||||
}
|
||||
}
|
||||
Assertion::Revealed(expected_type) => {
|
||||
#[cfg(not(debug_assertions))]
|
||||
let expected_type = discard_todo_metadata(&expected_type);
|
||||
|
||||
let mut matched_revealed_type = None;
|
||||
let mut matched_undefined_reveal = None;
|
||||
let expected_reveal_type_message = format!("Revealed type is `{expected_type}`");
|
||||
|
||||
@@ -1 +1 @@
|
||||
5052fa2f18db4493892e0f2775030683c9d06531
|
||||
0a2da01946a406ede42e9c66f416a7e7758991d6
|
||||
|
||||
@@ -33,6 +33,7 @@ _contextvars: 3.7-
|
||||
_csv: 3.0-
|
||||
_ctypes: 3.0-
|
||||
_curses: 3.0-
|
||||
_curses_panel: 3.0-
|
||||
_dbm: 3.0-
|
||||
_decimal: 3.3-
|
||||
_dummy_thread: 3.0-3.8
|
||||
@@ -40,6 +41,7 @@ _dummy_threading: 3.0-3.8
|
||||
_frozen_importlib: 3.0-
|
||||
_frozen_importlib_external: 3.5-
|
||||
_gdbm: 3.0-
|
||||
_hashlib: 3.0-
|
||||
_heapq: 3.0-
|
||||
_imp: 3.0-
|
||||
_interpchannels: 3.13-
|
||||
@@ -52,6 +54,7 @@ _lsprof: 3.0-
|
||||
_lzma: 3.3-
|
||||
_markupbase: 3.0-
|
||||
_msi: 3.0-3.12
|
||||
_multibytecodec: 3.0-
|
||||
_operator: 3.4-
|
||||
_osx_support: 3.0-
|
||||
_posixsubprocess: 3.2-
|
||||
@@ -139,6 +142,12 @@ doctest: 3.0-
|
||||
dummy_threading: 3.0-3.8
|
||||
email: 3.0-
|
||||
encodings: 3.0-
|
||||
encodings.cp1125: 3.4-
|
||||
encodings.cp273: 3.4-
|
||||
encodings.cp858: 3.2-
|
||||
encodings.koi8_t: 3.5-
|
||||
encodings.kz1048: 3.5-
|
||||
encodings.mac_centeuro: 3.0-3.8
|
||||
ensurepip: 3.0-
|
||||
enum: 3.4-
|
||||
errno: 3.0-
|
||||
|
||||
@@ -2,10 +2,13 @@ import codecs
|
||||
import sys
|
||||
from _typeshed import ReadableBuffer
|
||||
from collections.abc import Callable
|
||||
from typing import Literal, overload
|
||||
from typing import Literal, final, overload, type_check_only
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
# This type is not exposed; it is defined in unicodeobject.c
|
||||
# At runtime it calls itself builtins.EncodingMap
|
||||
@final
|
||||
@type_check_only
|
||||
class _EncodingMap:
|
||||
def size(self) -> int: ...
|
||||
|
||||
|
||||
@@ -73,6 +73,7 @@ _VT_co = TypeVar("_VT_co", covariant=True) # Value type covariant containers.
|
||||
@final
|
||||
class dict_keys(KeysView[_KT_co], Generic[_KT_co, _VT_co]): # undocumented
|
||||
def __eq__(self, value: object, /) -> bool: ...
|
||||
def __reversed__(self) -> Iterator[_KT_co]: ...
|
||||
if sys.version_info >= (3, 13):
|
||||
def isdisjoint(self, other: Iterable[_KT_co], /) -> bool: ...
|
||||
if sys.version_info >= (3, 10):
|
||||
@@ -81,6 +82,7 @@ class dict_keys(KeysView[_KT_co], Generic[_KT_co, _VT_co]): # undocumented
|
||||
|
||||
@final
|
||||
class dict_values(ValuesView[_VT_co], Generic[_KT_co, _VT_co]): # undocumented
|
||||
def __reversed__(self) -> Iterator[_VT_co]: ...
|
||||
if sys.version_info >= (3, 10):
|
||||
@property
|
||||
def mapping(self) -> MappingProxyType[_KT_co, _VT_co]: ...
|
||||
@@ -88,6 +90,7 @@ class dict_values(ValuesView[_VT_co], Generic[_KT_co, _VT_co]): # undocumented
|
||||
@final
|
||||
class dict_items(ItemsView[_KT_co, _VT_co]): # undocumented
|
||||
def __eq__(self, value: object, /) -> bool: ...
|
||||
def __reversed__(self) -> Iterator[tuple[_KT_co, _VT_co]]: ...
|
||||
if sys.version_info >= (3, 13):
|
||||
def isdisjoint(self, other: Iterable[tuple[_KT_co, _VT_co]], /) -> bool: ...
|
||||
if sys.version_info >= (3, 10):
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import csv
|
||||
import sys
|
||||
from _typeshed import SupportsWrite
|
||||
from collections.abc import Iterable, Iterator
|
||||
from typing import Any, Final
|
||||
from typing_extensions import TypeAlias
|
||||
from collections.abc import Iterable
|
||||
from typing import Any, Final, type_check_only
|
||||
from typing_extensions import Self, TypeAlias
|
||||
|
||||
__version__: Final[str]
|
||||
|
||||
@@ -45,17 +45,47 @@ class Dialect:
|
||||
strict: bool = False,
|
||||
) -> None: ...
|
||||
|
||||
class _reader(Iterator[list[str]]):
|
||||
@property
|
||||
def dialect(self) -> Dialect: ...
|
||||
line_num: int
|
||||
def __next__(self) -> list[str]: ...
|
||||
if sys.version_info >= (3, 10):
|
||||
# This class calls itself _csv.reader.
|
||||
class Reader:
|
||||
@property
|
||||
def dialect(self) -> Dialect: ...
|
||||
line_num: int
|
||||
def __iter__(self) -> Self: ...
|
||||
def __next__(self) -> list[str]: ...
|
||||
|
||||
class _writer:
|
||||
@property
|
||||
def dialect(self) -> Dialect: ...
|
||||
def writerow(self, row: Iterable[Any]) -> Any: ...
|
||||
def writerows(self, rows: Iterable[Iterable[Any]]) -> None: ...
|
||||
# This class calls itself _csv.writer.
|
||||
class Writer:
|
||||
@property
|
||||
def dialect(self) -> Dialect: ...
|
||||
if sys.version_info >= (3, 13):
|
||||
def writerow(self, row: Iterable[Any], /) -> Any: ...
|
||||
def writerows(self, rows: Iterable[Iterable[Any]], /) -> None: ...
|
||||
else:
|
||||
def writerow(self, row: Iterable[Any]) -> Any: ...
|
||||
def writerows(self, rows: Iterable[Iterable[Any]]) -> None: ...
|
||||
|
||||
# For the return types below.
|
||||
# These aliases can be removed when typeshed drops support for 3.9.
|
||||
_reader = Reader
|
||||
_writer = Writer
|
||||
else:
|
||||
# This class is not exposed. It calls itself _csv.reader.
|
||||
@type_check_only
|
||||
class _reader:
|
||||
@property
|
||||
def dialect(self) -> Dialect: ...
|
||||
line_num: int
|
||||
def __iter__(self) -> Self: ...
|
||||
def __next__(self) -> list[str]: ...
|
||||
|
||||
# This class is not exposed. It calls itself _csv.writer.
|
||||
@type_check_only
|
||||
class _writer:
|
||||
@property
|
||||
def dialect(self) -> Dialect: ...
|
||||
def writerow(self, row: Iterable[Any]) -> Any: ...
|
||||
def writerows(self, rows: Iterable[Iterable[Any]]) -> None: ...
|
||||
|
||||
def writer(
|
||||
csvfile: SupportsWrite[str],
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import _typeshed
|
||||
import sys
|
||||
from _typeshed import ReadableBuffer, WriteableBuffer
|
||||
from _typeshed import ReadableBuffer, StrOrBytesPath, WriteableBuffer
|
||||
from abc import abstractmethod
|
||||
from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence
|
||||
from ctypes import CDLL, ArgumentError as ArgumentError, c_void_p
|
||||
from typing import Any, ClassVar, Generic, TypeVar, overload
|
||||
from typing import Any, ClassVar, Generic, TypeVar, final, overload, type_check_only
|
||||
from typing_extensions import Self, TypeAlias
|
||||
|
||||
if sys.version_info >= (3, 9):
|
||||
@@ -47,46 +48,79 @@ if sys.platform == "win32":
|
||||
def LoadLibrary(name: str, load_flags: int = 0, /) -> int: ...
|
||||
def FreeLibrary(handle: int, /) -> None: ...
|
||||
|
||||
class _CDataMeta(type):
|
||||
# By default mypy complains about the following two methods, because strictly speaking cls
|
||||
# might not be a Type[_CT]. However this can never actually happen, because the only class that
|
||||
# uses _CDataMeta as its metaclass is _CData. So it's safe to ignore the errors here.
|
||||
def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
else:
|
||||
def dlclose(handle: int, /) -> None: ...
|
||||
# The default for flag is RTLD_GLOBAL|RTLD_LOCAL, which is platform dependent.
|
||||
def dlopen(name: StrOrBytesPath, flag: int = ..., /) -> int: ...
|
||||
def dlsym(handle: int, name: str, /) -> int: ...
|
||||
|
||||
class _CData(metaclass=_CDataMeta):
|
||||
if sys.version_info >= (3, 13):
|
||||
# This class is not exposed. It calls itself _ctypes.CType_Type.
|
||||
@type_check_only
|
||||
class _CType_Type(type):
|
||||
# By default mypy complains about the following two methods, because strictly speaking cls
|
||||
# might not be a Type[_CT]. However this doesn't happen because this is only a
|
||||
# metaclass for subclasses of _CData.
|
||||
def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
|
||||
_CTypeBaseType = _CType_Type
|
||||
|
||||
else:
|
||||
_CTypeBaseType = type
|
||||
|
||||
# This class is not exposed.
|
||||
@type_check_only
|
||||
class _CData:
|
||||
_b_base_: int
|
||||
_b_needsfree_: bool
|
||||
_objects: Mapping[Any, int] | None
|
||||
# At runtime the following classmethods are available only on classes, not
|
||||
# on instances. This can't be reflected properly in the type system:
|
||||
#
|
||||
# Structure.from_buffer(...) # valid at runtime
|
||||
# Structure(...).from_buffer(...) # invalid at runtime
|
||||
#
|
||||
@classmethod
|
||||
def from_buffer(cls, source: WriteableBuffer, offset: int = ...) -> Self: ...
|
||||
@classmethod
|
||||
def from_buffer_copy(cls, source: ReadableBuffer, offset: int = ...) -> Self: ...
|
||||
@classmethod
|
||||
def from_address(cls, address: int) -> Self: ...
|
||||
@classmethod
|
||||
def from_param(cls, value: Any, /) -> Self | _CArgObject: ...
|
||||
@classmethod
|
||||
def in_dll(cls, library: CDLL, name: str) -> Self: ...
|
||||
def __buffer__(self, flags: int, /) -> memoryview: ...
|
||||
def __release_buffer__(self, buffer: memoryview, /) -> None: ...
|
||||
def __ctypes_from_outparam__(self, /) -> Self: ...
|
||||
|
||||
class _SimpleCData(_CData, Generic[_T]):
|
||||
# this is a union of all the subclasses of _CData, which is useful because of
|
||||
# the methods that are present on each of those subclasses which are not present
|
||||
# on _CData itself.
|
||||
_CDataType: TypeAlias = _SimpleCData[Any] | _Pointer[Any] | CFuncPtr | Union | Structure | Array[Any]
|
||||
|
||||
# This class is not exposed. It calls itself _ctypes.PyCSimpleType.
|
||||
@type_check_only
|
||||
class _PyCSimpleType(_CTypeBaseType):
|
||||
def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ...
|
||||
def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
|
||||
def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
|
||||
def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ...
|
||||
def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ...
|
||||
if sys.version_info < (3, 13):
|
||||
# Inherited from CType_Type starting on 3.13
|
||||
def __mul__(self: type[_CT], value: int, /) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
def __rmul__(self: type[_CT], value: int, /) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
|
||||
class _SimpleCData(_CData, Generic[_T], metaclass=_PyCSimpleType):
|
||||
value: _T
|
||||
# The TypeVar can be unsolved here,
|
||||
# but we can't use overloads without creating many, many mypy false-positive errors
|
||||
def __init__(self, value: _T = ...) -> None: ... # pyright: ignore[reportInvalidTypeVarUse]
|
||||
def __ctypes_from_outparam__(self, /) -> _T: ... # type: ignore[override]
|
||||
|
||||
class _CanCastTo(_CData): ...
|
||||
class _PointerLike(_CanCastTo): ...
|
||||
|
||||
class _Pointer(_PointerLike, _CData, Generic[_CT]):
|
||||
# This type is not exposed. It calls itself _ctypes.PyCPointerType.
|
||||
@type_check_only
|
||||
class _PyCPointerType(_CTypeBaseType):
|
||||
def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ...
|
||||
def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
|
||||
def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
|
||||
def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ...
|
||||
def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ...
|
||||
def set_type(self, type: Any, /) -> None: ...
|
||||
if sys.version_info < (3, 13):
|
||||
# Inherited from CType_Type starting on 3.13
|
||||
def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
|
||||
class _Pointer(_PointerLike, _CData, Generic[_CT], metaclass=_PyCPointerType):
|
||||
_type_: type[_CT]
|
||||
contents: _CT
|
||||
@overload
|
||||
@@ -105,16 +139,32 @@ def POINTER(type: None, /) -> type[c_void_p]: ...
|
||||
def POINTER(type: type[_CT], /) -> type[_Pointer[_CT]]: ...
|
||||
def pointer(obj: _CT, /) -> _Pointer[_CT]: ...
|
||||
|
||||
# This class is not exposed. It calls itself _ctypes.CArgObject.
|
||||
@final
|
||||
@type_check_only
|
||||
class _CArgObject: ...
|
||||
|
||||
def byref(obj: _CData, offset: int = ...) -> _CArgObject: ...
|
||||
def byref(obj: _CData | _CDataType, offset: int = ...) -> _CArgObject: ...
|
||||
|
||||
_ECT: TypeAlias = Callable[[_CData | None, CFuncPtr, tuple[_CData, ...]], _CData]
|
||||
_ECT: TypeAlias = Callable[[_CData | _CDataType | None, CFuncPtr, tuple[_CData | _CDataType, ...]], _CDataType]
|
||||
_PF: TypeAlias = tuple[int] | tuple[int, str | None] | tuple[int, str | None, Any]
|
||||
|
||||
class CFuncPtr(_PointerLike, _CData):
|
||||
restype: type[_CData] | Callable[[int], Any] | None
|
||||
argtypes: Sequence[type[_CData]]
|
||||
# This class is not exposed. It calls itself _ctypes.PyCFuncPtrType.
|
||||
@type_check_only
|
||||
class _PyCFuncPtrType(_CTypeBaseType):
|
||||
def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ...
|
||||
def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
|
||||
def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
|
||||
def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ...
|
||||
def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ...
|
||||
if sys.version_info < (3, 13):
|
||||
# Inherited from CType_Type starting on 3.13
|
||||
def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
|
||||
class CFuncPtr(_PointerLike, _CData, metaclass=_PyCFuncPtrType):
|
||||
restype: type[_CDataType] | Callable[[int], Any] | None
|
||||
argtypes: Sequence[type[_CDataType]]
|
||||
errcheck: _ECT
|
||||
# Abstract attribute that must be defined on subclasses
|
||||
_flags_: ClassVar[int]
|
||||
@@ -129,7 +179,7 @@ class CFuncPtr(_PointerLike, _CData):
|
||||
if sys.platform == "win32":
|
||||
@overload
|
||||
def __init__(
|
||||
self, vtbl_index: int, name: str, paramflags: tuple[_PF, ...] | None = ..., iid: _CData | None = ..., /
|
||||
self, vtbl_index: int, name: str, paramflags: tuple[_PF, ...] | None = ..., iid: _CData | _CDataType | None = ..., /
|
||||
) -> None: ...
|
||||
|
||||
def __call__(self, *args: Any, **kwargs: Any) -> Any: ...
|
||||
@@ -137,30 +187,95 @@ class CFuncPtr(_PointerLike, _CData):
|
||||
_GetT = TypeVar("_GetT")
|
||||
_SetT = TypeVar("_SetT")
|
||||
|
||||
# This class is not exposed. It calls itself _ctypes.CField.
|
||||
@final
|
||||
@type_check_only
|
||||
class _CField(Generic[_CT, _GetT, _SetT]):
|
||||
offset: int
|
||||
size: int
|
||||
@overload
|
||||
def __get__(self, instance: None, owner: type[Any] | None, /) -> Self: ...
|
||||
@overload
|
||||
def __get__(self, instance: Any, owner: type[Any] | None, /) -> _GetT: ...
|
||||
if sys.version_info >= (3, 10):
|
||||
@overload
|
||||
def __get__(self, instance: None, owner: type[Any] | None = None, /) -> Self: ...
|
||||
@overload
|
||||
def __get__(self, instance: Any, owner: type[Any] | None = None, /) -> _GetT: ...
|
||||
else:
|
||||
@overload
|
||||
def __get__(self, instance: None, owner: type[Any] | None, /) -> Self: ...
|
||||
@overload
|
||||
def __get__(self, instance: Any, owner: type[Any] | None, /) -> _GetT: ...
|
||||
|
||||
def __set__(self, instance: Any, value: _SetT, /) -> None: ...
|
||||
|
||||
class _StructUnionMeta(_CDataMeta):
|
||||
_fields_: Sequence[tuple[str, type[_CData]] | tuple[str, type[_CData], int]]
|
||||
_pack_: int
|
||||
_anonymous_: Sequence[str]
|
||||
# This class is not exposed. It calls itself _ctypes.UnionType.
|
||||
@type_check_only
|
||||
class _UnionType(_CTypeBaseType):
|
||||
def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ...
|
||||
def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
|
||||
def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
|
||||
def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ...
|
||||
def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ...
|
||||
# At runtime, various attributes are created on a Union subclass based
|
||||
# on its _fields_. This method doesn't exist, but represents those
|
||||
# dynamically created attributes.
|
||||
def __getattr__(self, name: str) -> _CField[Any, Any, Any]: ...
|
||||
if sys.version_info < (3, 13):
|
||||
# Inherited from CType_Type starting on 3.13
|
||||
def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
|
||||
class Union(_CData, metaclass=_UnionType):
|
||||
_fields_: ClassVar[Sequence[tuple[str, type[_CDataType]] | tuple[str, type[_CDataType], int]]]
|
||||
_pack_: ClassVar[int]
|
||||
_anonymous_: ClassVar[Sequence[str]]
|
||||
if sys.version_info >= (3, 13):
|
||||
_align_: ClassVar[int]
|
||||
|
||||
class _StructUnionBase(_CData, metaclass=_StructUnionMeta):
|
||||
def __init__(self, *args: Any, **kw: Any) -> None: ...
|
||||
def __getattr__(self, name: str) -> Any: ...
|
||||
def __setattr__(self, name: str, value: Any) -> None: ...
|
||||
|
||||
class Union(_StructUnionBase): ...
|
||||
class Structure(_StructUnionBase): ...
|
||||
# This class is not exposed. It calls itself _ctypes.PyCStructType.
|
||||
@type_check_only
|
||||
class _PyCStructType(_CTypeBaseType):
|
||||
def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ...
|
||||
def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
|
||||
def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
|
||||
def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ...
|
||||
def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ...
|
||||
# At runtime, various attributes are created on a Structure subclass based
|
||||
# on its _fields_. This method doesn't exist, but represents those
|
||||
# dynamically created attributes.
|
||||
def __getattr__(self, name: str) -> _CField[Any, Any, Any]: ...
|
||||
if sys.version_info < (3, 13):
|
||||
# Inherited from CType_Type starting on 3.13
|
||||
def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
|
||||
class Array(_CData, Generic[_CT]):
|
||||
class Structure(_CData, metaclass=_PyCStructType):
|
||||
_fields_: ClassVar[Sequence[tuple[str, type[_CDataType]] | tuple[str, type[_CDataType], int]]]
|
||||
_pack_: ClassVar[int]
|
||||
_anonymous_: ClassVar[Sequence[str]]
|
||||
if sys.version_info >= (3, 13):
|
||||
_align_: ClassVar[int]
|
||||
|
||||
def __init__(self, *args: Any, **kw: Any) -> None: ...
|
||||
def __getattr__(self, name: str) -> Any: ...
|
||||
def __setattr__(self, name: str, value: Any) -> None: ...
|
||||
|
||||
# This class is not exposed. It calls itself _ctypes.PyCArrayType.
|
||||
@type_check_only
|
||||
class _PyCArrayType(_CTypeBaseType):
|
||||
def from_address(self: type[_typeshed.Self], value: int, /) -> _typeshed.Self: ...
|
||||
def from_buffer(self: type[_typeshed.Self], obj: WriteableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
|
||||
def from_buffer_copy(self: type[_typeshed.Self], buffer: ReadableBuffer, offset: int = 0, /) -> _typeshed.Self: ...
|
||||
def from_param(self: type[_typeshed.Self], value: Any, /) -> _typeshed.Self | _CArgObject: ...
|
||||
def in_dll(self: type[_typeshed.Self], dll: CDLL, name: str, /) -> _typeshed.Self: ...
|
||||
if sys.version_info < (3, 13):
|
||||
# Inherited from CType_Type starting on 3.13
|
||||
def __mul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
def __rmul__(cls: type[_CT], other: int) -> type[Array[_CT]]: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
|
||||
class Array(_CData, Generic[_CT], metaclass=_PyCArrayType):
|
||||
@property
|
||||
@abstractmethod
|
||||
def _length_(self) -> int: ...
|
||||
@@ -205,9 +320,15 @@ class Array(_CData, Generic[_CT]):
|
||||
if sys.version_info >= (3, 9):
|
||||
def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...
|
||||
|
||||
def addressof(obj: _CData, /) -> int: ...
|
||||
def alignment(obj_or_type: _CData | type[_CData], /) -> int: ...
|
||||
def addressof(obj: _CData | _CDataType, /) -> int: ...
|
||||
def alignment(obj_or_type: _CData | _CDataType | type[_CData | _CDataType], /) -> int: ...
|
||||
def get_errno() -> int: ...
|
||||
def resize(obj: _CData, size: int, /) -> None: ...
|
||||
def resize(obj: _CData | _CDataType, size: int, /) -> None: ...
|
||||
def set_errno(value: int, /) -> int: ...
|
||||
def sizeof(obj_or_type: _CData | type[_CData], /) -> int: ...
|
||||
def sizeof(obj_or_type: _CData | _CDataType | type[_CData | _CDataType], /) -> int: ...
|
||||
def PyObj_FromPtr(address: int, /) -> Any: ...
|
||||
def Py_DECREF(o: _T, /) -> _T: ...
|
||||
def Py_INCREF(o: _T, /) -> _T: ...
|
||||
def buffer_info(o: _CData | _CDataType | type[_CData | _CDataType], /) -> tuple[str, int, tuple[int, ...]]: ...
|
||||
def call_cdeclfunction(address: int, arguments: tuple[Any, ...], /) -> Any: ...
|
||||
def call_function(address: int, arguments: tuple[Any, ...], /) -> Any: ...
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import sys
|
||||
from _typeshed import ReadOnlyBuffer, SupportsRead
|
||||
from typing import IO, Any, NamedTuple, final, overload
|
||||
from curses import _ncurses_version
|
||||
from typing import IO, Any, final, overload
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
# NOTE: This module is ordinarily only available on Unix, but the windows-curses
|
||||
@@ -549,9 +550,4 @@ class window: # undocumented
|
||||
@overload
|
||||
def vline(self, y: int, x: int, ch: _ChType, n: int) -> None: ...
|
||||
|
||||
class _ncurses_version(NamedTuple):
|
||||
major: int
|
||||
minor: int
|
||||
patch: int
|
||||
|
||||
ncurses_version: _ncurses_version
|
||||
|
||||
27
crates/red_knot_vendored/vendor/typeshed/stdlib/_curses_panel.pyi
vendored
Normal file
27
crates/red_knot_vendored/vendor/typeshed/stdlib/_curses_panel.pyi
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
from _curses import window
|
||||
from typing import final
|
||||
|
||||
__version__: str
|
||||
version: str
|
||||
|
||||
class error(Exception): ...
|
||||
|
||||
@final
|
||||
class panel:
|
||||
def above(self) -> panel: ...
|
||||
def below(self) -> panel: ...
|
||||
def bottom(self) -> None: ...
|
||||
def hidden(self) -> bool: ...
|
||||
def hide(self) -> None: ...
|
||||
def move(self, y: int, x: int, /) -> None: ...
|
||||
def replace(self, win: window, /) -> None: ...
|
||||
def set_userptr(self, obj: object, /) -> None: ...
|
||||
def show(self) -> None: ...
|
||||
def top(self) -> None: ...
|
||||
def userptr(self) -> object: ...
|
||||
def window(self) -> window: ...
|
||||
|
||||
def bottom_panel() -> panel: ...
|
||||
def new_panel(win: window, /) -> panel: ...
|
||||
def top_panel() -> panel: ...
|
||||
def update_panels() -> panel: ...
|
||||
@@ -1,7 +1,7 @@
|
||||
import sys
|
||||
from _typeshed import ReadOnlyBuffer, StrOrBytesPath
|
||||
from types import TracebackType
|
||||
from typing import TypeVar, overload
|
||||
from typing import TypeVar, final, overload, type_check_only
|
||||
from typing_extensions import Self, TypeAlias
|
||||
|
||||
if sys.platform != "win32":
|
||||
@@ -13,6 +13,8 @@ if sys.platform != "win32":
|
||||
library: str
|
||||
|
||||
# Actual typename dbm, not exposed by the implementation
|
||||
@final
|
||||
@type_check_only
|
||||
class _dbm:
|
||||
def close(self) -> None: ...
|
||||
if sys.version_info >= (3, 13):
|
||||
@@ -22,18 +24,17 @@ if sys.platform != "win32":
|
||||
def __setitem__(self, key: _KeyType, value: _ValueType) -> None: ...
|
||||
def __delitem__(self, key: _KeyType) -> None: ...
|
||||
def __len__(self) -> int: ...
|
||||
def __del__(self) -> None: ...
|
||||
def __enter__(self) -> Self: ...
|
||||
def __exit__(
|
||||
self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
|
||||
) -> None: ...
|
||||
@overload
|
||||
def get(self, k: _KeyType) -> bytes | None: ...
|
||||
def get(self, k: _KeyType, /) -> bytes | None: ...
|
||||
@overload
|
||||
def get(self, k: _KeyType, default: _T) -> bytes | _T: ...
|
||||
def get(self, k: _KeyType, default: _T, /) -> bytes | _T: ...
|
||||
def keys(self) -> list[bytes]: ...
|
||||
def setdefault(self, k: _KeyType, default: _ValueType = ...) -> bytes: ...
|
||||
# Don't exist at runtime
|
||||
def setdefault(self, k: _KeyType, default: _ValueType = ..., /) -> bytes: ...
|
||||
# This isn't true, but the class can't be instantiated. See #13024
|
||||
__new__: None # type: ignore[assignment]
|
||||
__init__: None # type: ignore[assignment]
|
||||
|
||||
|
||||
@@ -17,20 +17,13 @@ from decimal import (
|
||||
Rounded as Rounded,
|
||||
Subnormal as Subnormal,
|
||||
Underflow as Underflow,
|
||||
_ContextManager,
|
||||
)
|
||||
from types import TracebackType
|
||||
from typing import Final
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
_TrapType: TypeAlias = type[DecimalException]
|
||||
|
||||
class _ContextManager:
|
||||
new_context: Context
|
||||
saved_context: Context
|
||||
def __init__(self, new_context: Context) -> None: ...
|
||||
def __enter__(self) -> Context: ...
|
||||
def __exit__(self, t: type[BaseException] | None, v: BaseException | None, tb: TracebackType | None) -> None: ...
|
||||
|
||||
__version__: Final[str]
|
||||
__libmpdec_version__: Final[str]
|
||||
|
||||
|
||||
80
crates/red_knot_vendored/vendor/typeshed/stdlib/_hashlib.pyi
vendored
Normal file
80
crates/red_knot_vendored/vendor/typeshed/stdlib/_hashlib.pyi
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
import sys
|
||||
from _typeshed import ReadableBuffer
|
||||
from collections.abc import Callable
|
||||
from types import ModuleType
|
||||
from typing import AnyStr, final, overload
|
||||
from typing_extensions import Self, TypeAlias
|
||||
|
||||
_DigestMod: TypeAlias = str | Callable[[], HASH] | ModuleType | None
|
||||
|
||||
openssl_md_meth_names: frozenset[str]
|
||||
|
||||
class HASH:
|
||||
@property
|
||||
def digest_size(self) -> int: ...
|
||||
@property
|
||||
def block_size(self) -> int: ...
|
||||
@property
|
||||
def name(self) -> str: ...
|
||||
def copy(self) -> Self: ...
|
||||
def digest(self) -> bytes: ...
|
||||
def hexdigest(self) -> str: ...
|
||||
def update(self, obj: ReadableBuffer, /) -> None: ...
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
class UnsupportedDigestmodError(ValueError): ...
|
||||
|
||||
if sys.version_info >= (3, 9):
|
||||
class HASHXOF(HASH):
|
||||
def digest(self, length: int) -> bytes: ... # type: ignore[override]
|
||||
def hexdigest(self, length: int) -> str: ... # type: ignore[override]
|
||||
|
||||
@final
|
||||
class HMAC:
|
||||
@property
|
||||
def digest_size(self) -> int: ...
|
||||
@property
|
||||
def block_size(self) -> int: ...
|
||||
@property
|
||||
def name(self) -> str: ...
|
||||
def copy(self) -> Self: ...
|
||||
def digest(self) -> bytes: ...
|
||||
def hexdigest(self) -> str: ...
|
||||
def update(self, msg: ReadableBuffer) -> None: ...
|
||||
|
||||
@overload
|
||||
def compare_digest(a: ReadableBuffer, b: ReadableBuffer, /) -> bool: ...
|
||||
@overload
|
||||
def compare_digest(a: AnyStr, b: AnyStr, /) -> bool: ...
|
||||
def get_fips_mode() -> int: ...
|
||||
def hmac_new(key: bytes | bytearray, msg: ReadableBuffer = b"", digestmod: _DigestMod = None) -> HMAC: ...
|
||||
def new(name: str, string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
|
||||
def openssl_md5(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
|
||||
def openssl_sha1(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
|
||||
def openssl_sha224(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
|
||||
def openssl_sha256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
|
||||
def openssl_sha384(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
|
||||
def openssl_sha512(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
|
||||
def openssl_sha3_224(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
|
||||
def openssl_sha3_256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
|
||||
def openssl_sha3_384(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
|
||||
def openssl_sha3_512(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASH: ...
|
||||
def openssl_shake_128(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASHXOF: ...
|
||||
def openssl_shake_256(string: ReadableBuffer = b"", *, usedforsecurity: bool = True) -> HASHXOF: ...
|
||||
|
||||
else:
|
||||
def new(name: str, string: ReadableBuffer = b"") -> HASH: ...
|
||||
def openssl_md5(string: ReadableBuffer = b"") -> HASH: ...
|
||||
def openssl_sha1(string: ReadableBuffer = b"") -> HASH: ...
|
||||
def openssl_sha224(string: ReadableBuffer = b"") -> HASH: ...
|
||||
def openssl_sha256(string: ReadableBuffer = b"") -> HASH: ...
|
||||
def openssl_sha384(string: ReadableBuffer = b"") -> HASH: ...
|
||||
def openssl_sha512(string: ReadableBuffer = b"") -> HASH: ...
|
||||
|
||||
def hmac_digest(key: bytes | bytearray, msg: ReadableBuffer, digest: str) -> bytes: ...
|
||||
def pbkdf2_hmac(
|
||||
hash_name: str, password: ReadableBuffer, salt: ReadableBuffer, iterations: int, dklen: int | None = None
|
||||
) -> bytes: ...
|
||||
def scrypt(
|
||||
password: ReadableBuffer, *, salt: ReadableBuffer, n: int, r: int, p: int, maxmem: int = 0, dklen: int = 64
|
||||
) -> bytes: ...
|
||||
@@ -45,5 +45,6 @@ class make_scanner:
|
||||
def __init__(self, context: make_scanner) -> None: ...
|
||||
def __call__(self, string: str, index: int) -> tuple[Any, int]: ...
|
||||
|
||||
def encode_basestring(s: str, /) -> str: ...
|
||||
def encode_basestring_ascii(s: str, /) -> str: ...
|
||||
def scanstring(string: str, end: int, strict: bool = ...) -> tuple[str, int]: ...
|
||||
|
||||
44
crates/red_knot_vendored/vendor/typeshed/stdlib/_multibytecodec.pyi
vendored
Normal file
44
crates/red_knot_vendored/vendor/typeshed/stdlib/_multibytecodec.pyi
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
from _typeshed import ReadableBuffer
|
||||
from codecs import _ReadableStream, _WritableStream
|
||||
from collections.abc import Iterable
|
||||
from typing import final, type_check_only
|
||||
|
||||
# This class is not exposed. It calls itself _multibytecodec.MultibyteCodec.
|
||||
@final
|
||||
@type_check_only
|
||||
class _MultibyteCodec:
|
||||
def decode(self, input: ReadableBuffer, errors: str | None = None) -> str: ...
|
||||
def encode(self, input: str, errors: str | None = None) -> bytes: ...
|
||||
|
||||
class MultibyteIncrementalDecoder:
|
||||
errors: str
|
||||
def __init__(self, errors: str = "strict") -> None: ...
|
||||
def decode(self, input: ReadableBuffer, final: bool = False) -> str: ...
|
||||
def getstate(self) -> tuple[bytes, int]: ...
|
||||
def reset(self) -> None: ...
|
||||
def setstate(self, state: tuple[bytes, int], /) -> None: ...
|
||||
|
||||
class MultibyteIncrementalEncoder:
|
||||
errors: str
|
||||
def __init__(self, errors: str = "strict") -> None: ...
|
||||
def encode(self, input: str, final: bool = False) -> bytes: ...
|
||||
def getstate(self) -> int: ...
|
||||
def reset(self) -> None: ...
|
||||
def setstate(self, state: int, /) -> None: ...
|
||||
|
||||
class MultibyteStreamReader:
|
||||
errors: str
|
||||
stream: _ReadableStream
|
||||
def __init__(self, stream: _ReadableStream, errors: str = "strict") -> None: ...
|
||||
def read(self, sizeobj: int | None = None, /) -> str: ...
|
||||
def readline(self, sizeobj: int | None = None, /) -> str: ...
|
||||
def readlines(self, sizehintobj: int | None = None, /) -> list[str]: ...
|
||||
def reset(self) -> None: ...
|
||||
|
||||
class MultibyteStreamWriter:
|
||||
errors: str
|
||||
stream: _WritableStream
|
||||
def __init__(self, stream: _WritableStream, errors: str = "strict") -> None: ...
|
||||
def reset(self) -> None: ...
|
||||
def write(self, strobj: str, /) -> None: ...
|
||||
def writelines(self, lines: Iterable[str], /) -> None: ...
|
||||
@@ -4,7 +4,6 @@ from collections.abc import Callable, Sequence
|
||||
from typing import SupportsIndex
|
||||
|
||||
if sys.platform != "win32":
|
||||
def cloexec_pipe() -> tuple[int, int]: ...
|
||||
def fork_exec(
|
||||
args: Sequence[StrOrBytesPath] | None,
|
||||
executable_list: Sequence[bytes],
|
||||
|
||||
@@ -3,7 +3,7 @@ from _typeshed import ReadableBuffer, WriteableBuffer
|
||||
from collections.abc import Iterable
|
||||
from socket import error as error, gaierror as gaierror, herror as herror, timeout as timeout
|
||||
from typing import Any, SupportsIndex, overload
|
||||
from typing_extensions import TypeAlias
|
||||
from typing_extensions import CapsuleType, TypeAlias
|
||||
|
||||
_CMSG: TypeAlias = tuple[int, int, bytes]
|
||||
_CMSGArg: TypeAlias = tuple[int, int, ReadableBuffer]
|
||||
@@ -72,7 +72,8 @@ SO_SNDBUF: int
|
||||
SO_SNDLOWAT: int
|
||||
SO_SNDTIMEO: int
|
||||
SO_TYPE: int
|
||||
SO_USELOOPBACK: int
|
||||
if sys.platform != "linux":
|
||||
SO_USELOOPBACK: int
|
||||
if sys.platform == "win32":
|
||||
SO_EXCLUSIVEADDRUSE: int
|
||||
if sys.platform != "win32":
|
||||
@@ -87,7 +88,10 @@ if sys.platform != "win32" and sys.platform != "darwin":
|
||||
SO_PEERSEC: int
|
||||
SO_PRIORITY: int
|
||||
SO_PROTOCOL: int
|
||||
if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux":
|
||||
SO_SETFIB: int
|
||||
if sys.platform == "linux" and sys.version_info >= (3, 13):
|
||||
SO_BINDTOIFINDEX: int
|
||||
|
||||
SOMAXCONN: int
|
||||
|
||||
@@ -99,27 +103,32 @@ MSG_TRUNC: int
|
||||
MSG_WAITALL: int
|
||||
if sys.platform != "win32":
|
||||
MSG_DONTWAIT: int
|
||||
MSG_EOF: int
|
||||
MSG_EOR: int
|
||||
MSG_NOSIGNAL: int # Sometimes this exists on darwin, sometimes not
|
||||
if sys.platform != "darwin":
|
||||
MSG_BCAST: int
|
||||
MSG_ERRQUEUE: int
|
||||
if sys.platform == "win32":
|
||||
MSG_BCAST: int
|
||||
MSG_MCAST: int
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
MSG_BTAG: int
|
||||
MSG_CMSG_CLOEXEC: int
|
||||
MSG_CONFIRM: int
|
||||
MSG_ETAG: int
|
||||
MSG_FASTOPEN: int
|
||||
MSG_MORE: int
|
||||
if sys.platform != "win32" and sys.platform != "linux":
|
||||
MSG_EOF: int
|
||||
if sys.platform != "win32" and sys.platform != "linux" and sys.platform != "darwin":
|
||||
MSG_NOTIFICATION: int
|
||||
MSG_BTAG: int # Not FreeBSD either
|
||||
MSG_ETAG: int # Not FreeBSD either
|
||||
|
||||
SOL_IP: int
|
||||
SOL_SOCKET: int
|
||||
SOL_TCP: int
|
||||
SOL_UDP: int
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
# Defined in socket.h for Linux, but these aren't always present for
|
||||
# some reason.
|
||||
SOL_ATALK: int
|
||||
SOL_AX25: int
|
||||
SOL_HCI: int
|
||||
@@ -128,10 +137,11 @@ if sys.platform != "win32" and sys.platform != "darwin":
|
||||
SOL_ROSE: int
|
||||
|
||||
if sys.platform != "win32":
|
||||
SCM_CREDS: int
|
||||
SCM_RIGHTS: int
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
SCM_CREDENTIALS: int
|
||||
if sys.platform != "win32" and sys.platform != "linux":
|
||||
SCM_CREDS: int
|
||||
|
||||
IPPROTO_ICMP: int
|
||||
IPPROTO_IP: int
|
||||
@@ -143,21 +153,22 @@ IPPROTO_DSTOPTS: int
|
||||
IPPROTO_EGP: int
|
||||
IPPROTO_ESP: int
|
||||
IPPROTO_FRAGMENT: int
|
||||
IPPROTO_GGP: int
|
||||
IPPROTO_HOPOPTS: int
|
||||
IPPROTO_ICMPV6: int
|
||||
IPPROTO_IDP: int
|
||||
IPPROTO_IGMP: int
|
||||
IPPROTO_IPV4: int
|
||||
IPPROTO_IPV6: int
|
||||
IPPROTO_MAX: int
|
||||
IPPROTO_ND: int
|
||||
IPPROTO_NONE: int
|
||||
IPPROTO_PIM: int
|
||||
IPPROTO_PUP: int
|
||||
IPPROTO_ROUTING: int
|
||||
IPPROTO_SCTP: int
|
||||
if sys.platform != "darwin":
|
||||
if sys.platform != "linux":
|
||||
IPPROTO_GGP: int
|
||||
IPPROTO_IPV4: int
|
||||
IPPROTO_MAX: int
|
||||
IPPROTO_ND: int
|
||||
if sys.platform == "win32":
|
||||
IPPROTO_CBT: int
|
||||
IPPROTO_ICLFXBM: int
|
||||
IPPROTO_IGP: int
|
||||
@@ -166,18 +177,19 @@ if sys.platform != "darwin":
|
||||
IPPROTO_RDP: int
|
||||
IPPROTO_ST: int
|
||||
if sys.platform != "win32":
|
||||
IPPROTO_EON: int
|
||||
IPPROTO_GRE: int
|
||||
IPPROTO_HELLO: int
|
||||
IPPROTO_IPCOMP: int
|
||||
IPPROTO_IPIP: int
|
||||
IPPROTO_RSVP: int
|
||||
IPPROTO_TP: int
|
||||
if sys.platform != "win32" and sys.platform != "linux":
|
||||
IPPROTO_EON: int
|
||||
IPPROTO_HELLO: int
|
||||
IPPROTO_IPCOMP: int
|
||||
IPPROTO_XTP: int
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
IPPROTO_BIP: int
|
||||
IPPROTO_MOBILE: int
|
||||
IPPROTO_VRRP: int
|
||||
if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux":
|
||||
IPPROTO_BIP: int # Not FreeBSD either
|
||||
IPPROTO_MOBILE: int # Not FreeBSD either
|
||||
IPPROTO_VRRP: int # Not FreeBSD either
|
||||
if sys.version_info >= (3, 9) and sys.platform == "linux":
|
||||
# Availability: Linux >= 2.6.20, FreeBSD >= 10.1
|
||||
IPPROTO_UDPLITE: int
|
||||
@@ -202,11 +214,10 @@ IP_MULTICAST_IF: int
|
||||
IP_MULTICAST_LOOP: int
|
||||
IP_MULTICAST_TTL: int
|
||||
IP_OPTIONS: int
|
||||
IP_RECVDSTADDR: int
|
||||
if sys.platform != "linux":
|
||||
IP_RECVDSTADDR: int
|
||||
if sys.version_info >= (3, 10):
|
||||
IP_RECVTOS: int
|
||||
elif sys.platform != "win32" and sys.platform != "darwin":
|
||||
IP_RECVTOS: int
|
||||
IP_TOS: int
|
||||
IP_TTL: int
|
||||
if sys.platform != "win32":
|
||||
@@ -218,6 +229,7 @@ if sys.platform != "win32":
|
||||
IP_RETOPTS: int
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
IP_TRANSPARENT: int
|
||||
if sys.platform != "win32" and sys.platform != "darwin" and sys.version_info >= (3, 11):
|
||||
IP_BIND_ADDRESS_NO_PORT: int
|
||||
if sys.version_info >= (3, 12):
|
||||
IP_ADD_SOURCE_MEMBERSHIP: int
|
||||
@@ -255,6 +267,9 @@ if sys.platform != "win32":
|
||||
IPV6_RECVPATHMTU: int
|
||||
IPV6_RECVPKTINFO: int
|
||||
IPV6_RTHDRDSTOPTS: int
|
||||
|
||||
if sys.platform != "win32" and sys.platform != "linux":
|
||||
if sys.version_info >= (3, 9) or sys.platform != "darwin":
|
||||
IPV6_USE_MIN_MTU: int
|
||||
|
||||
EAI_AGAIN: int
|
||||
@@ -268,11 +283,12 @@ EAI_SERVICE: int
|
||||
EAI_SOCKTYPE: int
|
||||
if sys.platform != "win32":
|
||||
EAI_ADDRFAMILY: int
|
||||
EAI_OVERFLOW: int
|
||||
EAI_SYSTEM: int
|
||||
if sys.platform != "win32" and sys.platform != "linux":
|
||||
EAI_BADHINTS: int
|
||||
EAI_MAX: int
|
||||
EAI_OVERFLOW: int
|
||||
EAI_PROTOCOL: int
|
||||
EAI_SYSTEM: int
|
||||
|
||||
AI_ADDRCONFIG: int
|
||||
AI_ALL: int
|
||||
@@ -281,7 +297,7 @@ AI_NUMERICHOST: int
|
||||
AI_NUMERICSERV: int
|
||||
AI_PASSIVE: int
|
||||
AI_V4MAPPED: int
|
||||
if sys.platform != "win32":
|
||||
if sys.platform != "win32" and sys.platform != "linux":
|
||||
AI_DEFAULT: int
|
||||
AI_MASK: int
|
||||
AI_V4MAPPED_CFG: int
|
||||
@@ -293,6 +309,8 @@ NI_NAMEREQD: int
|
||||
NI_NOFQDN: int
|
||||
NI_NUMERICHOST: int
|
||||
NI_NUMERICSERV: int
|
||||
if sys.platform == "linux" and sys.version_info >= (3, 13):
|
||||
NI_IDN: int
|
||||
|
||||
TCP_FASTOPEN: int
|
||||
TCP_KEEPCNT: int
|
||||
@@ -318,6 +336,27 @@ if sys.platform != "win32" and sys.platform != "darwin":
|
||||
TCP_SYNCNT: int
|
||||
TCP_USER_TIMEOUT: int
|
||||
TCP_WINDOW_CLAMP: int
|
||||
if sys.platform == "linux" and sys.version_info >= (3, 12):
|
||||
TCP_CC_INFO: int
|
||||
TCP_FASTOPEN_CONNECT: int
|
||||
TCP_FASTOPEN_KEY: int
|
||||
TCP_FASTOPEN_NO_COOKIE: int
|
||||
TCP_INQ: int
|
||||
TCP_MD5SIG: int
|
||||
TCP_MD5SIG_EXT: int
|
||||
TCP_QUEUE_SEQ: int
|
||||
TCP_REPAIR: int
|
||||
TCP_REPAIR_OPTIONS: int
|
||||
TCP_REPAIR_QUEUE: int
|
||||
TCP_REPAIR_WINDOW: int
|
||||
TCP_SAVED_SYN: int
|
||||
TCP_SAVE_SYN: int
|
||||
TCP_THIN_DUPACK: int
|
||||
TCP_THIN_LINEAR_TIMEOUTS: int
|
||||
TCP_TIMESTAMP: int
|
||||
TCP_TX_DELAY: int
|
||||
TCP_ULP: int
|
||||
TCP_ZEROCOPY_RECEIVE: int
|
||||
|
||||
# --------------------
|
||||
# Specifically documented constants
|
||||
@@ -334,12 +373,13 @@ if sys.platform == "linux":
|
||||
CAN_ERR_FLAG: int
|
||||
CAN_ERR_MASK: int
|
||||
CAN_RAW: int
|
||||
CAN_RAW_ERR_FILTER: int
|
||||
CAN_RAW_FILTER: int
|
||||
CAN_RAW_LOOPBACK: int
|
||||
CAN_RAW_RECV_OWN_MSGS: int
|
||||
CAN_RTR_FLAG: int
|
||||
CAN_SFF_MASK: int
|
||||
if sys.version_info < (3, 11):
|
||||
CAN_RAW_ERR_FILTER: int
|
||||
|
||||
if sys.platform == "linux":
|
||||
# Availability: Linux >= 2.6.25
|
||||
@@ -437,12 +477,13 @@ if sys.platform == "linux":
|
||||
AF_RDS: int
|
||||
PF_RDS: int
|
||||
SOL_RDS: int
|
||||
# These are present in include/linux/rds.h but don't always show up
|
||||
# here.
|
||||
RDS_CANCEL_SENT_TO: int
|
||||
RDS_CMSG_RDMA_ARGS: int
|
||||
RDS_CMSG_RDMA_DEST: int
|
||||
RDS_CMSG_RDMA_MAP: int
|
||||
RDS_CMSG_RDMA_STATUS: int
|
||||
RDS_CMSG_RDMA_UPDATE: int
|
||||
RDS_CONG_MONITOR: int
|
||||
RDS_FREE_MR: int
|
||||
RDS_GET_MR: int
|
||||
@@ -456,6 +497,10 @@ if sys.platform == "linux":
|
||||
RDS_RDMA_USE_ONCE: int
|
||||
RDS_RECVERR: int
|
||||
|
||||
# This is supported by CPython but doesn't seem to be a real thing.
|
||||
# The closest existing constant in rds.h is RDS_CMSG_CONG_UPDATE
|
||||
# RDS_CMSG_RDMA_UPDATE: int
|
||||
|
||||
if sys.platform == "win32":
|
||||
SIO_RCVALL: int
|
||||
SIO_KEEPALIVE_VALS: int
|
||||
@@ -522,16 +567,17 @@ if sys.platform == "linux":
|
||||
if sys.platform != "win32" or sys.version_info >= (3, 9):
|
||||
# Documented as only available on BSD, macOS, but empirically sometimes
|
||||
# available on Windows
|
||||
AF_LINK: int
|
||||
if sys.platform != "linux":
|
||||
AF_LINK: int
|
||||
|
||||
has_ipv6: bool
|
||||
|
||||
if sys.platform != "darwin":
|
||||
if sys.platform != "darwin" and sys.platform != "linux":
|
||||
if sys.platform != "win32" or sys.version_info >= (3, 9):
|
||||
BDADDR_ANY: str
|
||||
BDADDR_LOCAL: str
|
||||
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux":
|
||||
HCI_FILTER: int # not in NetBSD or DragonFlyBSD
|
||||
HCI_TIME_STAMP: int # not in FreeBSD, NetBSD, or DragonFlyBSD
|
||||
HCI_DATA_DIR: int # not in FreeBSD, NetBSD, or DragonFlyBSD
|
||||
@@ -580,36 +626,37 @@ if sys.version_info >= (3, 12):
|
||||
if sys.platform == "linux":
|
||||
# Netlink is defined by Linux
|
||||
AF_NETLINK: int
|
||||
NETLINK_ARPD: int
|
||||
NETLINK_CRYPTO: int
|
||||
NETLINK_DNRTMSG: int
|
||||
NETLINK_FIREWALL: int
|
||||
NETLINK_IP6_FW: int
|
||||
NETLINK_NFLOG: int
|
||||
NETLINK_ROUTE6: int
|
||||
NETLINK_ROUTE: int
|
||||
NETLINK_SKIP: int
|
||||
NETLINK_TAPBASE: int
|
||||
NETLINK_TCPDIAG: int
|
||||
NETLINK_USERSOCK: int
|
||||
NETLINK_W1: int
|
||||
NETLINK_XFRM: int
|
||||
# Technically still supported by CPython
|
||||
# NETLINK_ARPD: int # linux 2.0 to 2.6.12 (EOL August 2005)
|
||||
# NETLINK_ROUTE6: int # linux 2.2 to 2.6.12 (EOL August 2005)
|
||||
# NETLINK_SKIP: int # linux 2.0 to 2.6.12 (EOL August 2005)
|
||||
# NETLINK_TAPBASE: int # linux 2.2 to 2.6.12 (EOL August 2005)
|
||||
# NETLINK_TCPDIAG: int # linux 2.6.0 to 2.6.13 (EOL December 2005)
|
||||
# NETLINK_W1: int # linux 2.6.13 to 2.6.17 (EOL October 2006)
|
||||
|
||||
if sys.platform == "darwin":
|
||||
PF_SYSTEM: int
|
||||
SYSPROTO_CONTROL: int
|
||||
|
||||
if sys.platform != "darwin":
|
||||
if sys.platform != "darwin" and sys.platform != "linux":
|
||||
if sys.version_info >= (3, 9) or sys.platform != "win32":
|
||||
AF_BLUETOOTH: int
|
||||
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
if sys.platform != "win32" and sys.platform != "darwin" and sys.platform != "linux":
|
||||
# Linux and some BSD support is explicit in the docs
|
||||
# Windows and macOS do not support in practice
|
||||
BTPROTO_HCI: int
|
||||
BTPROTO_L2CAP: int
|
||||
BTPROTO_SCO: int # not in FreeBSD
|
||||
if sys.platform != "darwin":
|
||||
if sys.platform != "darwin" and sys.platform != "linux":
|
||||
if sys.version_info >= (3, 9) or sys.platform != "win32":
|
||||
BTPROTO_RFCOMM: int
|
||||
|
||||
@@ -636,13 +683,14 @@ AF_SNA: int
|
||||
|
||||
if sys.platform != "win32":
|
||||
AF_ROUTE: int
|
||||
|
||||
if sys.platform == "darwin":
|
||||
AF_SYSTEM: int
|
||||
|
||||
if sys.platform != "darwin":
|
||||
AF_IRDA: int
|
||||
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
AF_AAL5: int
|
||||
AF_ASH: int
|
||||
AF_ATMPVC: int
|
||||
AF_ATMSVC: int
|
||||
@@ -661,10 +709,12 @@ if sys.platform != "win32" and sys.platform != "darwin":
|
||||
|
||||
# Miscellaneous undocumented
|
||||
|
||||
if sys.platform != "win32":
|
||||
if sys.platform != "win32" and sys.platform != "linux":
|
||||
LOCAL_PEERCRED: int
|
||||
|
||||
if sys.platform != "win32" and sys.platform != "darwin":
|
||||
# Defined in linux socket.h, but this isn't always present for
|
||||
# some reason.
|
||||
IPX_TYPE: int
|
||||
|
||||
# ===== Classes =====
|
||||
@@ -792,4 +842,4 @@ def if_nameindex() -> list[tuple[int, str]]: ...
|
||||
def if_nametoindex(oname: str, /) -> int: ...
|
||||
def if_indextoname(index: int, /) -> str: ...
|
||||
|
||||
CAPI: object
|
||||
CAPI: CapsuleType
|
||||
|
||||
@@ -44,6 +44,12 @@ def start_new_thread(function: Callable[[Unpack[_Ts]], object], args: tuple[Unpa
|
||||
@overload
|
||||
def start_new_thread(function: Callable[..., object], args: tuple[Any, ...], kwargs: dict[str, Any], /) -> int: ...
|
||||
|
||||
# Obsolete synonym for start_new_thread()
|
||||
@overload
|
||||
def start_new(function: Callable[[Unpack[_Ts]], object], args: tuple[Unpack[_Ts]], /) -> int: ...
|
||||
@overload
|
||||
def start_new(function: Callable[..., object], args: tuple[Any, ...], kwargs: dict[str, Any], /) -> int: ...
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
def interrupt_main(signum: signal.Signals = ..., /) -> None: ...
|
||||
|
||||
@@ -51,7 +57,9 @@ else:
|
||||
def interrupt_main() -> None: ...
|
||||
|
||||
def exit() -> NoReturn: ...
|
||||
def exit_thread() -> NoReturn: ... # Obsolete synonym for exit()
|
||||
def allocate_lock() -> LockType: ...
|
||||
def allocate() -> LockType: ... # Obsolete synonym for allocate_lock()
|
||||
def get_ident() -> int: ...
|
||||
def stack_size(size: int = 0, /) -> int: ...
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from threading import RLock
|
||||
from typing import Any
|
||||
from typing_extensions import Self, TypeAlias
|
||||
from weakref import ReferenceType
|
||||
@@ -8,6 +9,9 @@ _LocalDict: TypeAlias = dict[Any, Any]
|
||||
class _localimpl:
|
||||
key: str
|
||||
dicts: dict[int, tuple[ReferenceType[Any], _LocalDict]]
|
||||
# Keep localargs in sync with the *args, **kwargs annotation on local.__new__
|
||||
localargs: tuple[list[Any], dict[str, Any]]
|
||||
locallock: RLock
|
||||
def get_dict(self) -> _LocalDict: ...
|
||||
def create_dict(self) -> _LocalDict: ...
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ class object:
|
||||
@property
|
||||
def __class__(self) -> type[Self]: ...
|
||||
@__class__.setter
|
||||
def __class__(self, type: type[object], /) -> None: ...
|
||||
def __class__(self, type: type[Self], /) -> None: ...
|
||||
def __init__(self) -> None: ...
|
||||
def __new__(cls) -> Self: ...
|
||||
# N.B. `object.__setattr__` and `object.__delattr__` are heavily special-cased by type checkers.
|
||||
@@ -1963,14 +1963,33 @@ class StopAsyncIteration(Exception):
|
||||
|
||||
class SyntaxError(Exception):
|
||||
msg: str
|
||||
filename: str | None
|
||||
lineno: int | None
|
||||
offset: int | None
|
||||
text: str | None
|
||||
filename: str | None
|
||||
# Errors are displayed differently if this attribute exists on the exception.
|
||||
# The value is always None.
|
||||
print_file_and_line: None
|
||||
if sys.version_info >= (3, 10):
|
||||
end_lineno: int | None
|
||||
end_offset: int | None
|
||||
|
||||
@overload
|
||||
def __init__(self) -> None: ...
|
||||
@overload
|
||||
def __init__(self, msg: object, /) -> None: ...
|
||||
# Second argument is the tuple (filename, lineno, offset, text)
|
||||
@overload
|
||||
def __init__(self, msg: str, info: tuple[str | None, int | None, int | None, str | None], /) -> None: ...
|
||||
if sys.version_info >= (3, 10):
|
||||
# end_lineno and end_offset must both be provided if one is.
|
||||
@overload
|
||||
def __init__(
|
||||
self, msg: str, info: tuple[str | None, int | None, int | None, str | None, int | None, int | None], /
|
||||
) -> None: ...
|
||||
# If you provide more than two arguments, it still creates the SyntaxError, but
|
||||
# the arguments from the info tuple are not parsed. This form is omitted.
|
||||
|
||||
class SystemError(Exception): ...
|
||||
class TypeError(Exception): ...
|
||||
class ValueError(Exception): ...
|
||||
|
||||
@@ -126,6 +126,7 @@ class BZ2File(BaseStream, IO[bytes]):
|
||||
def readline(self, size: SupportsIndex = -1) -> bytes: ... # type: ignore[override]
|
||||
def readinto(self, b: WriteableBuffer) -> int: ...
|
||||
def readlines(self, size: SupportsIndex = -1) -> list[bytes]: ...
|
||||
def peek(self, n: int = 0) -> bytes: ...
|
||||
def seek(self, offset: int, whence: int = 0) -> int: ...
|
||||
def write(self, data: ReadableBuffer) -> int: ...
|
||||
def writelines(self, seq: Iterable[ReadableBuffer]) -> None: ...
|
||||
|
||||
@@ -3,7 +3,7 @@ from _codecs import *
|
||||
from _typeshed import ReadableBuffer
|
||||
from abc import abstractmethod
|
||||
from collections.abc import Callable, Generator, Iterable
|
||||
from typing import Any, BinaryIO, Final, Literal, Protocol, TextIO
|
||||
from typing import Any, BinaryIO, ClassVar, Final, Literal, Protocol, TextIO
|
||||
from typing_extensions import Self
|
||||
|
||||
__all__ = [
|
||||
@@ -202,6 +202,7 @@ class StreamWriter(Codec):
|
||||
def write(self, object: str) -> None: ...
|
||||
def writelines(self, list: Iterable[str]) -> None: ...
|
||||
def reset(self) -> None: ...
|
||||
def seek(self, offset: int, whence: int = 0) -> None: ...
|
||||
def __enter__(self) -> Self: ...
|
||||
def __exit__(self, type: type[BaseException] | None, value: BaseException | None, tb: types.TracebackType | None) -> None: ...
|
||||
def __getattr__(self, name: str, getattr: Callable[[Any, str], Any] = ...) -> Any: ...
|
||||
@@ -209,11 +210,14 @@ class StreamWriter(Codec):
|
||||
class StreamReader(Codec):
|
||||
stream: _ReadableStream
|
||||
errors: str
|
||||
# This is set to str, but some subclasses set to bytes instead.
|
||||
charbuffertype: ClassVar[type] = ...
|
||||
def __init__(self, stream: _ReadableStream, errors: str = "strict") -> None: ...
|
||||
def read(self, size: int = -1, chars: int = -1, firstline: bool = False) -> str: ...
|
||||
def readline(self, size: int | None = None, keepends: bool = True) -> str: ...
|
||||
def readlines(self, sizehint: int | None = None, keepends: bool = True) -> list[str]: ...
|
||||
def reset(self) -> None: ...
|
||||
def seek(self, offset: int, whence: int = 0) -> None: ...
|
||||
def __enter__(self) -> Self: ...
|
||||
def __exit__(self, type: type[BaseException] | None, value: BaseException | None, tb: types.TracebackType | None) -> None: ...
|
||||
def __iter__(self) -> Self: ...
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import sys
|
||||
from _typeshed import StrOrBytesPath, SupportsWrite
|
||||
from _typeshed import MaybeNone, StrOrBytesPath, SupportsWrite
|
||||
from collections.abc import Callable, ItemsView, Iterable, Iterator, Mapping, MutableMapping, Sequence
|
||||
from re import Pattern
|
||||
from typing import Any, ClassVar, Final, Literal, TypeVar, overload
|
||||
@@ -263,11 +263,11 @@ class RawConfigParser(_Parser):
|
||||
) -> _T: ...
|
||||
# This is incompatible with MutableMapping so we ignore the type
|
||||
@overload # type: ignore[override]
|
||||
def get(self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None) -> str | Any: ...
|
||||
def get(self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None) -> str | MaybeNone: ...
|
||||
@overload
|
||||
def get(
|
||||
self, section: str, option: str, *, raw: bool = False, vars: _Section | None = None, fallback: _T
|
||||
) -> str | _T | Any: ...
|
||||
) -> str | _T | MaybeNone: ...
|
||||
@overload
|
||||
def items(self, *, raw: bool = False, vars: _Section | None = None) -> ItemsView[str, SectionProxy]: ...
|
||||
@overload
|
||||
@@ -277,6 +277,8 @@ class RawConfigParser(_Parser):
|
||||
def remove_option(self, section: str, option: str) -> bool: ...
|
||||
def remove_section(self, section: str) -> bool: ...
|
||||
def optionxform(self, optionstr: str) -> str: ...
|
||||
@property
|
||||
def converters(self) -> ConverterMapping: ...
|
||||
|
||||
class ConfigParser(RawConfigParser):
|
||||
# This is incompatible with MutableMapping so we ignore the type
|
||||
@@ -300,28 +302,34 @@ class SectionProxy(MutableMapping[str, str]):
|
||||
def parser(self) -> RawConfigParser: ...
|
||||
@property
|
||||
def name(self) -> str: ...
|
||||
def get( # type: ignore[override]
|
||||
# This is incompatible with MutableMapping so we ignore the type
|
||||
@overload # type: ignore[override]
|
||||
def get(
|
||||
self, option: str, *, raw: bool = False, vars: _Section | None = None, _impl: Any | None = None, **kwargs: Any
|
||||
) -> str | None: ...
|
||||
@overload
|
||||
def get(
|
||||
self,
|
||||
option: str,
|
||||
fallback: str | None = None,
|
||||
fallback: _T,
|
||||
*,
|
||||
raw: bool = False,
|
||||
vars: _Section | None = None,
|
||||
_impl: Any | None = None,
|
||||
**kwargs: Any,
|
||||
) -> str | Any: ... # can be None in RawConfigParser's sections
|
||||
) -> str | _T: ...
|
||||
# These are partially-applied version of the methods with the same names in
|
||||
# RawConfigParser; the stubs should be kept updated together
|
||||
@overload
|
||||
def getint(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> int: ...
|
||||
def getint(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> int | None: ...
|
||||
@overload
|
||||
def getint(self, option: str, fallback: _T = ..., *, raw: bool = ..., vars: _Section | None = ...) -> int | _T: ...
|
||||
@overload
|
||||
def getfloat(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> float: ...
|
||||
def getfloat(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> float | None: ...
|
||||
@overload
|
||||
def getfloat(self, option: str, fallback: _T = ..., *, raw: bool = ..., vars: _Section | None = ...) -> float | _T: ...
|
||||
@overload
|
||||
def getboolean(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> bool: ...
|
||||
def getboolean(self, option: str, *, raw: bool = ..., vars: _Section | None = ...) -> bool | None: ...
|
||||
@overload
|
||||
def getboolean(self, option: str, fallback: _T = ..., *, raw: bool = ..., vars: _Section | None = ...) -> bool | _T: ...
|
||||
# SectionProxy can have arbitrary attributes when custom converters are used
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import abc
|
||||
import sys
|
||||
from _typeshed import FileDescriptorOrPath, Unused
|
||||
from abc import abstractmethod
|
||||
from abc import ABC, abstractmethod
|
||||
from collections.abc import AsyncGenerator, AsyncIterator, Awaitable, Callable, Generator, Iterator
|
||||
from types import TracebackType
|
||||
from typing import IO, Any, Generic, Protocol, TypeVar, overload, runtime_checkable
|
||||
@@ -38,16 +38,22 @@ _P = ParamSpec("_P")
|
||||
_ExitFunc: TypeAlias = Callable[[type[BaseException] | None, BaseException | None, TracebackType | None], bool | None]
|
||||
_CM_EF = TypeVar("_CM_EF", bound=AbstractContextManager[Any, Any] | _ExitFunc)
|
||||
|
||||
# mypy and pyright object to this being both ABC and Protocol.
|
||||
# At runtime it inherits from ABC and is not a Protocol, but it is on the
|
||||
# allowlist for use as a Protocol.
|
||||
@runtime_checkable
|
||||
class AbstractContextManager(Protocol[_T_co, _ExitT_co]):
|
||||
class AbstractContextManager(ABC, Protocol[_T_co, _ExitT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
def __enter__(self) -> _T_co: ...
|
||||
@abstractmethod
|
||||
def __exit__(
|
||||
self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None, /
|
||||
) -> _ExitT_co: ...
|
||||
|
||||
# mypy and pyright object to this being both ABC and Protocol.
|
||||
# At runtime it inherits from ABC and is not a Protocol, but it is on the
|
||||
# allowlist for use as a Protocol.
|
||||
@runtime_checkable
|
||||
class AbstractAsyncContextManager(Protocol[_T_co, _ExitT_co]):
|
||||
class AbstractAsyncContextManager(ABC, Protocol[_T_co, _ExitT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
|
||||
async def __aenter__(self) -> _T_co: ...
|
||||
@abstractmethod
|
||||
async def __aexit__(
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
import sys
|
||||
from typing import Final
|
||||
from typing import Final, NamedTuple, type_check_only
|
||||
|
||||
if sys.platform != "win32":
|
||||
class _Method: ...
|
||||
@type_check_only
|
||||
class _MethodBase(NamedTuple):
|
||||
name: str
|
||||
ident: str | None
|
||||
salt_chars: int
|
||||
total_size: int
|
||||
|
||||
class _Method(_MethodBase): ...
|
||||
METHOD_CRYPT: Final[_Method]
|
||||
METHOD_MD5: Final[_Method]
|
||||
METHOD_SHA256: Final[_Method]
|
||||
|
||||
@@ -8,8 +8,6 @@ from _csv import (
|
||||
__version__ as __version__,
|
||||
_DialectLike,
|
||||
_QuotingType,
|
||||
_reader,
|
||||
_writer,
|
||||
field_size_limit as field_size_limit,
|
||||
get_dialect as get_dialect,
|
||||
list_dialects as list_dialects,
|
||||
@@ -21,6 +19,10 @@ from _csv import (
|
||||
|
||||
if sys.version_info >= (3, 12):
|
||||
from _csv import QUOTE_NOTNULL as QUOTE_NOTNULL, QUOTE_STRINGS as QUOTE_STRINGS
|
||||
if sys.version_info >= (3, 10):
|
||||
from _csv import Reader, Writer
|
||||
else:
|
||||
from _csv import _reader as Reader, _writer as Writer
|
||||
|
||||
from _typeshed import SupportsWrite
|
||||
from collections.abc import Collection, Iterable, Mapping, Sequence
|
||||
@@ -77,7 +79,7 @@ class DictReader(Generic[_T]):
|
||||
fieldnames: Sequence[_T] | None
|
||||
restkey: _T | None
|
||||
restval: str | Any | None
|
||||
reader: _reader
|
||||
reader: Reader
|
||||
dialect: _DialectLike
|
||||
line_num: int
|
||||
@overload
|
||||
@@ -125,7 +127,7 @@ class DictWriter(Generic[_T]):
|
||||
fieldnames: Collection[_T]
|
||||
restval: Any | None
|
||||
extrasaction: Literal["raise", "ignore"]
|
||||
writer: _writer
|
||||
writer: Writer
|
||||
def __init__(
|
||||
self,
|
||||
f: SupportsWrite[str],
|
||||
|
||||
@@ -10,13 +10,11 @@ from _ctypes import (
|
||||
_CanCastTo as _CanCastTo,
|
||||
_CArgObject as _CArgObject,
|
||||
_CData as _CData,
|
||||
_CDataMeta as _CDataMeta,
|
||||
_CDataType as _CDataType,
|
||||
_CField as _CField,
|
||||
_Pointer as _Pointer,
|
||||
_PointerLike as _PointerLike,
|
||||
_SimpleCData as _SimpleCData,
|
||||
_StructUnionBase as _StructUnionBase,
|
||||
_StructUnionMeta as _StructUnionMeta,
|
||||
addressof as addressof,
|
||||
alignment as alignment,
|
||||
byref as byref,
|
||||
@@ -28,7 +26,7 @@ from _ctypes import (
|
||||
)
|
||||
from ctypes._endian import BigEndianStructure as BigEndianStructure, LittleEndianStructure as LittleEndianStructure
|
||||
from typing import Any, ClassVar, Generic, TypeVar
|
||||
from typing_extensions import TypeAlias
|
||||
from typing_extensions import Self, TypeAlias, deprecated
|
||||
|
||||
if sys.platform == "win32":
|
||||
from _ctypes import FormatError as FormatError, get_last_error as get_last_error, set_last_error as set_last_error
|
||||
@@ -41,6 +39,7 @@ if sys.version_info >= (3, 9):
|
||||
|
||||
_T = TypeVar("_T")
|
||||
_DLLT = TypeVar("_DLLT", bound=CDLL)
|
||||
_CT = TypeVar("_CT", bound=_CData)
|
||||
|
||||
DEFAULT_MODE: int
|
||||
|
||||
@@ -48,7 +47,7 @@ class ArgumentError(Exception): ...
|
||||
|
||||
class CDLL:
|
||||
_func_flags_: ClassVar[int]
|
||||
_func_restype_: ClassVar[_CData]
|
||||
_func_restype_: ClassVar[_CDataType]
|
||||
_name: str
|
||||
_handle: int
|
||||
_FuncPtr: type[_FuncPointer]
|
||||
@@ -91,15 +90,21 @@ class _NamedFuncPointer(_FuncPointer):
|
||||
__name__: str
|
||||
|
||||
def CFUNCTYPE(
|
||||
restype: type[_CData] | None, *argtypes: type[_CData], use_errno: bool = ..., use_last_error: bool = ...
|
||||
restype: type[_CData | _CDataType] | None,
|
||||
*argtypes: type[_CData | _CDataType],
|
||||
use_errno: bool = ...,
|
||||
use_last_error: bool = ...,
|
||||
) -> type[_FuncPointer]: ...
|
||||
|
||||
if sys.platform == "win32":
|
||||
def WINFUNCTYPE(
|
||||
restype: type[_CData] | None, *argtypes: type[_CData], use_errno: bool = ..., use_last_error: bool = ...
|
||||
restype: type[_CData | _CDataType] | None,
|
||||
*argtypes: type[_CData | _CDataType],
|
||||
use_errno: bool = ...,
|
||||
use_last_error: bool = ...,
|
||||
) -> type[_FuncPointer]: ...
|
||||
|
||||
def PYFUNCTYPE(restype: type[_CData] | None, *argtypes: type[_CData]) -> type[_FuncPointer]: ...
|
||||
def PYFUNCTYPE(restype: type[_CData | _CDataType] | None, *argtypes: type[_CData | _CDataType]) -> type[_FuncPointer]: ...
|
||||
|
||||
# Any type that can be implicitly converted to c_void_p when passed as a C function argument.
|
||||
# (bytes is not included here, see below.)
|
||||
@@ -112,12 +117,17 @@ _CVoidConstPLike: TypeAlias = _CVoidPLike | bytes
|
||||
|
||||
_CastT = TypeVar("_CastT", bound=_CanCastTo)
|
||||
|
||||
def cast(obj: _CData | _CArgObject | int, typ: type[_CastT]) -> _CastT: ...
|
||||
def cast(obj: _CData | _CDataType | _CArgObject | int, typ: type[_CastT]) -> _CastT: ...
|
||||
def create_string_buffer(init: int | bytes, size: int | None = None) -> Array[c_char]: ...
|
||||
|
||||
c_buffer = create_string_buffer
|
||||
|
||||
def create_unicode_buffer(init: int | str, size: int | None = None) -> Array[c_wchar]: ...
|
||||
@deprecated("Deprecated in Python 3.13; removal scheduled for Python 3.15")
|
||||
def SetPointerType(
|
||||
pointer: type[_Pointer[Any]], cls: Any # noqa: F811 # Redefinition of unused `pointer` from line 22
|
||||
) -> None: ...
|
||||
def ARRAY(typ: _CT, len: int) -> Array[_CT]: ... # Soft Deprecated, no plans to remove
|
||||
|
||||
if sys.platform == "win32":
|
||||
def DllCanUnloadNow() -> int: ...
|
||||
@@ -126,12 +136,12 @@ if sys.platform == "win32":
|
||||
|
||||
def memmove(dst: _CVoidPLike, src: _CVoidConstPLike, count: int) -> int: ...
|
||||
def memset(dst: _CVoidPLike, c: int, count: int) -> int: ...
|
||||
def string_at(address: _CVoidConstPLike, size: int = -1) -> bytes: ...
|
||||
def string_at(ptr: _CVoidConstPLike, size: int = -1) -> bytes: ...
|
||||
|
||||
if sys.platform == "win32":
|
||||
def WinError(code: int | None = None, descr: str | None = None) -> OSError: ...
|
||||
|
||||
def wstring_at(address: _CVoidConstPLike, size: int = -1) -> str: ...
|
||||
def wstring_at(ptr: _CVoidConstPLike, size: int = -1) -> str: ...
|
||||
|
||||
class c_byte(_SimpleCData[int]): ...
|
||||
|
||||
@@ -140,6 +150,8 @@ class c_char(_SimpleCData[bytes]):
|
||||
|
||||
class c_char_p(_PointerLike, _SimpleCData[bytes | None]):
|
||||
def __init__(self, value: int | bytes | None = ...) -> None: ...
|
||||
@classmethod
|
||||
def from_param(cls, value: Any, /) -> Self | _CArgObject: ...
|
||||
|
||||
class c_double(_SimpleCData[float]): ...
|
||||
class c_longdouble(_SimpleCData[float]): ... # can be an alias for c_double
|
||||
@@ -155,7 +167,13 @@ class c_uint(_SimpleCData[int]): ... # can be an alias for c_ulong
|
||||
class c_ulong(_SimpleCData[int]): ...
|
||||
class c_ulonglong(_SimpleCData[int]): ... # can be an alias for c_ulong
|
||||
class c_ushort(_SimpleCData[int]): ...
|
||||
class c_void_p(_PointerLike, _SimpleCData[int | None]): ...
|
||||
|
||||
class c_void_p(_PointerLike, _SimpleCData[int | None]):
|
||||
@classmethod
|
||||
def from_param(cls, value: Any, /) -> Self | _CArgObject: ...
|
||||
|
||||
c_voidp = c_void_p # backwards compatibility (to a bug)
|
||||
|
||||
class c_wchar(_SimpleCData[str]): ...
|
||||
|
||||
c_int8 = c_byte
|
||||
@@ -174,6 +192,8 @@ class c_uint64(_SimpleCData[int]): ...
|
||||
|
||||
class c_wchar_p(_PointerLike, _SimpleCData[str | None]):
|
||||
def __init__(self, value: int | str | None = ...) -> None: ...
|
||||
@classmethod
|
||||
def from_param(cls, value: Any, /) -> Self | _CArgObject: ...
|
||||
|
||||
class c_bool(_SimpleCData[bool]):
|
||||
def __init__(self, value: bool = ...) -> None: ...
|
||||
|
||||
1
crates/red_knot_vendored/vendor/typeshed/stdlib/ctypes/macholib/__init__.pyi
vendored
Normal file
1
crates/red_knot_vendored/vendor/typeshed/stdlib/ctypes/macholib/__init__.pyi
vendored
Normal file
@@ -0,0 +1 @@
|
||||
__version__: str
|
||||
8
crates/red_knot_vendored/vendor/typeshed/stdlib/ctypes/macholib/dyld.pyi
vendored
Normal file
8
crates/red_knot_vendored/vendor/typeshed/stdlib/ctypes/macholib/dyld.pyi
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
from collections.abc import Mapping
|
||||
from ctypes.macholib.dylib import dylib_info as dylib_info
|
||||
from ctypes.macholib.framework import framework_info as framework_info
|
||||
|
||||
__all__ = ["dyld_find", "framework_find", "framework_info", "dylib_info"]
|
||||
|
||||
def dyld_find(name: str, executable_path: str | None = None, env: Mapping[str, str] | None = None) -> str: ...
|
||||
def framework_find(fn: str, executable_path: str | None = None, env: Mapping[str, str] | None = None) -> str: ...
|
||||
14
crates/red_knot_vendored/vendor/typeshed/stdlib/ctypes/macholib/dylib.pyi
vendored
Normal file
14
crates/red_knot_vendored/vendor/typeshed/stdlib/ctypes/macholib/dylib.pyi
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
from typing import TypedDict, type_check_only
|
||||
|
||||
__all__ = ["dylib_info"]
|
||||
|
||||
# Actual result is produced by re.match.groupdict()
|
||||
@type_check_only
|
||||
class _DylibInfo(TypedDict):
|
||||
location: str
|
||||
name: str
|
||||
shortname: str
|
||||
version: str | None
|
||||
suffix: str | None
|
||||
|
||||
def dylib_info(filename: str) -> _DylibInfo | None: ...
|
||||
14
crates/red_knot_vendored/vendor/typeshed/stdlib/ctypes/macholib/framework.pyi
vendored
Normal file
14
crates/red_knot_vendored/vendor/typeshed/stdlib/ctypes/macholib/framework.pyi
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
from typing import TypedDict, type_check_only
|
||||
|
||||
__all__ = ["framework_info"]
|
||||
|
||||
# Actual result is produced by re.match.groupdict()
|
||||
@type_check_only
|
||||
class _FrameworkInfo(TypedDict):
|
||||
location: str
|
||||
name: str
|
||||
shortname: str
|
||||
version: str | None
|
||||
suffix: str | None
|
||||
|
||||
def framework_info(filename: str) -> _FrameworkInfo | None: ...
|
||||
@@ -4,3 +4,5 @@ def find_library(name: str) -> str | None: ...
|
||||
|
||||
if sys.platform == "win32":
|
||||
def find_msvcrt() -> str | None: ...
|
||||
|
||||
def test() -> None: ...
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from _ctypes import _CArgObject, _CField
|
||||
from ctypes import (
|
||||
Array,
|
||||
Structure,
|
||||
_CField,
|
||||
_Pointer,
|
||||
_SimpleCData,
|
||||
c_byte,
|
||||
@@ -21,8 +21,8 @@ from ctypes import (
|
||||
c_wchar,
|
||||
c_wchar_p,
|
||||
)
|
||||
from typing import TypeVar
|
||||
from typing_extensions import TypeAlias
|
||||
from typing import Any, TypeVar
|
||||
from typing_extensions import Self, TypeAlias
|
||||
|
||||
BYTE = c_byte
|
||||
WORD = c_ushort
|
||||
@@ -241,10 +241,16 @@ LPBYTE = PBYTE
|
||||
PBOOLEAN = PBYTE
|
||||
|
||||
# LP_c_char
|
||||
class PCHAR(_Pointer[CHAR]): ...
|
||||
class PCHAR(_Pointer[CHAR]):
|
||||
# this is inherited from ctypes.c_char_p, kind of.
|
||||
@classmethod
|
||||
def from_param(cls, value: Any, /) -> Self | _CArgObject: ...
|
||||
|
||||
# LP_c_wchar
|
||||
class PWCHAR(_Pointer[WCHAR]): ...
|
||||
class PWCHAR(_Pointer[WCHAR]):
|
||||
# inherited from ctypes.c_wchar_p, kind of
|
||||
@classmethod
|
||||
def from_param(cls, value: Any, /) -> Self | _CArgObject: ...
|
||||
|
||||
# LP_c_void_p
|
||||
class PHANDLE(_Pointer[HANDLE]): ...
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import sys
|
||||
from _curses import *
|
||||
from _curses import window as window
|
||||
from _typeshed import structseq
|
||||
from collections.abc import Callable
|
||||
from typing import TypeVar
|
||||
from typing import Final, TypeVar, final, type_check_only
|
||||
from typing_extensions import Concatenate, ParamSpec
|
||||
|
||||
# NOTE: The _curses module is ordinarily only available on Unix, but the
|
||||
@@ -25,3 +27,19 @@ def wrapper(func: Callable[Concatenate[window, _P], _T], /, *arg: _P.args, **kwd
|
||||
# it was mapped to the name 'window' in 3.8.
|
||||
# Kept here as a legacy alias in case any third-party code is relying on it.
|
||||
_CursesWindow = window
|
||||
|
||||
# At runtime this class is unexposed and calls itself curses.ncurses_version.
|
||||
# That name would conflict with the actual curses.ncurses_version, which is
|
||||
# an instance of this class.
|
||||
@final
|
||||
@type_check_only
|
||||
class _ncurses_version(structseq[int], tuple[int, int, int]):
|
||||
if sys.version_info >= (3, 10):
|
||||
__match_args__: Final = ("major", "minor", "patch")
|
||||
|
||||
@property
|
||||
def major(self) -> int: ...
|
||||
@property
|
||||
def minor(self) -> int: ...
|
||||
@property
|
||||
def patch(self) -> int: ...
|
||||
|
||||
@@ -1,22 +1 @@
|
||||
from _curses import window
|
||||
|
||||
version: str
|
||||
|
||||
class _Curses_Panel: # type is <class '_curses_panel.curses panel'> (note the space in the class name)
|
||||
def above(self) -> _Curses_Panel: ...
|
||||
def below(self) -> _Curses_Panel: ...
|
||||
def bottom(self) -> None: ...
|
||||
def hidden(self) -> bool: ...
|
||||
def hide(self) -> None: ...
|
||||
def move(self, y: int, x: int) -> None: ...
|
||||
def replace(self, win: window) -> None: ...
|
||||
def set_userptr(self, obj: object) -> None: ...
|
||||
def show(self) -> None: ...
|
||||
def top(self) -> None: ...
|
||||
def userptr(self) -> object: ...
|
||||
def window(self) -> window: ...
|
||||
|
||||
def bottom_panel() -> _Curses_Panel: ...
|
||||
def new_panel(win: window, /) -> _Curses_Panel: ...
|
||||
def top_panel() -> _Curses_Panel: ...
|
||||
def update_panels() -> _Curses_Panel: ...
|
||||
from _curses_panel import *
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import sys
|
||||
from abc import abstractmethod
|
||||
from time import struct_time
|
||||
from typing import ClassVar, Final, NamedTuple, NoReturn, SupportsIndex, final, overload
|
||||
from typing_extensions import Self, TypeAlias, deprecated
|
||||
from typing import ClassVar, Final, NoReturn, SupportsIndex, final, overload, type_check_only
|
||||
from typing_extensions import CapsuleType, Self, TypeAlias, deprecated
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
__all__ = ("date", "datetime", "time", "timedelta", "timezone", "tzinfo", "MINYEAR", "MAXYEAR", "UTC")
|
||||
@@ -40,10 +40,17 @@ if sys.version_info >= (3, 11):
|
||||
UTC: timezone
|
||||
|
||||
if sys.version_info >= (3, 9):
|
||||
class _IsoCalendarDate(NamedTuple):
|
||||
year: int
|
||||
week: int
|
||||
weekday: int
|
||||
# This class calls itself datetime.IsoCalendarDate. It's neither
|
||||
# NamedTuple nor structseq.
|
||||
@final
|
||||
@type_check_only
|
||||
class _IsoCalendarDate(tuple[int, int, int]):
|
||||
@property
|
||||
def year(self) -> int: ...
|
||||
@property
|
||||
def week(self) -> int: ...
|
||||
@property
|
||||
def weekday(self) -> int: ...
|
||||
|
||||
class date:
|
||||
min: ClassVar[date]
|
||||
@@ -325,3 +332,5 @@ class datetime(date):
|
||||
def __sub__(self, value: Self, /) -> timedelta: ...
|
||||
@overload
|
||||
def __sub__(self, value: timedelta, /) -> Self: ...
|
||||
|
||||
datetime_CAPI: CapsuleType
|
||||
|
||||
@@ -2,7 +2,7 @@ import sys
|
||||
from _typeshed import StrOrBytesPath
|
||||
from collections.abc import Iterator, MutableMapping
|
||||
from types import TracebackType
|
||||
from typing import Literal
|
||||
from typing import Literal, type_check_only
|
||||
from typing_extensions import Self, TypeAlias
|
||||
|
||||
__all__ = ["open", "whichdb", "error"]
|
||||
@@ -89,6 +89,8 @@ class _Database(MutableMapping[_KeyType, bytes]):
|
||||
self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
|
||||
) -> None: ...
|
||||
|
||||
# This class is not exposed. It calls itself dbm.error.
|
||||
@type_check_only
|
||||
class _error(Exception): ...
|
||||
|
||||
error: tuple[type[_error], type[OSError]]
|
||||
|
||||
@@ -24,7 +24,8 @@ from _decimal import (
|
||||
setcontext as setcontext,
|
||||
)
|
||||
from collections.abc import Container, Sequence
|
||||
from typing import Any, ClassVar, Literal, NamedTuple, overload
|
||||
from types import TracebackType
|
||||
from typing import Any, ClassVar, Literal, NamedTuple, final, overload, type_check_only
|
||||
from typing_extensions import Self, TypeAlias
|
||||
|
||||
_Decimal: TypeAlias = Decimal | int
|
||||
@@ -35,6 +36,14 @@ _TrapType: TypeAlias = type[DecimalException]
|
||||
# At runtime, these classes are implemented in C as part of "_decimal".
|
||||
# However, they consider themselves to live in "decimal", so we'll put them here.
|
||||
|
||||
# This type isn't exposed at runtime. It calls itself decimal.ContextManager
|
||||
@final
|
||||
@type_check_only
|
||||
class _ContextManager:
|
||||
def __init__(self, new_context: Context) -> None: ...
|
||||
def __enter__(self) -> Context: ...
|
||||
def __exit__(self, t: type[BaseException] | None, v: BaseException | None, tb: TracebackType | None) -> None: ...
|
||||
|
||||
class DecimalTuple(NamedTuple):
|
||||
sign: int
|
||||
digits: tuple[int, ...]
|
||||
|
||||
@@ -165,7 +165,7 @@ class CCompiler:
|
||||
def execute(
|
||||
self, func: Callable[[Unpack[_Ts]], Unused], args: tuple[Unpack[_Ts]], msg: str | None = None, level: int = 1
|
||||
) -> None: ...
|
||||
def spawn(self, cmd: list[str]) -> None: ...
|
||||
def spawn(self, cmd: Iterable[str]) -> None: ...
|
||||
def mkpath(self, name: str, mode: int = 0o777) -> None: ...
|
||||
@overload
|
||||
def move_file(self, src: StrPath, dst: _StrPathT) -> _StrPathT | str: ...
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
from collections.abc import Iterable
|
||||
from typing import Literal
|
||||
|
||||
def spawn(
|
||||
cmd: list[str], search_path: bool | Literal[0, 1] = 1, verbose: bool | Literal[0, 1] = 0, dry_run: bool | Literal[0, 1] = 0
|
||||
cmd: Iterable[str],
|
||||
search_path: bool | Literal[0, 1] = 1,
|
||||
verbose: bool | Literal[0, 1] = 0,
|
||||
dry_run: bool | Literal[0, 1] = 0,
|
||||
) -> None: ...
|
||||
def find_executable(executable: str, path: str | None = None) -> str | None: ...
|
||||
|
||||
@@ -3,7 +3,7 @@ import types
|
||||
import unittest
|
||||
from _typeshed import ExcInfo
|
||||
from collections.abc import Callable
|
||||
from typing import Any, ClassVar, NamedTuple
|
||||
from typing import Any, NamedTuple, type_check_only
|
||||
from typing_extensions import Self, TypeAlias
|
||||
|
||||
__all__ = [
|
||||
@@ -42,17 +42,15 @@ __all__ = [
|
||||
"debug",
|
||||
]
|
||||
|
||||
# MyPy errors on conditionals within named tuples.
|
||||
|
||||
if sys.version_info >= (3, 13):
|
||||
class TestResults(NamedTuple):
|
||||
def __new__(cls, failed: int, attempted: int, *, skipped: int = 0) -> Self: ... # type: ignore[misc]
|
||||
skipped: int
|
||||
@type_check_only
|
||||
class _TestResultsBase(NamedTuple):
|
||||
failed: int
|
||||
attempted: int
|
||||
_fields: ClassVar = ("failed", "attempted") # type: ignore[misc]
|
||||
__match_args__ = ("failed", "attempted") # type: ignore[misc]
|
||||
__doc__: None # type: ignore[misc]
|
||||
|
||||
class TestResults(_TestResultsBase):
|
||||
def __new__(cls, failed: int, attempted: int, *, skipped: int = 0) -> Self: ...
|
||||
skipped: int
|
||||
|
||||
else:
|
||||
class TestResults(NamedTuple):
|
||||
|
||||
@@ -4,6 +4,29 @@ from email.policy import Policy
|
||||
from typing import IO
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
# At runtime, listing submodules in __all__ without them being imported is
|
||||
# valid, and causes them to be included in a star import. See #6523
|
||||
|
||||
__all__ = [ # noqa: F822 # Undefined names in __all__
|
||||
"base64mime", # pyright: ignore[reportUnsupportedDunderAll]
|
||||
"charset", # pyright: ignore[reportUnsupportedDunderAll]
|
||||
"encoders", # pyright: ignore[reportUnsupportedDunderAll]
|
||||
"errors", # pyright: ignore[reportUnsupportedDunderAll]
|
||||
"feedparser", # pyright: ignore[reportUnsupportedDunderAll]
|
||||
"generator", # pyright: ignore[reportUnsupportedDunderAll]
|
||||
"header", # pyright: ignore[reportUnsupportedDunderAll]
|
||||
"iterators", # pyright: ignore[reportUnsupportedDunderAll]
|
||||
"message", # pyright: ignore[reportUnsupportedDunderAll]
|
||||
"message_from_file",
|
||||
"message_from_binary_file",
|
||||
"message_from_string",
|
||||
"message_from_bytes",
|
||||
"mime", # pyright: ignore[reportUnsupportedDunderAll]
|
||||
"parser", # pyright: ignore[reportUnsupportedDunderAll]
|
||||
"quoprimime", # pyright: ignore[reportUnsupportedDunderAll]
|
||||
"utils", # pyright: ignore[reportUnsupportedDunderAll]
|
||||
]
|
||||
|
||||
# Definitions imported by multiple submodules in typeshed
|
||||
_ParamType: TypeAlias = str | tuple[str | None, str | None, str] # noqa: Y047
|
||||
_ParamsType: TypeAlias = str | None | tuple[str, str | None, str] # noqa: Y047
|
||||
@@ -12,18 +35,3 @@ def message_from_string(s: str, _class: Callable[[], Message] = ..., *, policy:
|
||||
def message_from_bytes(s: bytes | bytearray, _class: Callable[[], Message] = ..., *, policy: Policy = ...) -> Message: ...
|
||||
def message_from_file(fp: IO[str], _class: Callable[[], Message] = ..., *, policy: Policy = ...) -> Message: ...
|
||||
def message_from_binary_file(fp: IO[bytes], _class: Callable[[], Message] = ..., *, policy: Policy = ...) -> Message: ...
|
||||
|
||||
# Names in __all__ with no definition:
|
||||
# base64mime
|
||||
# charset
|
||||
# encoders
|
||||
# errors
|
||||
# feedparser
|
||||
# generator
|
||||
# header
|
||||
# iterators
|
||||
# message
|
||||
# mime
|
||||
# parser
|
||||
# quoprimime
|
||||
# utils
|
||||
|
||||
@@ -5,6 +5,8 @@ from email.message import Message
|
||||
from typing import Generic, Protocol, TypeVar, type_check_only
|
||||
from typing_extensions import Self
|
||||
|
||||
__all__ = ["Policy", "Compat32", "compat32"]
|
||||
|
||||
_MessageT = TypeVar("_MessageT", bound=Message, default=Message)
|
||||
|
||||
@type_check_only
|
||||
|
||||
1
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/aliases.pyi
vendored
Normal file
1
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/aliases.pyi
vendored
Normal file
@@ -0,0 +1 @@
|
||||
aliases: dict[str, str]
|
||||
30
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/ascii.pyi
vendored
Normal file
30
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/ascii.pyi
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
import codecs
|
||||
from _typeshed import ReadableBuffer
|
||||
|
||||
class Codec(codecs.Codec):
|
||||
# At runtime, this is codecs.ascii_encode
|
||||
@staticmethod
|
||||
def encode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ...
|
||||
# At runtime, this is codecs.ascii_decode
|
||||
@staticmethod
|
||||
def decode(data: ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ...
|
||||
|
||||
class IncrementalEncoder(codecs.IncrementalEncoder):
|
||||
def encode(self, input: str, final: bool = False) -> bytes: ...
|
||||
|
||||
class IncrementalDecoder(codecs.IncrementalDecoder):
|
||||
def decode(self, input: ReadableBuffer, final: bool = False) -> str: ...
|
||||
|
||||
class StreamWriter(Codec, codecs.StreamWriter): ...
|
||||
class StreamReader(Codec, codecs.StreamReader): ...
|
||||
|
||||
# Note: encode being a decode function and decode being an encode function is accurate to runtime.
|
||||
class StreamConverter(StreamWriter, StreamReader): # type: ignore[misc] # incompatible methods in base classes
|
||||
# At runtime, this is codecs.ascii_decode
|
||||
@staticmethod
|
||||
def encode(data: ReadableBuffer, errors: str | None = None, /) -> tuple[str, int]: ... # type: ignore[override]
|
||||
# At runtime, this is codecs.ascii_encode
|
||||
@staticmethod
|
||||
def decode(str: str, errors: str | None = None, /) -> tuple[bytes, int]: ... # type: ignore[override]
|
||||
|
||||
def getregentry() -> codecs.CodecInfo: ...
|
||||
26
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/base64_codec.pyi
vendored
Normal file
26
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/base64_codec.pyi
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
import codecs
|
||||
from _typeshed import ReadableBuffer
|
||||
from typing import ClassVar
|
||||
|
||||
# This codec is bytes to bytes.
|
||||
|
||||
def base64_encode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ...
|
||||
def base64_decode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ...
|
||||
|
||||
class Codec(codecs.Codec):
|
||||
def encode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override]
|
||||
def decode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override]
|
||||
|
||||
class IncrementalEncoder(codecs.IncrementalEncoder):
|
||||
def encode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override]
|
||||
|
||||
class IncrementalDecoder(codecs.IncrementalDecoder):
|
||||
def decode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override]
|
||||
|
||||
class StreamWriter(Codec, codecs.StreamWriter):
|
||||
charbuffertype: ClassVar[type] = ...
|
||||
|
||||
class StreamReader(Codec, codecs.StreamReader):
|
||||
charbuffertype: ClassVar[type] = ...
|
||||
|
||||
def getregentry() -> codecs.CodecInfo: ...
|
||||
23
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/big5.pyi
vendored
Normal file
23
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/big5.pyi
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
import _multibytecodec as mbc
|
||||
import codecs
|
||||
from typing import ClassVar
|
||||
|
||||
codec: mbc._MultibyteCodec
|
||||
|
||||
class Codec(codecs.Codec):
|
||||
encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType]
|
||||
decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType]
|
||||
|
||||
class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc]
|
||||
codec: ClassVar[mbc._MultibyteCodec] = ...
|
||||
|
||||
class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder):
|
||||
codec: ClassVar[mbc._MultibyteCodec] = ...
|
||||
|
||||
class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc]
|
||||
codec: ClassVar[mbc._MultibyteCodec] = ...
|
||||
|
||||
class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter):
|
||||
codec: ClassVar[mbc._MultibyteCodec] = ...
|
||||
|
||||
def getregentry() -> codecs.CodecInfo: ...
|
||||
23
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/big5hkscs.pyi
vendored
Normal file
23
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/big5hkscs.pyi
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
import _multibytecodec as mbc
|
||||
import codecs
|
||||
from typing import ClassVar
|
||||
|
||||
codec: mbc._MultibyteCodec
|
||||
|
||||
class Codec(codecs.Codec):
|
||||
encode = codec.encode # type: ignore[assignment] # pyright: ignore[reportAssignmentType]
|
||||
decode = codec.decode # type: ignore[assignment] # pyright: ignore[reportAssignmentType]
|
||||
|
||||
class IncrementalEncoder(mbc.MultibyteIncrementalEncoder, codecs.IncrementalEncoder): # type: ignore[misc]
|
||||
codec: ClassVar[mbc._MultibyteCodec] = ...
|
||||
|
||||
class IncrementalDecoder(mbc.MultibyteIncrementalDecoder, codecs.IncrementalDecoder):
|
||||
codec: ClassVar[mbc._MultibyteCodec] = ...
|
||||
|
||||
class StreamReader(Codec, mbc.MultibyteStreamReader, codecs.StreamReader): # type: ignore[misc]
|
||||
codec: ClassVar[mbc._MultibyteCodec] = ...
|
||||
|
||||
class StreamWriter(Codec, mbc.MultibyteStreamWriter, codecs.StreamWriter):
|
||||
codec: ClassVar[mbc._MultibyteCodec] = ...
|
||||
|
||||
def getregentry() -> codecs.CodecInfo: ...
|
||||
26
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/bz2_codec.pyi
vendored
Normal file
26
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/bz2_codec.pyi
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
import codecs
|
||||
from _typeshed import ReadableBuffer
|
||||
from typing import ClassVar
|
||||
|
||||
# This codec is bytes to bytes.
|
||||
|
||||
def bz2_encode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ...
|
||||
def bz2_decode(input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ...
|
||||
|
||||
class Codec(codecs.Codec):
|
||||
def encode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override]
|
||||
def decode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override]
|
||||
|
||||
class IncrementalEncoder(codecs.IncrementalEncoder):
|
||||
def encode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override]
|
||||
|
||||
class IncrementalDecoder(codecs.IncrementalDecoder):
|
||||
def decode(self, input: ReadableBuffer, final: bool = False) -> bytes: ... # type: ignore[override]
|
||||
|
||||
class StreamWriter(Codec, codecs.StreamWriter):
|
||||
charbuffertype: ClassVar[type] = ...
|
||||
|
||||
class StreamReader(Codec, codecs.StreamReader):
|
||||
charbuffertype: ClassVar[type] = ...
|
||||
|
||||
def getregentry() -> codecs.CodecInfo: ...
|
||||
33
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/charmap.pyi
vendored
Normal file
33
crates/red_knot_vendored/vendor/typeshed/stdlib/encodings/charmap.pyi
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
import codecs
|
||||
from _codecs import _CharMap
|
||||
from _typeshed import ReadableBuffer
|
||||
|
||||
class Codec(codecs.Codec):
|
||||
# At runtime, this is codecs.charmap_encode
|
||||
@staticmethod
|
||||
def encode(str: str, errors: str | None = None, mapping: _CharMap | None = None, /) -> tuple[bytes, int]: ...
|
||||
# At runtime, this is codecs.charmap_decode
|
||||
@staticmethod
|
||||
def decode(data: ReadableBuffer, errors: str | None = None, mapping: _CharMap | None = None, /) -> tuple[str, int]: ...
|
||||
|
||||
class IncrementalEncoder(codecs.IncrementalEncoder):
|
||||
mapping: _CharMap | None
|
||||
def __init__(self, errors: str = "strict", mapping: _CharMap | None = None) -> None: ...
|
||||
def encode(self, input: str, final: bool = False) -> bytes: ...
|
||||
|
||||
class IncrementalDecoder(codecs.IncrementalDecoder):
|
||||
mapping: _CharMap | None
|
||||
def __init__(self, errors: str = "strict", mapping: _CharMap | None = None) -> None: ...
|
||||
def decode(self, input: ReadableBuffer, final: bool = False) -> str: ...
|
||||
|
||||
class StreamWriter(Codec, codecs.StreamWriter):
|
||||
mapping: _CharMap | None
|
||||
def __init__(self, stream: codecs._WritableStream, errors: str = "strict", mapping: _CharMap | None = None) -> None: ...
|
||||
def encode(self, input: str, errors: str = "strict") -> tuple[bytes, int]: ... # type: ignore[override]
|
||||
|
||||
class StreamReader(Codec, codecs.StreamReader):
|
||||
mapping: _CharMap | None
|
||||
def __init__(self, stream: codecs._ReadableStream, errors: str = "strict", mapping: _CharMap | None = None) -> None: ...
|
||||
def decode(self, input: ReadableBuffer, errors: str = "strict") -> tuple[str, int]: ... # type: ignore[override]
|
||||
|
||||
def getregentry() -> codecs.CodecInfo: ...
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user