Compare commits
32 Commits
zb/cache-n
...
dcreager/s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
832131f382 | ||
|
|
7da9a8ebcf | ||
|
|
10c910b5ac | ||
|
|
dc3e36fb06 | ||
|
|
42aabc8b99 | ||
|
|
6b3de1517a | ||
|
|
f3dea6e5c9 | ||
|
|
24dd149e03 | ||
|
|
b8d527ff46 | ||
|
|
e63cf978ae | ||
|
|
3dab4ff8ad | ||
|
|
24580e2ee8 | ||
|
|
3d3af6f7c8 | ||
|
|
7cc34c081a | ||
|
|
7a95013f56 | ||
|
|
2395954d9a | ||
|
|
670bd01fb5 | ||
|
|
eae5c685f8 | ||
|
|
994f05f3ca | ||
|
|
8dcecf323b | ||
|
|
b12c94e411 | ||
|
|
a9c3ea9674 | ||
|
|
704c57f491 | ||
|
|
7a27662eca | ||
|
|
ce2490ee93 | ||
|
|
92a2f2c992 | ||
|
|
11b551c2be | ||
|
|
b85c0190c5 | ||
|
|
46a4bfc478 | ||
|
|
0c53395917 | ||
|
|
8464aca795 | ||
|
|
e1439beab2 |
28
.github/workflows/ci.yaml
vendored
28
.github/workflows/ci.yaml
vendored
@@ -281,11 +281,11 @@ jobs:
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@b9c5db3aef04caffaf95a1d03931de10fb2a140f # v2.65.1
|
||||
uses: taiki-e/install-action@28a9d316db64b78a951f3f8587a5d08cc97ad8eb # v2.65.6
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Install cargo insta"
|
||||
uses: taiki-e/install-action@b9c5db3aef04caffaf95a1d03931de10fb2a140f # v2.65.1
|
||||
uses: taiki-e/install-action@28a9d316db64b78a951f3f8587a5d08cc97ad8eb # v2.65.6
|
||||
with:
|
||||
tool: cargo-insta
|
||||
- name: "Install uv"
|
||||
@@ -343,7 +343,7 @@ jobs:
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@b9c5db3aef04caffaf95a1d03931de10fb2a140f # v2.65.1
|
||||
uses: taiki-e/install-action@28a9d316db64b78a951f3f8587a5d08cc97ad8eb # v2.65.6
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Install uv"
|
||||
@@ -376,7 +376,7 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@b9c5db3aef04caffaf95a1d03931de10fb2a140f # v2.65.1
|
||||
uses: taiki-e/install-action@28a9d316db64b78a951f3f8587a5d08cc97ad8eb # v2.65.6
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Install uv"
|
||||
@@ -468,7 +468,7 @@ jobs:
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
||||
- name: "Install cargo-binstall"
|
||||
uses: cargo-bins/cargo-binstall@4a9028576ed64318f7b24193a62695e96dcbe015 # v1.16.5
|
||||
uses: cargo-bins/cargo-binstall@80aaafe04903087c333980fa2686259ddd34b2d9 # v1.16.6
|
||||
- name: "Install cargo-fuzz"
|
||||
# Download the latest version from quick install and not the github releases because github releases only has MUSL targets.
|
||||
run: cargo binstall cargo-fuzz --force --disable-strategies crate-meta-data --no-confirm
|
||||
@@ -713,7 +713,7 @@ jobs:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: cargo-bins/cargo-binstall@4a9028576ed64318f7b24193a62695e96dcbe015 # v1.16.5
|
||||
- uses: cargo-bins/cargo-binstall@80aaafe04903087c333980fa2686259ddd34b2d9 # v1.16.6
|
||||
- run: cargo binstall --no-confirm cargo-shear
|
||||
- run: cargo shear
|
||||
|
||||
@@ -972,7 +972,7 @@ jobs:
|
||||
run: rustup show
|
||||
|
||||
- name: "Install codspeed"
|
||||
uses: taiki-e/install-action@b9c5db3aef04caffaf95a1d03931de10fb2a140f # v2.65.1
|
||||
uses: taiki-e/install-action@28a9d316db64b78a951f3f8587a5d08cc97ad8eb # v2.65.6
|
||||
with:
|
||||
tool: cargo-codspeed
|
||||
|
||||
@@ -980,7 +980,7 @@ jobs:
|
||||
run: cargo codspeed build --features "codspeed,ruff_instrumented" --profile profiling --no-default-features -p ruff_benchmark --bench formatter --bench lexer --bench linter --bench parser
|
||||
|
||||
- name: "Run benchmarks"
|
||||
uses: CodSpeedHQ/action@346a2d8a8d9d38909abd0bc3d23f773110f076ad # v4.4.1
|
||||
uses: CodSpeedHQ/action@972e3437949c89e1357ebd1a2dbc852fcbc57245 # v4.5.1
|
||||
with:
|
||||
mode: simulation
|
||||
run: cargo codspeed run
|
||||
@@ -1011,7 +1011,7 @@ jobs:
|
||||
run: rustup show
|
||||
|
||||
- name: "Install codspeed"
|
||||
uses: taiki-e/install-action@b9c5db3aef04caffaf95a1d03931de10fb2a140f # v2.65.1
|
||||
uses: taiki-e/install-action@28a9d316db64b78a951f3f8587a5d08cc97ad8eb # v2.65.6
|
||||
with:
|
||||
tool: cargo-codspeed
|
||||
|
||||
@@ -1047,7 +1047,7 @@ jobs:
|
||||
- uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6
|
||||
|
||||
- name: "Install codspeed"
|
||||
uses: taiki-e/install-action@b9c5db3aef04caffaf95a1d03931de10fb2a140f # v2.65.1
|
||||
uses: taiki-e/install-action@28a9d316db64b78a951f3f8587a5d08cc97ad8eb # v2.65.6
|
||||
with:
|
||||
tool: cargo-codspeed
|
||||
|
||||
@@ -1061,7 +1061,7 @@ jobs:
|
||||
run: chmod +x target/codspeed/simulation/ruff_benchmark/ty
|
||||
|
||||
- name: "Run benchmarks"
|
||||
uses: CodSpeedHQ/action@346a2d8a8d9d38909abd0bc3d23f773110f076ad # v4.4.1
|
||||
uses: CodSpeedHQ/action@972e3437949c89e1357ebd1a2dbc852fcbc57245 # v4.5.1
|
||||
with:
|
||||
mode: simulation
|
||||
run: cargo codspeed run --bench ty "${{ matrix.benchmark }}"
|
||||
@@ -1098,7 +1098,7 @@ jobs:
|
||||
run: rustup show
|
||||
|
||||
- name: "Install codspeed"
|
||||
uses: taiki-e/install-action@b9c5db3aef04caffaf95a1d03931de10fb2a140f # v2.65.1
|
||||
uses: taiki-e/install-action@28a9d316db64b78a951f3f8587a5d08cc97ad8eb # v2.65.6
|
||||
with:
|
||||
tool: cargo-codspeed
|
||||
|
||||
@@ -1136,7 +1136,7 @@ jobs:
|
||||
- uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6
|
||||
|
||||
- name: "Install codspeed"
|
||||
uses: taiki-e/install-action@b9c5db3aef04caffaf95a1d03931de10fb2a140f # v2.65.1
|
||||
uses: taiki-e/install-action@28a9d316db64b78a951f3f8587a5d08cc97ad8eb # v2.65.6
|
||||
with:
|
||||
tool: cargo-codspeed
|
||||
|
||||
@@ -1150,7 +1150,7 @@ jobs:
|
||||
run: chmod +x target/codspeed/walltime/ruff_benchmark/ty_walltime
|
||||
|
||||
- name: "Run benchmarks"
|
||||
uses: CodSpeedHQ/action@346a2d8a8d9d38909abd0bc3d23f773110f076ad # v4.4.1
|
||||
uses: CodSpeedHQ/action@972e3437949c89e1357ebd1a2dbc852fcbc57245 # v4.5.1
|
||||
env:
|
||||
# enabling walltime flamegraphs adds ~6 minutes to the CI time, and they don't
|
||||
# appear to provide much useful insight for our walltime benchmarks right now
|
||||
|
||||
@@ -32,13 +32,13 @@ repos:
|
||||
- id: validate-pyproject
|
||||
|
||||
- repo: https://github.com/executablebooks/mdformat
|
||||
rev: 0.7.22
|
||||
rev: 1.0.0
|
||||
hooks:
|
||||
- id: mdformat
|
||||
language: python # means renovate will also update `additional_dependencies`
|
||||
additional_dependencies:
|
||||
- mdformat-mkdocs==4.0.0
|
||||
- mdformat-footnote==0.1.1
|
||||
- mdformat-mkdocs==5.0.0
|
||||
- mdformat-footnote==0.1.2
|
||||
exclude: |
|
||||
(?x)^(
|
||||
docs/formatter/black\.md
|
||||
|
||||
64
CLAUDE.md
Normal file
64
CLAUDE.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# Ruff Repository
|
||||
|
||||
This repository contains both Ruff (a Python linter and formatter) and ty (a Python type checker). The crates follow a naming convention: `ruff_*` for Ruff-specific code and `ty_*` for ty-specific code. ty reuses several Ruff crates, including the Python parser (`ruff_python_parser`) and AST definitions (`ruff_python_ast`).
|
||||
|
||||
## Running Tests
|
||||
|
||||
Run all tests (using `nextest` for faster execution):
|
||||
|
||||
```sh
|
||||
cargo nextest run
|
||||
```
|
||||
|
||||
Run tests for a specific crate:
|
||||
|
||||
```sh
|
||||
cargo nextest run -p ty_python_semantic
|
||||
```
|
||||
|
||||
Run a specific mdtest (use a substring of the test name):
|
||||
|
||||
```sh
|
||||
MDTEST_TEST_FILTER="<filter>" cargo nextest run -p ty_python_semantic mdtest
|
||||
```
|
||||
|
||||
Update snapshots after running tests:
|
||||
|
||||
```sh
|
||||
cargo insta accept
|
||||
```
|
||||
|
||||
## Running Clippy
|
||||
|
||||
```sh
|
||||
cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||
```
|
||||
|
||||
## Running Debug Builds
|
||||
|
||||
Use debug builds (not `--release`) when developing, as release builds lack debug assertions and have slower compile times.
|
||||
|
||||
Run Ruff:
|
||||
|
||||
```sh
|
||||
cargo run --bin ruff -- check path/to/file.py
|
||||
```
|
||||
|
||||
Run ty:
|
||||
|
||||
```sh
|
||||
cargo run --bin ty -- check path/to/file.py
|
||||
```
|
||||
|
||||
## Pull Requests
|
||||
|
||||
When working on ty, PR titles should start with `[ty]` and be tagged with the `ty` GitHub label.
|
||||
|
||||
## Development Guidelines
|
||||
|
||||
- All changes must be tested. If you're not testing your changes, you're not done.
|
||||
- Get your tests to pass. If you didn't run the tests, your code does not work.
|
||||
- Follow existing code style. Check neighboring files for patterns.
|
||||
- Always run `uvx pre-commit run -a` at the end of a task.
|
||||
- Avoid writing significant amounts of new code. This is often a sign that we're missing an existing method or mechanism that could help solve the problem. Look for existing utilities first.
|
||||
- Avoid falling back to patterns that require `panic!`, `unreachable!`, or `.unwrap()`. Instead, try to encode those constraints in the type system.
|
||||
68
Cargo.lock
generated
68
Cargo.lock
generated
@@ -146,9 +146,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
version = "1.7.1"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
|
||||
checksum = "51d03449bb8ca2cc2ef70869af31463d1ae5ccc8fa3e334b307203fbf815207e"
|
||||
dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "argfile"
|
||||
@@ -1030,7 +1033,7 @@ dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1122,7 +1125,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1645,9 +1648,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "insta"
|
||||
version = "1.45.0"
|
||||
version = "1.45.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b76866be74d68b1595eb8060cb9191dca9c021db2316558e52ddc5d55d41b66c"
|
||||
checksum = "983e3b24350c84ab8a65151f537d67afbbf7153bb9f1110e03e9fa9b07f67a5c"
|
||||
dependencies = [
|
||||
"console 0.15.11",
|
||||
"once_cell",
|
||||
@@ -1778,9 +1781,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "jiff"
|
||||
version = "0.2.16"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35"
|
||||
checksum = "a87d9b8105c23642f50cbbae03d1f75d8422c5cb98ce7ee9271f7ff7505be6b8"
|
||||
dependencies = [
|
||||
"jiff-static",
|
||||
"jiff-tzdb-platform",
|
||||
@@ -1788,14 +1791,14 @@ dependencies = [
|
||||
"portable-atomic",
|
||||
"portable-atomic-util",
|
||||
"serde_core",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jiff-static"
|
||||
version = "0.2.16"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69"
|
||||
checksum = "b787bebb543f8969132630c51fd0afab173a86c6abae56ff3b9e5e3e3f9f6e58"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2057,9 +2060,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
|
||||
|
||||
[[package]]
|
||||
name = "matchit"
|
||||
version = "0.9.0"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ea5f97102eb9e54ab99fb70bb175589073f554bdadfb74d9bd656482ea73e2a"
|
||||
checksum = "b3eede3bdf92f3b4f9dc04072a9ce5ab557d5ec9038773bf9ffcd5588b3cc05b"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
@@ -2631,9 +2634,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.103"
|
||||
version = "1.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||
checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -3246,7 +3249,6 @@ name = "ruff_memory_usage"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"get-size2",
|
||||
"ordermap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3617,15 +3619,15 @@ checksum = "781442f29170c5c93b7185ad559492601acdc71d5bb0706f5868094f45cfcd08"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.2"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
|
||||
checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3692,9 +3694,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "schemars"
|
||||
version = "1.1.0"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289"
|
||||
checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2"
|
||||
dependencies = [
|
||||
"dyn-clone",
|
||||
"ref-cast",
|
||||
@@ -3705,9 +3707,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "schemars_derive"
|
||||
version = "1.1.0"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "301858a4023d78debd2353c7426dc486001bddc91ae31a76fb1f55132f7e2633"
|
||||
checksum = "4908ad288c5035a8eb12cfdf0d49270def0a268ee162b75eeee0f85d155a7c45"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3781,15 +3783,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.146"
|
||||
version = "1.0.148"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "217ca874ae0207aac254aa02c957ded05585a90892cc8d87f9e5fa49669dadd8"
|
||||
checksum = "3084b546a1dd6289475996f182a22aba973866ea8e8b02c51d9f46b1336a22da"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4019,15 +4021,15 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.23.0"
|
||||
version = "3.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16"
|
||||
checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom 0.3.4",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5131,7 +5133,7 @@ version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5523,6 +5525,12 @@ dependencies = [
|
||||
"zstd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30e0d8dffbae3d840f64bda38e28391faef673a7b5a6017840f2a106c8145868"
|
||||
|
||||
[[package]]
|
||||
name = "zstd"
|
||||
version = "0.11.2+zstd.1.5.2"
|
||||
|
||||
@@ -93,6 +93,7 @@ get-size2 = { version = "0.7.3", features = [
|
||||
"smallvec",
|
||||
"hashbrown",
|
||||
"compact-str",
|
||||
"ordermap"
|
||||
] }
|
||||
getrandom = { version = "0.3.1" }
|
||||
glob = { version = "0.3.1" }
|
||||
|
||||
@@ -557,6 +557,60 @@ fn benchmark_many_enum_members(criterion: &mut Criterion) {
|
||||
});
|
||||
}
|
||||
|
||||
fn benchmark_many_enum_members_2(criterion: &mut Criterion) {
|
||||
const NUM_ENUM_MEMBERS: usize = 48;
|
||||
|
||||
setup_rayon();
|
||||
|
||||
let mut code = "\
|
||||
from enum import Enum
|
||||
from typing_extensions import assert_never
|
||||
|
||||
class E(Enum):
|
||||
"
|
||||
.to_string();
|
||||
|
||||
for i in 0..NUM_ENUM_MEMBERS {
|
||||
writeln!(&mut code, " m{i} = {i}").ok();
|
||||
}
|
||||
|
||||
code.push_str(
|
||||
"
|
||||
def method(self):
|
||||
match self:",
|
||||
);
|
||||
|
||||
for i in 0..NUM_ENUM_MEMBERS {
|
||||
write!(
|
||||
&mut code,
|
||||
"
|
||||
case E.m{i}:
|
||||
pass"
|
||||
)
|
||||
.ok();
|
||||
}
|
||||
|
||||
write!(
|
||||
&mut code,
|
||||
"
|
||||
case _:
|
||||
assert_never(self)"
|
||||
)
|
||||
.ok();
|
||||
|
||||
criterion.bench_function("ty_micro[many_enum_members_2]", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| setup_micro_case(&code),
|
||||
|case| {
|
||||
let Case { db, .. } = case;
|
||||
let result = db.check();
|
||||
assert_eq!(result.len(), 0);
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
struct ProjectBenchmark<'a> {
|
||||
project: InstalledProject<'a>,
|
||||
fs: MemoryFileSystem,
|
||||
@@ -717,6 +771,7 @@ criterion_group!(
|
||||
benchmark_complex_constrained_attributes_2,
|
||||
benchmark_complex_constrained_attributes_3,
|
||||
benchmark_many_enum_members,
|
||||
benchmark_many_enum_members_2,
|
||||
);
|
||||
criterion_group!(project, anyio, attrs, hydra, datetype);
|
||||
criterion_main!(check_file, micro, project);
|
||||
|
||||
@@ -157,14 +157,11 @@ pub(crate) fn suppressible_exception(
|
||||
let mut rest: Vec<Edit> = Vec::new();
|
||||
let content: String;
|
||||
if exception == "BaseException" && handler_names.is_empty() {
|
||||
let (import_exception, binding_exception) =
|
||||
checker.importer().get_or_import_symbol(
|
||||
&ImportRequest::import("builtins", &exception),
|
||||
stmt.start(),
|
||||
checker.semantic(),
|
||||
)?;
|
||||
let (import_exception, binding_exception) = checker
|
||||
.importer()
|
||||
.get_or_import_builtin_symbol(&exception, stmt.start(), checker.semantic())?;
|
||||
content = format!("with {binding}({binding_exception})");
|
||||
rest.push(import_exception);
|
||||
rest.extend(import_exception);
|
||||
} else {
|
||||
content = format!("with {binding}({exception})");
|
||||
}
|
||||
|
||||
@@ -104,22 +104,21 @@ SIM105 [*] Use `contextlib.suppress(BaseException)` instead of `try`-`except`-`p
|
||||
|
|
||||
help: Replace `try`-`except`-`pass` with `with contextlib.suppress(BaseException): ...`
|
||||
1 + import contextlib
|
||||
2 + import builtins
|
||||
3 | def foo():
|
||||
4 | pass
|
||||
5 |
|
||||
2 | def foo():
|
||||
3 | pass
|
||||
4 |
|
||||
--------------------------------------------------------------------------------
|
||||
24 | pass
|
||||
25 |
|
||||
26 | # SIM105
|
||||
23 | pass
|
||||
24 |
|
||||
25 | # SIM105
|
||||
- try:
|
||||
27 + with contextlib.suppress(builtins.BaseException):
|
||||
28 | foo()
|
||||
26 + with contextlib.suppress(BaseException):
|
||||
27 | foo()
|
||||
- except:
|
||||
- pass
|
||||
29 |
|
||||
30 | # SIM105
|
||||
31 | try:
|
||||
28 |
|
||||
29 | # SIM105
|
||||
30 | try:
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
SIM105 [*] Use `contextlib.suppress(a.Error, b.Error)` instead of `try`-`except`-`pass`
|
||||
|
||||
@@ -12,7 +12,6 @@ license = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
get-size2 = { workspace = true }
|
||||
ordermap = { workspace = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::cell::RefCell;
|
||||
|
||||
use get_size2::{GetSize, StandardTracker};
|
||||
use ordermap::{OrderMap, OrderSet};
|
||||
|
||||
thread_local! {
|
||||
pub static TRACKER: RefCell<Option<StandardTracker>>= const { RefCell::new(None) };
|
||||
@@ -42,16 +41,3 @@ pub fn heap_size<T: GetSize>(value: &T) -> usize {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// An implementation of [`GetSize::get_heap_size`] for [`OrderSet`].
|
||||
pub fn order_set_heap_size<T: GetSize, S>(set: &OrderSet<T, S>) -> usize {
|
||||
(set.capacity() * T::get_stack_size()) + set.iter().map(heap_size).sum::<usize>()
|
||||
}
|
||||
|
||||
/// An implementation of [`GetSize::get_heap_size`] for [`OrderMap`].
|
||||
pub fn order_map_heap_size<K: GetSize, V: GetSize, S>(map: &OrderMap<K, V, S>) -> usize {
|
||||
(map.capacity() * (K::get_stack_size() + V::get_stack_size()))
|
||||
+ (map.iter())
|
||||
.map(|(k, v)| heap_size(k) + heap_size(v))
|
||||
.sum::<usize>()
|
||||
}
|
||||
|
||||
@@ -25,6 +25,12 @@ that are ready for contributions.
|
||||
ty is written in Rust. You'll need to install the
|
||||
[Rust toolchain](https://www.rust-lang.org/tools/install) for development.
|
||||
|
||||
You'll also need [Insta](https://insta.rs/docs/) to update snapshot tests:
|
||||
|
||||
```shell
|
||||
cargo install cargo-insta
|
||||
```
|
||||
|
||||
You'll need [uv](https://docs.astral.sh/uv/getting-started/installation/) (or `pipx` and `pip`) to
|
||||
run Python utility commands.
|
||||
|
||||
@@ -68,6 +74,13 @@ will save you time and expedite the merge process.
|
||||
|
||||
If you're using VS Code, you can also install the recommended [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) extension to get these checks while editing.
|
||||
|
||||
Note that many code changes also require updating the snapshot tests, which is done interactively
|
||||
after running `cargo test` like so:
|
||||
|
||||
```shell
|
||||
cargo insta review
|
||||
```
|
||||
|
||||
Include the text `[ty]` at the beginning of your pull request title, to distinguish ty pull requests
|
||||
from Ruff ones.
|
||||
|
||||
|
||||
@@ -196,6 +196,7 @@ fn render_markdown(docstring: &str) -> String {
|
||||
let mut first_line = true;
|
||||
let mut block_indent = 0;
|
||||
let mut in_doctest = false;
|
||||
let mut in_markdown_with_fence = None;
|
||||
let mut starting_literal = None;
|
||||
let mut in_literal = false;
|
||||
let mut in_any_code = false;
|
||||
@@ -254,6 +255,34 @@ fn render_markdown(docstring: &str) -> String {
|
||||
output.push_str("python\n");
|
||||
}
|
||||
|
||||
// If we're not in a codeblock and we see a markdown codefence, start one
|
||||
let has_tick_fence = line.starts_with("```");
|
||||
let has_tilde_fence = line.starts_with("~~~");
|
||||
if !in_any_code && (has_tick_fence || has_tilde_fence) {
|
||||
let without_leading_fence = if has_tick_fence {
|
||||
line.trim_start_matches('`')
|
||||
} else {
|
||||
line.trim_start_matches('~')
|
||||
};
|
||||
let fence_len = line.len() - without_leading_fence.len();
|
||||
let fence = &line[..fence_len];
|
||||
// If we don't see this amount of ticks again on the line, assume we're opening a markdown block
|
||||
// (We *don't* want to consider ```hello``` as a codefence, that's inline code!)
|
||||
if !without_leading_fence.contains(fence) {
|
||||
// Unlike other blocks we don't need to emit fences because it's already markdown
|
||||
block_indent = line_indent;
|
||||
in_any_code = true;
|
||||
in_markdown_with_fence = Some(fence.to_owned());
|
||||
}
|
||||
// If we're in a markdown code fence and this line seems to terminate it, end the block
|
||||
} else if let Some(fence) = &in_markdown_with_fence
|
||||
&& line.starts_with(fence)
|
||||
{
|
||||
in_any_code = false;
|
||||
block_indent = 0;
|
||||
in_markdown_with_fence = None;
|
||||
}
|
||||
|
||||
// If we're not in a codeblock and we see something that signals a literal block, start one
|
||||
let parsed_lit = line
|
||||
// first check for a line ending with `::`
|
||||
@@ -423,7 +452,11 @@ fn render_markdown(docstring: &str) -> String {
|
||||
// Flush codeblock
|
||||
if in_any_code {
|
||||
output.push('\n');
|
||||
output.push_str(FENCE);
|
||||
if let Some(fence) = &in_markdown_with_fence {
|
||||
output.push_str(fence);
|
||||
} else {
|
||||
output.push_str(FENCE);
|
||||
}
|
||||
}
|
||||
|
||||
output
|
||||
@@ -1095,6 +1128,174 @@ mod tests {
|
||||
");
|
||||
}
|
||||
|
||||
// We should not parse the contents of a markdown codefence
|
||||
#[test]
|
||||
fn explicit_markdown_block_with_ps1_contents() {
|
||||
let docstring = r#"
|
||||
My cool func:
|
||||
|
||||
```python
|
||||
>>> thing.do_thing()
|
||||
wow it did the thing
|
||||
>>> thing.do_other_thing()
|
||||
it sure did the thing
|
||||
```
|
||||
"#;
|
||||
|
||||
let docstring = Docstring::new(docstring.to_owned());
|
||||
|
||||
assert_snapshot!(docstring.render_markdown(), @r"
|
||||
My cool func:
|
||||
|
||||
```python
|
||||
>>> thing.do_thing()
|
||||
wow it did the thing
|
||||
>>> thing.do_other_thing()
|
||||
it sure did the thing
|
||||
```
|
||||
");
|
||||
}
|
||||
|
||||
// We should not parse the contents of a markdown codefence
|
||||
#[test]
|
||||
fn explicit_markdown_block_with_underscore_contents_tick() {
|
||||
let docstring = r#"
|
||||
My cool func:
|
||||
|
||||
`````python
|
||||
x_y = thing_do();
|
||||
``` # this should't close the fence!
|
||||
a_b = other_thing();
|
||||
`````
|
||||
"#;
|
||||
|
||||
let docstring = Docstring::new(docstring.to_owned());
|
||||
|
||||
assert_snapshot!(docstring.render_markdown(), @r"
|
||||
My cool func:
|
||||
|
||||
`````python
|
||||
x_y = thing_do();
|
||||
``` # this should't close the fence!
|
||||
a_b = other_thing();
|
||||
`````
|
||||
");
|
||||
}
|
||||
|
||||
// `~~~` also starts a markdown codefence
|
||||
#[test]
|
||||
fn explicit_markdown_block_with_underscore_contents_tilde() {
|
||||
let docstring = r#"
|
||||
My cool func:
|
||||
|
||||
~~~~~python
|
||||
x_y = thing_do();
|
||||
~~~ # this should't close the fence!
|
||||
a_b = other_thing();
|
||||
~~~~~
|
||||
"#;
|
||||
|
||||
let docstring = Docstring::new(docstring.to_owned());
|
||||
|
||||
assert_snapshot!(docstring.render_markdown(), @r"
|
||||
My cool func:
|
||||
|
||||
~~~~~python
|
||||
x_y = thing_do();
|
||||
~~~ # this should't close the fence!
|
||||
a_b = other_thing();
|
||||
~~~~~
|
||||
");
|
||||
}
|
||||
|
||||
// What do we do when we hit the end of the docstring with an unclosed markdown block?
|
||||
#[test]
|
||||
fn explicit_markdown_block_with_unclosed_fence_tick() {
|
||||
let docstring = r#"
|
||||
My cool func:
|
||||
|
||||
````python
|
||||
x_y = thing_do();
|
||||
"#;
|
||||
|
||||
let docstring = Docstring::new(docstring.to_owned());
|
||||
|
||||
assert_snapshot!(docstring.render_markdown(), @r"
|
||||
My cool func:
|
||||
|
||||
````python
|
||||
x_y = thing_do();
|
||||
````
|
||||
");
|
||||
}
|
||||
|
||||
// What do we do when we hit the end of the docstring with an unclosed markdown block?
|
||||
#[test]
|
||||
fn explicit_markdown_block_with_unclosed_fence_tilde() {
|
||||
let docstring = r#"
|
||||
My cool func:
|
||||
|
||||
~~~~~python
|
||||
x_y = thing_do();
|
||||
"#;
|
||||
|
||||
let docstring = Docstring::new(docstring.to_owned());
|
||||
|
||||
assert_snapshot!(docstring.render_markdown(), @r"
|
||||
My cool func:
|
||||
|
||||
~~~~~python
|
||||
x_y = thing_do();
|
||||
~~~~~
|
||||
");
|
||||
}
|
||||
|
||||
// Demonstration of where we're unreasonably lax about markdown block parsing.
|
||||
// It's fine to break this test, it's not particularly intentional behaviour.
|
||||
#[test]
|
||||
fn explicit_markdown_block_messy_corners_tick() {
|
||||
let docstring = r#"
|
||||
My cool func:
|
||||
|
||||
``````we still think this is a codefence```
|
||||
x_y = thing_do();
|
||||
```````````` and are sloppy as heck with indentation and closing shrugggg
|
||||
"#;
|
||||
|
||||
let docstring = Docstring::new(docstring.to_owned());
|
||||
|
||||
assert_snapshot!(docstring.render_markdown(), @r"
|
||||
My cool func:
|
||||
|
||||
``````we still think this is a codefence```
|
||||
x_y = thing_do();
|
||||
```````````` and are sloppy as heck with indentation and closing shrugggg
|
||||
");
|
||||
}
|
||||
|
||||
// Demonstration of where we're unreasonably lax about markdown block parsing.
|
||||
// It's fine to break this test, it's not particularly intentional behaviour.
|
||||
#[test]
|
||||
fn explicit_markdown_block_messy_corners_tilde() {
|
||||
let docstring = r#"
|
||||
My cool func:
|
||||
|
||||
~~~~~~we still think this is a codefence~~~
|
||||
x_y = thing_do();
|
||||
~~~~~~~~~~~~~ and are sloppy as heck with indentation and closing shrugggg
|
||||
"#;
|
||||
|
||||
let docstring = Docstring::new(docstring.to_owned());
|
||||
|
||||
assert_snapshot!(docstring.render_markdown(), @r"
|
||||
My cool func:
|
||||
|
||||
~~~~~~we still think this is a codefence~~~
|
||||
x_y = thing_do();
|
||||
~~~~~~~~~~~~~ and are sloppy as heck with indentation and closing shrugggg
|
||||
");
|
||||
}
|
||||
|
||||
// `.. code::` is a literal block and the `.. code::` should be deleted
|
||||
#[test]
|
||||
fn code_block() {
|
||||
|
||||
@@ -234,3 +234,38 @@ def takes_no_argument() -> str:
|
||||
@takes_no_argument
|
||||
def g(x): ...
|
||||
```
|
||||
|
||||
## Class decorators
|
||||
|
||||
Class decorator calls are validated, emitting diagnostics for invalid arguments:
|
||||
|
||||
```py
|
||||
def takes_int(x: int) -> int:
|
||||
return x
|
||||
|
||||
# error: [invalid-argument-type]
|
||||
@takes_int
|
||||
class Foo: ...
|
||||
```
|
||||
|
||||
Using `None` as a decorator is an error:
|
||||
|
||||
```py
|
||||
# error: [call-non-callable]
|
||||
@None
|
||||
class Bar: ...
|
||||
```
|
||||
|
||||
A decorator can enforce type constraints on the class being decorated:
|
||||
|
||||
```py
|
||||
def decorator(cls: type[int]) -> type[int]:
|
||||
return cls
|
||||
|
||||
# error: [invalid-argument-type]
|
||||
@decorator
|
||||
class Baz: ...
|
||||
|
||||
# TODO: the revealed type should ideally be `type[int]` (the decorator's return type)
|
||||
reveal_type(Baz) # revealed: <class 'Baz'>
|
||||
```
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -40,8 +40,8 @@
|
||||
use crate::types::enums::{enum_member_literals, enum_metadata};
|
||||
use crate::types::type_ordering::union_or_intersection_elements_ordering;
|
||||
use crate::types::{
|
||||
BytesLiteralType, IntersectionType, KnownClass, StringLiteralType, Type,
|
||||
TypeVarBoundOrConstraints, UnionType,
|
||||
BytesLiteralType, IntersectionType, KnownClass, NegativeIntersectionElements,
|
||||
StringLiteralType, Type, TypeVarBoundOrConstraints, UnionType,
|
||||
};
|
||||
use crate::{Db, FxOrderSet};
|
||||
use rustc_hash::FxHashSet;
|
||||
@@ -99,92 +99,77 @@ impl<'db> UnionElement<'db> {
|
||||
|
||||
/// Try reducing this `UnionElement` given the presence in the same union of `other_type`.
|
||||
fn try_reduce(&mut self, db: &'db dyn Db, other_type: Type<'db>) -> ReduceResult<'db> {
|
||||
match self {
|
||||
let mut other_type_negated_cache = None;
|
||||
let mut other_type_negated =
|
||||
|| *other_type_negated_cache.get_or_insert_with(|| other_type.negate(db));
|
||||
|
||||
let mut collapse = false;
|
||||
let mut ignore = false;
|
||||
|
||||
// A closure called for each element in a set of literals
|
||||
// to determine whether the element should be retained in the set.
|
||||
//
|
||||
// If `ignore` or `collapse` is `true` for any element in the set,
|
||||
// we no longer need to do any expensive subtyping checks for any
|
||||
// further elements in the set:
|
||||
//
|
||||
// - if `ignore` is `true`, this indicates that `other_type` is a
|
||||
// subtype of one of the literals in this set. Given this fact,
|
||||
// it cannot be possible for any other literals in this set to be
|
||||
// a subtype of `other_type`.
|
||||
// - if `collapse` is `true`, all literals of this kind will be
|
||||
// removed from the union, so it's irrelevant to answer the
|
||||
// question of which literals should remain in this set.
|
||||
//
|
||||
// We therefore only ask if `ty` is a subtype of `other_type` if
|
||||
// both `ignore` and `collapse` are `false`. If either is `true`,
|
||||
// we skip the expensive subtype check and return `true`.
|
||||
let mut should_retain_type = |ty| {
|
||||
if ignore || other_type.is_subtype_of(db, ty) {
|
||||
ignore = true;
|
||||
return true;
|
||||
}
|
||||
if collapse || other_type_negated().is_subtype_of(db, ty) {
|
||||
collapse = true;
|
||||
return true;
|
||||
}
|
||||
!ty.is_subtype_of(db, other_type)
|
||||
};
|
||||
|
||||
let should_keep = match self {
|
||||
UnionElement::IntLiterals(literals) => {
|
||||
if other_type.splits_literals(db, LiteralKind::Int) {
|
||||
let mut collapse = false;
|
||||
let mut ignore = false;
|
||||
let negated = other_type.negate(db);
|
||||
literals.retain(|literal| {
|
||||
let ty = Type::IntLiteral(*literal);
|
||||
if negated.is_subtype_of(db, ty) {
|
||||
collapse = true;
|
||||
}
|
||||
if other_type.is_subtype_of(db, ty) {
|
||||
ignore = true;
|
||||
}
|
||||
!ty.is_subtype_of(db, other_type)
|
||||
});
|
||||
if ignore {
|
||||
ReduceResult::Ignore
|
||||
} else if collapse {
|
||||
ReduceResult::CollapseToObject
|
||||
} else {
|
||||
ReduceResult::KeepIf(!literals.is_empty())
|
||||
}
|
||||
literals.retain(|literal| should_retain_type(Type::IntLiteral(*literal)));
|
||||
!literals.is_empty()
|
||||
} else {
|
||||
ReduceResult::KeepIf(
|
||||
!Type::IntLiteral(literals[0]).is_subtype_of(db, other_type),
|
||||
)
|
||||
!Type::IntLiteral(literals[0]).is_subtype_of(db, other_type)
|
||||
}
|
||||
}
|
||||
UnionElement::StringLiterals(literals) => {
|
||||
if other_type.splits_literals(db, LiteralKind::String) {
|
||||
let mut collapse = false;
|
||||
let mut ignore = false;
|
||||
let negated = other_type.negate(db);
|
||||
literals.retain(|literal| {
|
||||
let ty = Type::StringLiteral(*literal);
|
||||
if negated.is_subtype_of(db, ty) {
|
||||
collapse = true;
|
||||
}
|
||||
if other_type.is_subtype_of(db, ty) {
|
||||
ignore = true;
|
||||
}
|
||||
!ty.is_subtype_of(db, other_type)
|
||||
});
|
||||
if ignore {
|
||||
ReduceResult::Ignore
|
||||
} else if collapse {
|
||||
ReduceResult::CollapseToObject
|
||||
} else {
|
||||
ReduceResult::KeepIf(!literals.is_empty())
|
||||
}
|
||||
literals.retain(|literal| should_retain_type(Type::StringLiteral(*literal)));
|
||||
!literals.is_empty()
|
||||
} else {
|
||||
ReduceResult::KeepIf(
|
||||
!Type::StringLiteral(literals[0]).is_subtype_of(db, other_type),
|
||||
)
|
||||
!Type::StringLiteral(literals[0]).is_subtype_of(db, other_type)
|
||||
}
|
||||
}
|
||||
UnionElement::BytesLiterals(literals) => {
|
||||
if other_type.splits_literals(db, LiteralKind::Bytes) {
|
||||
let mut collapse = false;
|
||||
let mut ignore = false;
|
||||
let negated = other_type.negate(db);
|
||||
literals.retain(|literal| {
|
||||
let ty = Type::BytesLiteral(*literal);
|
||||
if negated.is_subtype_of(db, ty) {
|
||||
collapse = true;
|
||||
}
|
||||
if other_type.is_subtype_of(db, ty) {
|
||||
ignore = true;
|
||||
}
|
||||
!ty.is_subtype_of(db, other_type)
|
||||
});
|
||||
if ignore {
|
||||
ReduceResult::Ignore
|
||||
} else if collapse {
|
||||
ReduceResult::CollapseToObject
|
||||
} else {
|
||||
ReduceResult::KeepIf(!literals.is_empty())
|
||||
}
|
||||
literals.retain(|literal| should_retain_type(Type::BytesLiteral(*literal)));
|
||||
!literals.is_empty()
|
||||
} else {
|
||||
ReduceResult::KeepIf(
|
||||
!Type::BytesLiteral(literals[0]).is_subtype_of(db, other_type),
|
||||
)
|
||||
!Type::BytesLiteral(literals[0]).is_subtype_of(db, other_type)
|
||||
}
|
||||
}
|
||||
UnionElement::Type(existing) => ReduceResult::Type(*existing),
|
||||
UnionElement::Type(existing) => return ReduceResult::Type(*existing),
|
||||
};
|
||||
|
||||
if ignore {
|
||||
ReduceResult::Ignore
|
||||
} else if collapse {
|
||||
ReduceResult::CollapseToObject
|
||||
} else {
|
||||
ReduceResult::KeepIf(should_keep)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -960,7 +945,7 @@ impl<'db> IntersectionBuilder<'db> {
|
||||
#[derive(Debug, Clone, Default)]
|
||||
struct InnerIntersectionBuilder<'db> {
|
||||
positive: FxOrderSet<Type<'db>>,
|
||||
negative: FxOrderSet<Type<'db>>,
|
||||
negative: NegativeIntersectionElements<'db>,
|
||||
}
|
||||
|
||||
impl<'db> InnerIntersectionBuilder<'db> {
|
||||
|
||||
@@ -1226,7 +1226,10 @@ impl<'db> Bindings<'db> {
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let constraints = ConstraintSet::range(db, *lower, *typevar, *upper);
|
||||
// XXX: Replace this with lower, upper, and equality
|
||||
let lower = ConstraintSet::lower_bound_constraint(db, *typevar, *lower);
|
||||
let upper = ConstraintSet::upper_bound_constraint(db, *typevar, *upper);
|
||||
let constraints = lower.and(db, || upper);
|
||||
let tracked = TrackedConstraintSet::new(db, constraints);
|
||||
overload.set_return_type(Type::KnownInstance(
|
||||
KnownInstanceType::ConstraintSet(tracked),
|
||||
|
||||
@@ -30,6 +30,9 @@ use crate::types::generics::{
|
||||
};
|
||||
use crate::types::infer::{infer_expression_type, infer_unpack_types, nearest_enclosing_class};
|
||||
use crate::types::member::{Member, class_member};
|
||||
use crate::types::relation::{
|
||||
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, TypeRelation,
|
||||
};
|
||||
use crate::types::signatures::{CallableSignature, Parameter, Parameters, Signature};
|
||||
use crate::types::tuple::{TupleSpec, TupleType};
|
||||
use crate::types::typed_dict::typed_dict_params_from_class_def;
|
||||
@@ -37,11 +40,10 @@ use crate::types::visitor::{TypeCollector, TypeVisitor, walk_type_with_recursion
|
||||
use crate::types::{
|
||||
ApplyTypeMappingVisitor, Binding, BindingContext, BoundSuperType, CallableType,
|
||||
CallableTypeKind, CallableTypes, DATACLASS_FLAGS, DataclassFlags, DataclassParams,
|
||||
DeprecatedInstance, FindLegacyTypeVarsVisitor, HasRelationToVisitor, IntersectionType,
|
||||
IsDisjointVisitor, IsEquivalentVisitor, KnownInstanceType, ManualPEP695TypeAliasType,
|
||||
MaterializationKind, NormalizedVisitor, PropertyInstanceType, TypeAliasType, TypeContext,
|
||||
TypeMapping, TypeRelation, TypedDictParams, UnionBuilder, VarianceInferable, binding_type,
|
||||
declaration_type, determine_upper_bound,
|
||||
DeprecatedInstance, FindLegacyTypeVarsVisitor, IntersectionType, KnownInstanceType,
|
||||
ManualPEP695TypeAliasType, MaterializationKind, NormalizedVisitor, PropertyInstanceType,
|
||||
TypeAliasType, TypeContext, TypeMapping, TypedDictParams, UnionBuilder, VarianceInferable,
|
||||
binding_type, declaration_type, determine_upper_bound,
|
||||
};
|
||||
use crate::{
|
||||
Db, FxIndexMap, FxIndexSet, FxOrderSet, Program,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -57,6 +57,7 @@ use ruff_db::files::{File, FileRange};
|
||||
use ruff_db::parsed::{ParsedModuleRef, parsed_module};
|
||||
use ruff_python_ast::{self as ast, ParameterWithDefault};
|
||||
use ruff_text_size::Ranged;
|
||||
use ty_module_resolver::{KnownModule, ModuleName, file_to_module, resolve_module};
|
||||
|
||||
use crate::place::{Definedness, Place, place_from_bindings};
|
||||
use crate::semantic_index::ast_ids::HasScopedUseId;
|
||||
@@ -76,18 +77,19 @@ use crate::types::generics::{GenericContext, InferableTypeVars, typing_self};
|
||||
use crate::types::infer::nearest_enclosing_class;
|
||||
use crate::types::list_members::all_members;
|
||||
use crate::types::narrow::ClassInfoConstraintFunction;
|
||||
use crate::types::relation::{
|
||||
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, TypeRelation,
|
||||
};
|
||||
use crate::types::signatures::{CallableSignature, Signature};
|
||||
use crate::types::visitor::any_over_type;
|
||||
use crate::types::{
|
||||
ApplyTypeMappingVisitor, BoundMethodType, BoundTypeVarInstance, CallableType, CallableTypeKind,
|
||||
ClassBase, ClassLiteral, ClassType, DeprecatedInstance, DynamicType, FindLegacyTypeVarsVisitor,
|
||||
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, KnownClass, KnownInstanceType,
|
||||
NormalizedVisitor, SpecialFormType, SubclassOfInner, SubclassOfType, Truthiness, Type,
|
||||
TypeContext, TypeMapping, TypeRelation, TypeVarBoundOrConstraints, UnionBuilder, binding_type,
|
||||
definition_expression_type, infer_definition_types, walk_signature,
|
||||
KnownClass, KnownInstanceType, NormalizedVisitor, SpecialFormType, SubclassOfInner,
|
||||
SubclassOfType, Truthiness, Type, TypeContext, TypeMapping, TypeVarBoundOrConstraints,
|
||||
UnionBuilder, binding_type, definition_expression_type, infer_definition_types, walk_signature,
|
||||
};
|
||||
use crate::{Db, FxOrderSet};
|
||||
use ty_module_resolver::{KnownModule, ModuleName, file_to_module, resolve_module};
|
||||
|
||||
/// A collection of useful spans for annotating functions.
|
||||
///
|
||||
|
||||
@@ -12,19 +12,21 @@ use crate::semantic_index::scope::{FileScopeId, NodeWithScopeKind, ScopeId};
|
||||
use crate::semantic_index::{SemanticIndex, semantic_index};
|
||||
use crate::types::class::ClassType;
|
||||
use crate::types::class_base::ClassBase;
|
||||
use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension};
|
||||
use crate::types::constraints::{ConstraintBound, ConstraintSet, IteratorConstraintsExtension};
|
||||
use crate::types::instance::{Protocol, ProtocolInstanceType};
|
||||
use crate::types::relation::{
|
||||
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, TypeRelation,
|
||||
};
|
||||
use crate::types::signatures::Parameters;
|
||||
use crate::types::tuple::{TupleSpec, TupleType, walk_tuple_type};
|
||||
use crate::types::variance::VarianceInferable;
|
||||
use crate::types::visitor::{TypeCollector, TypeVisitor, walk_type_with_recursion_guard};
|
||||
use crate::types::{
|
||||
ApplyTypeMappingVisitor, BindingContext, BoundTypeVarIdentity, BoundTypeVarInstance,
|
||||
ClassLiteral, FindLegacyTypeVarsVisitor, HasRelationToVisitor, IntersectionType,
|
||||
IsDisjointVisitor, IsEquivalentVisitor, KnownClass, KnownInstanceType, MaterializationKind,
|
||||
NormalizedVisitor, Type, TypeContext, TypeMapping, TypeRelation, TypeVarBoundOrConstraints,
|
||||
TypeVarIdentity, TypeVarInstance, TypeVarKind, TypeVarVariance, UnionType, declaration_type,
|
||||
walk_type_var_bounds,
|
||||
ClassLiteral, FindLegacyTypeVarsVisitor, IntersectionType, KnownClass, KnownInstanceType,
|
||||
MaterializationKind, NormalizedVisitor, Type, TypeContext, TypeMapping,
|
||||
TypeVarBoundOrConstraints, TypeVarIdentity, TypeVarInstance, TypeVarKind, TypeVarVariance,
|
||||
UnionType, declaration_type, walk_type_var_bounds,
|
||||
};
|
||||
use crate::{Db, FxOrderMap, FxOrderSet};
|
||||
|
||||
@@ -202,7 +204,7 @@ impl<'a, 'db> InferableTypeVars<'a, 'db> {
|
||||
/// # Ordering
|
||||
/// Ordering is based on the context's salsa-assigned id and not on its values.
|
||||
/// The id may change between runs, or when the context was garbage collected and recreated.
|
||||
#[salsa::interned(debug, constructor=new_internal, heap_size=GenericContext::heap_size)]
|
||||
#[salsa::interned(debug, constructor=new_internal, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct GenericContext<'db> {
|
||||
#[returns(ref)]
|
||||
@@ -689,12 +691,6 @@ impl<'db> GenericContext<'db> {
|
||||
|
||||
Self::from_typevar_instances(db, variables)
|
||||
}
|
||||
|
||||
fn heap_size(
|
||||
(variables,): &(FxOrderMap<BoundTypeVarIdentity<'db>, BoundTypeVarInstance<'db>>,),
|
||||
) -> usize {
|
||||
ruff_memory_usage::order_map_heap_size(variables)
|
||||
}
|
||||
}
|
||||
|
||||
fn inferable_typevars_cycle_initial<'db>(
|
||||
@@ -1647,20 +1643,23 @@ impl<'db> SpecializationBuilder<'db> {
|
||||
mappings.clear();
|
||||
for (constraint, _) in path {
|
||||
let typevar = constraint.typevar(self.db);
|
||||
let lower = constraint.lower(self.db);
|
||||
let upper = constraint.upper(self.db);
|
||||
let bound = constraint.bound(self.db);
|
||||
let bounds = mappings.entry(typevar).or_default();
|
||||
bounds.lower.insert(lower);
|
||||
bounds.upper.insert(upper);
|
||||
|
||||
if let Type::TypeVar(lower_bound_typevar) = lower {
|
||||
let bounds = mappings.entry(lower_bound_typevar).or_default();
|
||||
bounds.upper.insert(Type::TypeVar(typevar));
|
||||
}
|
||||
|
||||
if let Type::TypeVar(upper_bound_typevar) = upper {
|
||||
let bounds = mappings.entry(upper_bound_typevar).or_default();
|
||||
bounds.lower.insert(Type::TypeVar(typevar));
|
||||
match bound {
|
||||
ConstraintBound::Lower(lower) => {
|
||||
bounds.lower.insert(lower);
|
||||
if let Type::TypeVar(lower_bound_typevar) = lower {
|
||||
let bounds = mappings.entry(lower_bound_typevar).or_default();
|
||||
bounds.upper.insert(Type::TypeVar(typevar));
|
||||
}
|
||||
}
|
||||
ConstraintBound::Upper(upper) => {
|
||||
bounds.upper.insert(upper);
|
||||
if let Type::TypeVar(upper_bound_typevar) = upper {
|
||||
let bounds = mappings.entry(upper_bound_typevar).or_default();
|
||||
bounds.lower.insert(Type::TypeVar(typevar));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2797,6 +2797,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
body: _,
|
||||
} = class_node;
|
||||
|
||||
let mut decorator_types_and_nodes: Vec<(Type<'db>, &ast::Decorator)> =
|
||||
Vec::with_capacity(decorator_list.len());
|
||||
let mut deprecated = None;
|
||||
let mut type_check_only = false;
|
||||
let mut dataclass_params = None;
|
||||
@@ -2831,6 +2833,20 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip identity decorators to avoid salsa cycles on typeshed.
|
||||
if decorator_ty.as_function_literal().is_some_and(|function| {
|
||||
matches!(
|
||||
function.known(self.db()),
|
||||
Some(
|
||||
KnownFunction::Final
|
||||
| KnownFunction::DisjointBase
|
||||
| KnownFunction::RuntimeCheckable
|
||||
)
|
||||
)
|
||||
}) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Type::FunctionLiteral(f) = decorator_ty {
|
||||
// We do not yet detect or flag `@dataclass_transform` applied to more than one
|
||||
// overload, or an overload and the implementation both. Nevertheless, this is not
|
||||
@@ -2852,6 +2868,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
dataclass_transformer_params = Some(params);
|
||||
continue;
|
||||
}
|
||||
|
||||
decorator_types_and_nodes.push((decorator_ty, decorator));
|
||||
}
|
||||
|
||||
let body_scope = self
|
||||
@@ -2868,7 +2886,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
)
|
||||
};
|
||||
|
||||
let ty = match (maybe_known_class, &*name.id) {
|
||||
let inferred_ty = match (maybe_known_class, &*name.id) {
|
||||
(None, "NamedTuple") if in_typing_module() => {
|
||||
Type::SpecialForm(SpecialFormType::NamedTuple)
|
||||
}
|
||||
@@ -2885,10 +2903,19 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
)),
|
||||
};
|
||||
|
||||
// Validate decorator calls (but don't use return types yet).
|
||||
for (decorator_ty, decorator_node) in decorator_types_and_nodes.iter().rev() {
|
||||
if let Err(CallError(_, bindings)) =
|
||||
decorator_ty.try_call(self.db(), &CallArguments::positional([inferred_ty]))
|
||||
{
|
||||
bindings.report_diagnostics(&self.context, (*decorator_node).into());
|
||||
}
|
||||
}
|
||||
|
||||
self.add_declaration_with_binding(
|
||||
class_node.into(),
|
||||
definition,
|
||||
&DeclaredAndInferredType::are_the_same_type(ty),
|
||||
&DeclaredAndInferredType::are_the_same_type(inferred_ty),
|
||||
);
|
||||
|
||||
// if there are type parameters, then the keywords and bases are within that scope
|
||||
|
||||
@@ -1083,13 +1083,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
&mut self.inner_expression_inference_state,
|
||||
InnerExpressionInferenceState::Get,
|
||||
);
|
||||
let union = union
|
||||
.elements(self.db())
|
||||
.iter()
|
||||
.fold(UnionBuilder::new(self.db()), |builder, elem| {
|
||||
builder.add(self.infer_subscript_type_expression(subscript, *elem))
|
||||
})
|
||||
.build();
|
||||
let union = union.map(self.db(), |element| {
|
||||
self.infer_subscript_type_expression(subscript, *element)
|
||||
});
|
||||
self.inner_expression_inference_state = previous_slice_inference_state;
|
||||
union
|
||||
}
|
||||
|
||||
@@ -11,14 +11,15 @@ use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension};
|
||||
use crate::types::enums::is_single_member_enum;
|
||||
use crate::types::generics::{InferableTypeVars, walk_specialization};
|
||||
use crate::types::protocol_class::{ProtocolClass, walk_protocol_interface};
|
||||
use crate::types::relation::{
|
||||
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, TypeRelation,
|
||||
};
|
||||
use crate::types::tuple::{TupleSpec, TupleType, walk_tuple_type};
|
||||
use crate::types::{
|
||||
ApplyTypeMappingVisitor, ClassBase, ClassLiteral, FindLegacyTypeVarsVisitor,
|
||||
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, NormalizedVisitor, TypeContext,
|
||||
TypeMapping, TypeRelation, VarianceInferable,
|
||||
ApplyTypeMappingVisitor, ClassBase, ClassLiteral, FindLegacyTypeVarsVisitor, NormalizedVisitor,
|
||||
TypeContext, TypeMapping, VarianceInferable,
|
||||
};
|
||||
use crate::{Db, FxOrderSet};
|
||||
|
||||
pub(super) use synthesized_protocol::SynthesizedProtocolType;
|
||||
|
||||
impl<'db> Type<'db> {
|
||||
|
||||
@@ -926,10 +926,7 @@ impl<'db, 'ast> NarrowingConstraintsBuilder<'db, 'ast> {
|
||||
.build();
|
||||
|
||||
// Keep order: first literal complement, then broader arms.
|
||||
let result = UnionBuilder::new(self.db)
|
||||
.add(narrowed_single)
|
||||
.add(rest_union)
|
||||
.build();
|
||||
let result = UnionType::from_elements(self.db, [narrowed_single, rest_union]);
|
||||
Some(result)
|
||||
} else {
|
||||
None
|
||||
|
||||
@@ -6,6 +6,7 @@ use itertools::Itertools;
|
||||
use ruff_python_ast::name::Name;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::types::relation::{HasRelationToVisitor, IsDisjointVisitor, TypeRelation};
|
||||
use crate::types::{CallableTypeKind, TypeContext};
|
||||
use crate::{
|
||||
Db, FxOrderSet,
|
||||
@@ -13,10 +14,9 @@ use crate::{
|
||||
semantic_index::{definition::Definition, place::ScopedPlaceId, place_table, use_def_map},
|
||||
types::{
|
||||
ApplyTypeMappingVisitor, BoundTypeVarInstance, CallableType, ClassBase, ClassLiteral,
|
||||
ClassType, FindLegacyTypeVarsVisitor, HasRelationToVisitor,
|
||||
InstanceFallbackShadowsNonDataDescriptor, IsDisjointVisitor, KnownFunction,
|
||||
MemberLookupPolicy, NormalizedVisitor, PropertyInstanceType, Signature, Type, TypeMapping,
|
||||
TypeQualifiers, TypeRelation, TypeVarVariance, VarianceInferable,
|
||||
ClassType, FindLegacyTypeVarsVisitor, InstanceFallbackShadowsNonDataDescriptor,
|
||||
KnownFunction, MemberLookupPolicy, NormalizedVisitor, PropertyInstanceType, Signature,
|
||||
Type, TypeMapping, TypeQualifiers, TypeVarVariance, VarianceInferable,
|
||||
constraints::{ConstraintSet, IteratorConstraintsExtension, OptionConstraintsExtension},
|
||||
context::InferContext,
|
||||
diagnostic::report_undeclared_protocol_member,
|
||||
|
||||
2384
crates/ty_python_semantic/src/types/relation.rs
Normal file
2384
crates/ty_python_semantic/src/types/relation.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -23,11 +23,13 @@ use crate::types::constraints::{
|
||||
};
|
||||
use crate::types::generics::{GenericContext, InferableTypeVars, walk_generic_context};
|
||||
use crate::types::infer::{infer_deferred_types, infer_scope_types};
|
||||
use crate::types::relation::{
|
||||
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, TypeRelation,
|
||||
};
|
||||
use crate::types::{
|
||||
ApplyTypeMappingVisitor, BindingContext, BoundTypeVarInstance, CallableType, CallableTypeKind,
|
||||
FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor,
|
||||
KnownClass, MaterializationKind, NormalizedVisitor, ParamSpecAttrKind, TypeContext,
|
||||
TypeMapping, TypeRelation, VarianceInferable, todo_type,
|
||||
FindLegacyTypeVarsVisitor, KnownClass, MaterializationKind, NormalizedVisitor,
|
||||
ParamSpecAttrKind, TypeContext, TypeMapping, VarianceInferable, todo_type,
|
||||
};
|
||||
use crate::{Db, FxOrderSet};
|
||||
use ruff_python_ast::{self as ast, name::Name};
|
||||
@@ -403,11 +405,10 @@ impl<'db> CallableSignature<'db> {
|
||||
Some((self_bound_typevar, self_return_type)),
|
||||
Some((other_bound_typevar, other_return_type)),
|
||||
) => {
|
||||
let param_spec_matches = ConstraintSet::constrain_typevar(
|
||||
let param_spec_matches = ConstraintSet::equality_constraint(
|
||||
db,
|
||||
self_bound_typevar,
|
||||
Type::TypeVar(other_bound_typevar),
|
||||
Type::TypeVar(other_bound_typevar),
|
||||
);
|
||||
let return_types_match = self_return_type.zip(other_return_type).when_some_and(
|
||||
|(self_return_type, other_return_type)| {
|
||||
@@ -433,12 +434,8 @@ impl<'db> CallableSignature<'db> {
|
||||
)),
|
||||
CallableTypeKind::ParamSpecValue,
|
||||
));
|
||||
let param_spec_matches = ConstraintSet::constrain_typevar(
|
||||
db,
|
||||
self_bound_typevar,
|
||||
Type::Never,
|
||||
upper,
|
||||
);
|
||||
let param_spec_matches =
|
||||
ConstraintSet::upper_bound_constraint(db, self_bound_typevar, upper);
|
||||
let return_types_match = self_return_type.when_some_and(|self_return_type| {
|
||||
other_signatures
|
||||
.iter()
|
||||
@@ -466,12 +463,8 @@ impl<'db> CallableSignature<'db> {
|
||||
)),
|
||||
CallableTypeKind::ParamSpecValue,
|
||||
));
|
||||
let param_spec_matches = ConstraintSet::constrain_typevar(
|
||||
db,
|
||||
other_bound_typevar,
|
||||
lower,
|
||||
Type::object(),
|
||||
);
|
||||
let param_spec_matches =
|
||||
ConstraintSet::lower_bound_constraint(db, other_bound_typevar, lower);
|
||||
let return_types_match = other_return_type.when_some_and(|other_return_type| {
|
||||
self_signatures
|
||||
.iter()
|
||||
@@ -1118,7 +1111,7 @@ impl<'db> Signature<'db> {
|
||||
CallableTypeKind::ParamSpecValue,
|
||||
));
|
||||
let param_spec_matches =
|
||||
ConstraintSet::constrain_typevar(db, self_bound_typevar, Type::Never, upper);
|
||||
ConstraintSet::upper_bound_constraint(db, self_bound_typevar, upper);
|
||||
let return_types_match = self.return_ty.when_some_and(|self_return_type| {
|
||||
other
|
||||
.overloads
|
||||
@@ -1360,11 +1353,10 @@ impl<'db> Signature<'db> {
|
||||
// the other signature.
|
||||
match (self_is_paramspec, other_is_paramspec) {
|
||||
(Some(self_bound_typevar), Some(other_bound_typevar)) => {
|
||||
let param_spec_matches = ConstraintSet::constrain_typevar(
|
||||
let param_spec_matches = ConstraintSet::equality_constraint(
|
||||
db,
|
||||
self_bound_typevar,
|
||||
Type::TypeVar(other_bound_typevar),
|
||||
Type::TypeVar(other_bound_typevar),
|
||||
);
|
||||
result.intersect(db, param_spec_matches);
|
||||
return result;
|
||||
@@ -1376,12 +1368,8 @@ impl<'db> Signature<'db> {
|
||||
CallableSignature::single(Signature::new(other.parameters.clone(), None)),
|
||||
CallableTypeKind::ParamSpecValue,
|
||||
));
|
||||
let param_spec_matches = ConstraintSet::constrain_typevar(
|
||||
db,
|
||||
self_bound_typevar,
|
||||
Type::Never,
|
||||
upper,
|
||||
);
|
||||
let param_spec_matches =
|
||||
ConstraintSet::upper_bound_constraint(db, self_bound_typevar, upper);
|
||||
result.intersect(db, param_spec_matches);
|
||||
return result;
|
||||
}
|
||||
@@ -1392,12 +1380,8 @@ impl<'db> Signature<'db> {
|
||||
CallableSignature::single(Signature::new(self.parameters.clone(), None)),
|
||||
CallableTypeKind::ParamSpecValue,
|
||||
));
|
||||
let param_spec_matches = ConstraintSet::constrain_typevar(
|
||||
db,
|
||||
other_bound_typevar,
|
||||
lower,
|
||||
Type::object(),
|
||||
);
|
||||
let param_spec_matches =
|
||||
ConstraintSet::lower_bound_constraint(db, other_bound_typevar, lower);
|
||||
result.intersect(db, param_spec_matches);
|
||||
return result;
|
||||
}
|
||||
@@ -1860,7 +1844,7 @@ impl<'db> Parameters<'db> {
|
||||
///
|
||||
/// Internally, this is represented as `(*Any, **Any)` that accepts parameters of type [`Any`].
|
||||
///
|
||||
/// [`Any`]: crate::types::DynamicType::Any
|
||||
/// [`Any`]: DynamicType::Any
|
||||
pub(crate) fn gradual_form() -> Self {
|
||||
Self {
|
||||
value: vec![
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
use super::TypeVarVariance;
|
||||
use crate::place::PlaceAndQualifiers;
|
||||
use crate::semantic_index::definition::Definition;
|
||||
use crate::types::constraints::ConstraintSet;
|
||||
use crate::types::generics::InferableTypeVars;
|
||||
use crate::types::protocol_class::ProtocolClass;
|
||||
use crate::types::relation::{HasRelationToVisitor, IsDisjointVisitor, TypeRelation};
|
||||
use crate::types::variance::VarianceInferable;
|
||||
use crate::types::{
|
||||
ApplyTypeMappingVisitor, BoundTypeVarInstance, ClassType, DynamicType,
|
||||
FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsDisjointVisitor, KnownClass,
|
||||
MaterializationKind, MemberLookupPolicy, NormalizedVisitor, SpecialFormType, Type, TypeContext,
|
||||
TypeMapping, TypeRelation, TypeVarBoundOrConstraints, TypedDictType, UnionType, todo_type,
|
||||
FindLegacyTypeVarsVisitor, KnownClass, MaterializationKind, MemberLookupPolicy,
|
||||
NormalizedVisitor, SpecialFormType, Type, TypeContext, TypeMapping, TypeVarBoundOrConstraints,
|
||||
TypedDictType, UnionType, todo_type,
|
||||
};
|
||||
use crate::{Db, FxOrderSet};
|
||||
|
||||
use super::TypeVarVariance;
|
||||
|
||||
/// A type that represents `type[C]`, i.e. the class object `C` and class objects that are subclasses of `C`.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)]
|
||||
pub struct SubclassOfType<'db> {
|
||||
|
||||
@@ -28,10 +28,12 @@ use crate::types::builder::RecursivelyDefined;
|
||||
use crate::types::class::{ClassType, KnownClass};
|
||||
use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension};
|
||||
use crate::types::generics::InferableTypeVars;
|
||||
use crate::types::relation::{
|
||||
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, TypeRelation,
|
||||
};
|
||||
use crate::types::{
|
||||
ApplyTypeMappingVisitor, BoundTypeVarInstance, FindLegacyTypeVarsVisitor, HasRelationToVisitor,
|
||||
IntersectionType, IsDisjointVisitor, IsEquivalentVisitor, NormalizedVisitor, Type, TypeMapping,
|
||||
TypeRelation, UnionBuilder, UnionType,
|
||||
ApplyTypeMappingVisitor, BoundTypeVarInstance, FindLegacyTypeVarsVisitor, IntersectionType,
|
||||
NormalizedVisitor, Type, TypeMapping, UnionBuilder, UnionType,
|
||||
};
|
||||
use crate::types::{Truthiness, TypeContext};
|
||||
use crate::{Db, FxOrderSet, Program};
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::collections::BTreeMap;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use bitflags::bitflags;
|
||||
use ordermap::OrderSet;
|
||||
use ruff_db::diagnostic::{Annotation, Diagnostic, Span, SubDiagnostic, SubDiagnosticSeverity};
|
||||
use ruff_db::parsed::parsed_module;
|
||||
use ruff_python_ast::Arguments;
|
||||
@@ -21,12 +22,10 @@ use crate::semantic_index::definition::Definition;
|
||||
use crate::types::class::FieldKind;
|
||||
use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension};
|
||||
use crate::types::generics::InferableTypeVars;
|
||||
use crate::types::{
|
||||
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, NormalizedVisitor, TypeContext,
|
||||
TypeRelation,
|
||||
use crate::types::relation::{
|
||||
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, TypeRelation,
|
||||
};
|
||||
|
||||
use ordermap::OrderSet;
|
||||
use crate::types::{NormalizedVisitor, TypeContext};
|
||||
|
||||
bitflags! {
|
||||
/// Used for `TypedDict` class parameters.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
use crate::{
|
||||
Db, FxIndexSet,
|
||||
Db,
|
||||
types::{
|
||||
BoundMethodType, BoundSuperType, BoundTypeVarInstance, CallableType, GenericAlias,
|
||||
IntersectionType, KnownBoundMethodType, KnownInstanceType, NominalInstanceType,
|
||||
@@ -277,7 +279,7 @@ pub(crate) fn walk_type_with_recursion_guard<'db>(
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub(crate) struct TypeCollector<'db>(RefCell<FxIndexSet<Type<'db>>>);
|
||||
pub(crate) struct TypeCollector<'db>(RefCell<FxHashSet<Type<'db>>>);
|
||||
|
||||
impl<'db> TypeCollector<'db> {
|
||||
pub(crate) fn type_was_already_seen(&self, ty: Type<'db>) -> bool {
|
||||
|
||||
7
playground/api/package-lock.json
generated
7
playground/api/package-lock.json
generated
@@ -118,9 +118,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@cloudflare/workers-types": {
|
||||
"version": "4.20251225.0",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20251225.0.tgz",
|
||||
"integrity": "sha512-ZZl0cNLFcsBRFKtMftKWOsfAybUYSeiTMzpQV1NlTVlByHAs1rGQt45Jw/qz8LrfHoq9PGTieSj9W350Gi4Pvg==",
|
||||
"version": "4.20251229.0",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20251229.0.tgz",
|
||||
"integrity": "sha512-LgzxDZaT9bQhycQInf7S/fcZCQRTvWWQPE9xnEyedI+CXxWsXAD7hg84kvVyr+KUz+W9Oblzo75g6XZ3HdI5Yg==",
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"peer": true
|
||||
@@ -1814,6 +1814,7 @@
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"workerd": "bin/workerd"
|
||||
},
|
||||
|
||||
32
ruff.schema.json
generated
32
ruff.schema.json
generated
@@ -1880,6 +1880,38 @@
|
||||
"object",
|
||||
"null"
|
||||
],
|
||||
"properties": {
|
||||
"first-party": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"future": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"local-folder": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"standard-library": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"third-party": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
set -eu
|
||||
|
||||
docstring_adder="git+https://github.com/astral-sh/docstring-adder.git@1a0fb336fdc85014b22daeb34c862b695aef07d4"
|
||||
docstring_adder="git+https://github.com/astral-sh/docstring-adder.git@e98a04941d5a6b8b9240e40392de15990b8cb8be"
|
||||
stdlib_path="./crates/ty_vendored/vendor/typeshed/stdlib"
|
||||
|
||||
for python_version in 3.14 3.13 3.12 3.11 3.10 3.9
|
||||
|
||||
@@ -92,7 +92,7 @@ class Project(NamedTuple):
|
||||
)
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise RuntimeError(f"Failed to clone {self.name}: {e.stderr}")
|
||||
raise RuntimeError(f"Failed to clone {self.name}:\n\n{e.stderr}") from e
|
||||
|
||||
logging.info(f"Cloned {self.name} to {checkout_dir}.")
|
||||
|
||||
|
||||
@@ -147,7 +147,9 @@ def main() -> None:
|
||||
cwd = Path(tempdir)
|
||||
project.clone(cwd)
|
||||
|
||||
venv = Venv.create(cwd, project.python_version)
|
||||
venv = Venv.create(
|
||||
project=project.name, parent=cwd, python_version=project.python_version
|
||||
)
|
||||
venv.install(project.install_arguments)
|
||||
|
||||
commands = []
|
||||
|
||||
@@ -42,7 +42,9 @@ def project_setup(
|
||||
cwd = Path(tempdir)
|
||||
project.clone(cwd)
|
||||
|
||||
venv = Venv.create(cwd, project.python_version)
|
||||
venv = Venv.create(
|
||||
project=project.name, parent=cwd, python_version=project.python_version
|
||||
)
|
||||
venv.install(project.install_arguments)
|
||||
|
||||
yield project, venv
|
||||
|
||||
@@ -3,15 +3,15 @@ from __future__ import annotations
|
||||
import logging
|
||||
import subprocess
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True, slots=True)
|
||||
class Venv:
|
||||
project_name: str
|
||||
project_path: Path
|
||||
|
||||
def __init__(self, path: Path):
|
||||
self.project_path = path
|
||||
|
||||
@property
|
||||
def path(self) -> Path:
|
||||
return self.project_path / "venv"
|
||||
@@ -36,7 +36,7 @@ class Venv:
|
||||
return self.bin / f"{name}{extension}"
|
||||
|
||||
@staticmethod
|
||||
def create(parent: Path, python_version: str) -> Venv:
|
||||
def create(*, project: str, parent: Path, python_version: str) -> Venv:
|
||||
"""Creates a new, empty virtual environment."""
|
||||
|
||||
command = [
|
||||
@@ -53,9 +53,10 @@ class Venv:
|
||||
command, cwd=parent, check=True, capture_output=True, text=True
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise RuntimeError(f"Failed to create venv: {e.stderr}")
|
||||
msg = f"Failed to create venv for {project}:\n\n{e.stderr}"
|
||||
raise RuntimeError(msg) from e
|
||||
|
||||
return Venv(parent)
|
||||
return Venv(project_name=project, project_path=parent)
|
||||
|
||||
def install(self, pip_install_args: list[str]) -> None:
|
||||
"""Installs the dependencies required to type check the project."""
|
||||
@@ -87,4 +88,7 @@ class Venv:
|
||||
text=True,
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise RuntimeError(f"Failed to install dependencies: {e.stderr}")
|
||||
msg = (
|
||||
f"Failed to install dependencies for {self.project_name}:\n\n{e.stderr}"
|
||||
)
|
||||
raise RuntimeError(msg) from e
|
||||
|
||||
Reference in New Issue
Block a user