mirror of https://github.com/astral-sh/ruff
[ty] Infer types for key-based access on TypedDicts (#19763)
## Summary
This PR adds type inference for key-based access on `TypedDict`s and a
new diagnostic for invalid subscript accesses:
```py
class Person(TypedDict):
name: str
age: int | None
alice = Person(name="Alice", age=25)
reveal_type(alice["name"]) # revealed: str
reveal_type(alice["age"]) # revealed: int | None
alice["naem"] # Unknown key "naem" - did you mean "name"?
```
## Test Plan
Updated Markdown tests
This commit is contained in:
parent
e917d309f1
commit
4887bdf205
|
|
@ -4310,6 +4310,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
|
"strsim",
|
||||||
"strum",
|
"strum",
|
||||||
"strum_macros",
|
"strum_macros",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ def test(): -> "int":
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20call-non-callable) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20call-non-callable) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L99)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L100)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -58,7 +58,7 @@ Calling a non-callable object will raise a `TypeError` at runtime.
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-argument-forms) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-argument-forms) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L143)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L144)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -88,7 +88,7 @@ f(int) # error
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-declarations) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-declarations) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L169)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L170)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -117,7 +117,7 @@ a = 1
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-metaclass) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-metaclass) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L194)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L195)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -147,7 +147,7 @@ class C(A, B): ...
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20cyclic-class-definition) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20cyclic-class-definition) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L220)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L221)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -177,7 +177,7 @@ class B(A): ...
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20duplicate-base) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20duplicate-base) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L285)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L286)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -202,7 +202,7 @@ class B(A, A): ...
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20duplicate-kw-only) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20duplicate-kw-only) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L306)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L307)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -306,7 +306,7 @@ def test(): -> "Literal[5]":
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20inconsistent-mro) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20inconsistent-mro) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L448)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L449)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -334,7 +334,7 @@ class C(A, B): ...
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20index-out-of-bounds) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20index-out-of-bounds) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L472)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L473)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -358,7 +358,7 @@ t[3] # IndexError: tuple index out of range
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20instance-layout-conflict) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20instance-layout-conflict) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L338)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L339)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -445,7 +445,7 @@ an atypical memory layout.
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-argument-type) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-argument-type) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L492)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L518)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -470,7 +470,7 @@ func("foo") # error: [invalid-argument-type]
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-assignment) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-assignment) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L532)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L558)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -496,7 +496,7 @@ a: int = ''
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-attribute-access) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-attribute-access) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1536)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1562)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -528,7 +528,7 @@ C.instance_var = 3 # error: Cannot assign to instance variable
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-base) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-base) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L554)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L580)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -550,7 +550,7 @@ class A(42): ... # error: [invalid-base]
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-context-manager) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-context-manager) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L605)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L631)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -575,7 +575,7 @@ with 1:
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-declaration) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-declaration) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L626)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L652)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -602,7 +602,7 @@ a: str
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-exception-caught) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-exception-caught) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L649)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L675)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -644,7 +644,7 @@ except ZeroDivisionError:
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-generic-class) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-generic-class) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L685)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L711)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -670,12 +670,41 @@ class C[U](Generic[T]): ...
|
||||||
|
|
||||||
- [Typing spec: Generics](https://typing.python.org/en/latest/spec/generics.html#introduction)
|
- [Typing spec: Generics](https://typing.python.org/en/latest/spec/generics.html#introduction)
|
||||||
|
|
||||||
|
## `invalid-key`
|
||||||
|
|
||||||
|
<small>
|
||||||
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-key) ·
|
||||||
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L493)
|
||||||
|
</small>
|
||||||
|
|
||||||
|
**What it does**
|
||||||
|
|
||||||
|
Checks for subscript accesses with invalid keys.
|
||||||
|
|
||||||
|
**Why is this bad?**
|
||||||
|
|
||||||
|
Using an invalid key will raise a `KeyError` at runtime.
|
||||||
|
|
||||||
|
**Examples**
|
||||||
|
|
||||||
|
```python
|
||||||
|
from typing import TypedDict
|
||||||
|
|
||||||
|
class Person(TypedDict):
|
||||||
|
name: str
|
||||||
|
age: int
|
||||||
|
|
||||||
|
alice = Person(name="Alice", age=30)
|
||||||
|
alice["height"] # KeyError: 'height'
|
||||||
|
```
|
||||||
|
|
||||||
## `invalid-legacy-type-variable`
|
## `invalid-legacy-type-variable`
|
||||||
|
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-legacy-type-variable) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-legacy-type-variable) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L711)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L737)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -708,7 +737,7 @@ def f(t: TypeVar("U")): ...
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-metaclass) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-metaclass) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L760)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L786)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -740,7 +769,7 @@ class B(metaclass=f): ...
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-overload) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-overload) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L787)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L813)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -788,7 +817,7 @@ def foo(x: int) -> int: ...
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-parameter-default) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-parameter-default) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L830)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L856)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -812,7 +841,7 @@ def f(a: int = ''): ...
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-protocol) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-protocol) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L420)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L421)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -844,7 +873,7 @@ TypeError: Protocols can only inherit from other protocols, got <class 'int'>
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-raise) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-raise) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L850)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L876)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
Checks for `raise` statements that raise non-exceptions or use invalid
|
Checks for `raise` statements that raise non-exceptions or use invalid
|
||||||
|
|
@ -891,7 +920,7 @@ def g():
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-return-type) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-return-type) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L513)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L539)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -914,7 +943,7 @@ def func() -> int:
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-super-argument) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-super-argument) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L893)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L919)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -968,7 +997,7 @@ TODO #14889
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-alias-type) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-alias-type) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L739)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L765)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -993,7 +1022,7 @@ NewAlias = TypeAliasType(get_name(), int) # error: TypeAliasType name mus
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-checking-constant) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-checking-constant) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L932)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L958)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1021,7 +1050,7 @@ TYPE_CHECKING = ''
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-form) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-form) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L956)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L982)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1049,7 +1078,7 @@ b: Annotated[int] # `Annotated` expects at least two arguments
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-guard-call) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-guard-call) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1008)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1034)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1081,7 +1110,7 @@ f(10) # Error
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-guard-definition) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-guard-definition) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L980)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1006)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1113,7 +1142,7 @@ class C:
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-variable-constraints) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-variable-constraints) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1036)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1062)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1146,7 +1175,7 @@ T = TypeVar('T', bound=str) # valid bound TypeVar
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20missing-argument) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20missing-argument) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1065)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1091)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1169,7 +1198,7 @@ func() # TypeError: func() missing 1 required positional argument: 'x'
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20no-matching-overload) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20no-matching-overload) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1084)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1110)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1196,7 +1225,7 @@ func("string") # error: [no-matching-overload]
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20non-subscriptable) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20non-subscriptable) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1107)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1133)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1218,7 +1247,7 @@ Subscripting an object that does not support it will raise a `TypeError` at runt
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20not-iterable) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20not-iterable) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1125)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1151)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1242,7 +1271,7 @@ for i in 34: # TypeError: 'int' object is not iterable
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20parameter-already-assigned) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20parameter-already-assigned) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1176)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1202)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1296,7 +1325,7 @@ def test(): -> "int":
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20static-assert-error) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20static-assert-error) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1512)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1538)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1324,7 +1353,7 @@ static_assert(int(2.0 * 3.0) == 6) # error: does not have a statically known tr
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20subclass-of-final-class) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20subclass-of-final-class) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1267)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1293)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1351,7 +1380,7 @@ class B(A): ... # Error raised here
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20too-many-positional-arguments) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20too-many-positional-arguments) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1312)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1338)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1376,7 +1405,7 @@ f("foo") # Error raised here
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20type-assertion-failure) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20type-assertion-failure) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1290)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1316)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1402,7 +1431,7 @@ def _(x: int):
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unavailable-implicit-super-arguments) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unavailable-implicit-super-arguments) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1333)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1359)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1446,7 +1475,7 @@ class A:
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unknown-argument) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unknown-argument) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1390)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1416)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1471,7 +1500,7 @@ f(x=1, y=2) # Error raised here
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-attribute) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-attribute) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1411)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1437)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1497,7 +1526,7 @@ A().foo # AttributeError: 'A' object has no attribute 'foo'
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-import) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-import) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1433)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1459)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1520,7 +1549,7 @@ import foo # ModuleNotFoundError: No module named 'foo'
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-reference) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-reference) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1452)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1478)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1543,7 +1572,7 @@ print(x) # NameError: name 'x' is not defined
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-bool-conversion) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-bool-conversion) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1145)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1171)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1578,7 +1607,7 @@ b1 < b2 < b1 # exception raised here
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-operator) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-operator) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1471)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1497)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1604,7 +1633,7 @@ A() + A() # TypeError: unsupported operand type(s) for +: 'A' and 'A'
|
||||||
<small>
|
<small>
|
||||||
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
Default level: [`error`](../rules.md#rule-levels "This lint has a default level of 'error'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20zero-stepsize-in-slice) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20zero-stepsize-in-slice) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1493)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1519)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1627,7 +1656,7 @@ l[1:10:0] # ValueError: slice step cannot be zero
|
||||||
<small>
|
<small>
|
||||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20deprecated) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20deprecated) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L264)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L265)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1680,7 +1709,7 @@ a = 20 / 0 # type: ignore
|
||||||
<small>
|
<small>
|
||||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unbound-attribute) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unbound-attribute) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1197)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1223)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1706,7 +1735,7 @@ A.c # AttributeError: type object 'A' has no attribute 'c'
|
||||||
<small>
|
<small>
|
||||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unbound-implicit-call) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unbound-implicit-call) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L117)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L118)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1736,7 +1765,7 @@ A()[0] # TypeError: 'A' object is not subscriptable
|
||||||
<small>
|
<small>
|
||||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unbound-import) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unbound-import) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1219)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1245)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1766,7 +1795,7 @@ from module import a # ImportError: cannot import name 'a' from 'module'
|
||||||
<small>
|
<small>
|
||||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20redundant-cast) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20redundant-cast) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1564)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1590)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1791,7 +1820,7 @@ cast(int, f()) # Redundant
|
||||||
<small>
|
<small>
|
||||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20undefined-reveal) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20undefined-reveal) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1372)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1398)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1842,7 +1871,7 @@ a = 20 / 0 # ty: ignore[division-by-zero]
|
||||||
<small>
|
<small>
|
||||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-global) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-global) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1585)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1611)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1896,7 +1925,7 @@ def g():
|
||||||
<small>
|
<small>
|
||||||
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
Default level: [`warn`](../rules.md#rule-levels "This lint has a default level of 'warn'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-base) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-base) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L572)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L598)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1933,7 +1962,7 @@ class D(C): ... # error: [unsupported-base]
|
||||||
<small>
|
<small>
|
||||||
Default level: [`ignore`](../rules.md#rule-levels "This lint has a default level of 'ignore'.") ·
|
Default level: [`ignore`](../rules.md#rule-levels "This lint has a default level of 'ignore'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20division-by-zero) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20division-by-zero) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L246)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L247)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
@ -1955,7 +1984,7 @@ Dividing by zero raises a `ZeroDivisionError` at runtime.
|
||||||
<small>
|
<small>
|
||||||
Default level: [`ignore`](../rules.md#rule-levels "This lint has a default level of 'ignore'.") ·
|
Default level: [`ignore`](../rules.md#rule-levels "This lint has a default level of 'ignore'.") ·
|
||||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unresolved-reference) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unresolved-reference) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1245)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1271)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ test-case = { workspace = true }
|
||||||
memchr = { workspace = true }
|
memchr = { workspace = true }
|
||||||
strum = { workspace = true }
|
strum = { workspace = true }
|
||||||
strum_macros = { workspace = true }
|
strum_macros = { workspace = true }
|
||||||
|
strsim = "0.11.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
ruff_db = { workspace = true, features = ["testing", "os"] }
|
ruff_db = { workspace = true, features = ["testing", "os"] }
|
||||||
|
|
|
||||||
|
|
@ -244,8 +244,7 @@ class D(TypedDict):
|
||||||
|
|
||||||
td = D(x=1, label="a")
|
td = D(x=1, label="a")
|
||||||
td["x"] = 0
|
td["x"] = 0
|
||||||
# TODO: should be Literal[0]
|
reveal_type(td["x"]) # revealed: Literal[0]
|
||||||
reveal_type(td["x"]) # revealed: @Todo(Support for `TypedDict`)
|
|
||||||
|
|
||||||
# error: [unresolved-reference]
|
# error: [unresolved-reference]
|
||||||
does["not"]["exist"] = 0
|
does["not"]["exist"] = 0
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
---
|
||||||
|
source: crates/ty_test/src/lib.rs
|
||||||
|
expression: snapshot
|
||||||
|
---
|
||||||
|
---
|
||||||
|
mdtest name: typed_dict.md - `TypedDict` - Diagnostics
|
||||||
|
mdtest path: crates/ty_python_semantic/resources/mdtest/typed_dict.md
|
||||||
|
---
|
||||||
|
|
||||||
|
# Python source files
|
||||||
|
|
||||||
|
## mdtest_snippet.py
|
||||||
|
|
||||||
|
```
|
||||||
|
1 | from typing import TypedDict, Final
|
||||||
|
2 |
|
||||||
|
3 | class Person(TypedDict):
|
||||||
|
4 | name: str
|
||||||
|
5 | age: int | None
|
||||||
|
6 |
|
||||||
|
7 | def access_invalid_literal_string_key(person: Person):
|
||||||
|
8 | person["naem"] # error: [invalid-key]
|
||||||
|
9 |
|
||||||
|
10 | NAME_KEY: Final = "naem"
|
||||||
|
11 |
|
||||||
|
12 | def access_invalid_key(person: Person):
|
||||||
|
13 | person[NAME_KEY] # error: [invalid-key]
|
||||||
|
14 |
|
||||||
|
15 | def access_with_str_key(person: Person, str_key: str):
|
||||||
|
16 | person[str_key] # error: [invalid-key]
|
||||||
|
```
|
||||||
|
|
||||||
|
# Diagnostics
|
||||||
|
|
||||||
|
```
|
||||||
|
error[invalid-key]: Invalid key access on TypedDict `Person`
|
||||||
|
--> src/mdtest_snippet.py:8:5
|
||||||
|
|
|
||||||
|
7 | def access_invalid_literal_string_key(person: Person):
|
||||||
|
8 | person["naem"] # error: [invalid-key]
|
||||||
|
| ------ ^^^^^^ Unknown key "naem" - did you mean "name"?
|
||||||
|
| |
|
||||||
|
| TypedDict `Person`
|
||||||
|
9 |
|
||||||
|
10 | NAME_KEY: Final = "naem"
|
||||||
|
|
|
||||||
|
info: rule `invalid-key` is enabled by default
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
error[invalid-key]: Invalid key access on TypedDict `Person`
|
||||||
|
--> src/mdtest_snippet.py:13:5
|
||||||
|
|
|
||||||
|
12 | def access_invalid_key(person: Person):
|
||||||
|
13 | person[NAME_KEY] # error: [invalid-key]
|
||||||
|
| ------ ^^^^^^^^ Unknown key "naem" - did you mean "name"?
|
||||||
|
| |
|
||||||
|
| TypedDict `Person`
|
||||||
|
14 |
|
||||||
|
15 | def access_with_str_key(person: Person, str_key: str):
|
||||||
|
|
|
||||||
|
info: rule `invalid-key` is enabled by default
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
error[invalid-key]: TypedDict `Person` cannot be indexed with a key of type `str`
|
||||||
|
--> src/mdtest_snippet.py:16:12
|
||||||
|
|
|
||||||
|
15 | def access_with_str_key(person: Person, str_key: str):
|
||||||
|
16 | person[str_key] # error: [invalid-key]
|
||||||
|
| ^^^^^^^
|
||||||
|
|
|
||||||
|
info: rule `invalid-key` is enabled by default
|
||||||
|
|
||||||
|
```
|
||||||
|
|
@ -25,12 +25,21 @@ alice: Person = {"name": "Alice", "age": 30}
|
||||||
reveal_type(alice["name"]) # revealed: Unknown
|
reveal_type(alice["name"]) # revealed: Unknown
|
||||||
# TODO: this should be `int | None`
|
# TODO: this should be `int | None`
|
||||||
reveal_type(alice["age"]) # revealed: Unknown
|
reveal_type(alice["age"]) # revealed: Unknown
|
||||||
|
|
||||||
|
# TODO: this should reveal `Unknown`, and it should emit an error
|
||||||
|
reveal_type(alice["non_existing"]) # revealed: Unknown
|
||||||
```
|
```
|
||||||
|
|
||||||
Inhabitants can also be created through a constructor call:
|
Inhabitants can also be created through a constructor call:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
bob = Person(name="Bob", age=25)
|
bob = Person(name="Bob", age=25)
|
||||||
|
|
||||||
|
reveal_type(bob["name"]) # revealed: str
|
||||||
|
reveal_type(bob["age"]) # revealed: int | None
|
||||||
|
|
||||||
|
# error: [invalid-key] "Invalid key access on TypedDict `Person`: Unknown key "non_existing""
|
||||||
|
reveal_type(bob["non_existing"]) # revealed: Unknown
|
||||||
```
|
```
|
||||||
|
|
||||||
Methods that are available on `dict`s are also available on `TypedDict`s:
|
Methods that are available on `dict`s are also available on `TypedDict`s:
|
||||||
|
|
@ -127,6 +136,39 @@ dangerous(alice)
|
||||||
reveal_type(alice["name"]) # revealed: Unknown
|
reveal_type(alice["name"]) # revealed: Unknown
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Key-based access
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import TypedDict, Final, Literal, Any
|
||||||
|
|
||||||
|
class Person(TypedDict):
|
||||||
|
name: str
|
||||||
|
age: int | None
|
||||||
|
|
||||||
|
NAME_FINAL: Final = "name"
|
||||||
|
AGE_FINAL: Final[Literal["age"]] = "age"
|
||||||
|
|
||||||
|
def _(person: Person, literal_key: Literal["age"], union_of_keys: Literal["age", "name"], str_key: str, unknown_key: Any) -> None:
|
||||||
|
reveal_type(person["name"]) # revealed: str
|
||||||
|
reveal_type(person["age"]) # revealed: int | None
|
||||||
|
|
||||||
|
reveal_type(person[NAME_FINAL]) # revealed: str
|
||||||
|
reveal_type(person[AGE_FINAL]) # revealed: int | None
|
||||||
|
|
||||||
|
reveal_type(person[literal_key]) # revealed: int | None
|
||||||
|
|
||||||
|
reveal_type(person[union_of_keys]) # revealed: int | None | str
|
||||||
|
|
||||||
|
# error: [invalid-key] "Invalid key access on TypedDict `Person`: Unknown key "non_existing""
|
||||||
|
reveal_type(person["non_existing"]) # revealed: Unknown
|
||||||
|
|
||||||
|
# error: [invalid-key] "TypedDict `Person` cannot be indexed with a key of type `str`"
|
||||||
|
reveal_type(person[str_key]) # revealed: Unknown
|
||||||
|
|
||||||
|
# No error here:
|
||||||
|
reveal_type(person[unknown_key]) # revealed: Unknown
|
||||||
|
```
|
||||||
|
|
||||||
## Methods on `TypedDict`
|
## Methods on `TypedDict`
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
|
@ -333,4 +375,29 @@ reveal_type(Message.__required_keys__) # revealed: @Todo(Support for `TypedDict
|
||||||
msg.content
|
msg.content
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Diagnostics
|
||||||
|
|
||||||
|
<!-- snapshot-diagnostics -->
|
||||||
|
|
||||||
|
Snapshot tests for diagnostic messages including suggestions:
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import TypedDict, Final
|
||||||
|
|
||||||
|
class Person(TypedDict):
|
||||||
|
name: str
|
||||||
|
age: int | None
|
||||||
|
|
||||||
|
def access_invalid_literal_string_key(person: Person):
|
||||||
|
person["naem"] # error: [invalid-key]
|
||||||
|
|
||||||
|
NAME_KEY: Final = "naem"
|
||||||
|
|
||||||
|
def access_invalid_key(person: Person):
|
||||||
|
person[NAME_KEY] # error: [invalid-key]
|
||||||
|
|
||||||
|
def access_with_str_key(person: Person, str_key: str):
|
||||||
|
person[str_key] # error: [invalid-key]
|
||||||
|
```
|
||||||
|
|
||||||
[`typeddict`]: https://typing.python.org/en/latest/spec/typeddict.html
|
[`typeddict`]: https://typing.python.org/en/latest/spec/typeddict.html
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ mod util;
|
||||||
#[cfg(feature = "testing")]
|
#[cfg(feature = "testing")]
|
||||||
pub mod pull_types;
|
pub mod pull_types;
|
||||||
|
|
||||||
|
type FxOrderMap<K, V> = ordermap::map::OrderMap<K, V, BuildHasherDefault<FxHasher>>;
|
||||||
type FxOrderSet<V> = ordermap::set::OrderSet<V, BuildHasherDefault<FxHasher>>;
|
type FxOrderSet<V> = ordermap::set::OrderSet<V, BuildHasherDefault<FxHasher>>;
|
||||||
type FxIndexMap<K, V> = indexmap::IndexMap<K, V, BuildHasherDefault<FxHasher>>;
|
type FxIndexMap<K, V> = indexmap::IndexMap<K, V, BuildHasherDefault<FxHasher>>;
|
||||||
type FxIndexSet<V> = indexmap::IndexSet<V, BuildHasherDefault<FxHasher>>;
|
type FxIndexSet<V> = indexmap::IndexSet<V, BuildHasherDefault<FxHasher>>;
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ use crate::semantic_index::scope::ScopeId;
|
||||||
use crate::semantic_index::{imported_modules, place_table, semantic_index};
|
use crate::semantic_index::{imported_modules, place_table, semantic_index};
|
||||||
use crate::suppression::check_suppressions;
|
use crate::suppression::check_suppressions;
|
||||||
use crate::types::call::{Binding, Bindings, CallArguments, CallableBinding};
|
use crate::types::call::{Binding, Bindings, CallArguments, CallableBinding};
|
||||||
|
use crate::types::class::{CodeGeneratorKind, Field};
|
||||||
pub(crate) use crate::types::class_base::ClassBase;
|
pub(crate) use crate::types::class_base::ClassBase;
|
||||||
use crate::types::context::{LintDiagnosticGuard, LintDiagnosticGuardBuilder};
|
use crate::types::context::{LintDiagnosticGuard, LintDiagnosticGuardBuilder};
|
||||||
use crate::types::diagnostic::{INVALID_TYPE_FORM, UNSUPPORTED_BOOL_CONVERSION};
|
use crate::types::diagnostic::{INVALID_TYPE_FORM, UNSUPPORTED_BOOL_CONVERSION};
|
||||||
|
|
@ -61,7 +62,7 @@ use crate::types::signatures::{Parameter, ParameterForm, Parameters, walk_signat
|
||||||
use crate::types::tuple::{TupleSpec, TupleType};
|
use crate::types::tuple::{TupleSpec, TupleType};
|
||||||
use crate::unpack::EvaluationMode;
|
use crate::unpack::EvaluationMode;
|
||||||
pub use crate::util::diagnostics::add_inferred_python_version_hint_to_diagnostic;
|
pub use crate::util::diagnostics::add_inferred_python_version_hint_to_diagnostic;
|
||||||
use crate::{Db, FxOrderSet, Module, Program};
|
use crate::{Db, FxOrderMap, FxOrderSet, Module, Program};
|
||||||
pub(crate) use class::{ClassLiteral, ClassType, GenericAlias, KnownClass};
|
pub(crate) use class::{ClassLiteral, ClassType, GenericAlias, KnownClass};
|
||||||
use instance::Protocol;
|
use instance::Protocol;
|
||||||
pub use instance::{NominalInstanceType, ProtocolInstanceType};
|
pub use instance::{NominalInstanceType, ProtocolInstanceType};
|
||||||
|
|
@ -669,10 +670,6 @@ impl<'db> Type<'db> {
|
||||||
matches!(self, Type::Dynamic(_))
|
matches!(self, Type::Dynamic(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) const fn is_typed_dict(&self) -> bool {
|
|
||||||
matches!(self, Type::TypedDict(..))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the top materialization (or upper bound materialization) of this type, which is the
|
/// Returns the top materialization (or upper bound materialization) of this type, which is the
|
||||||
/// most general form of the type that is fully static.
|
/// most general form of the type that is fully static.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
|
@ -834,6 +831,17 @@ impl<'db> Type<'db> {
|
||||||
.expect("Expected a Type::EnumLiteral variant")
|
.expect("Expected a Type::EnumLiteral variant")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn is_typed_dict(&self) -> bool {
|
||||||
|
matches!(self, Type::TypedDict(..))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn into_typed_dict(self) -> Option<TypedDictType<'db>> {
|
||||||
|
match self {
|
||||||
|
Type::TypedDict(typed_dict) => Some(typed_dict),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Turn a class literal (`Type::ClassLiteral` or `Type::GenericAlias`) into a `ClassType`.
|
/// Turn a class literal (`Type::ClassLiteral` or `Type::GenericAlias`) into a `ClassType`.
|
||||||
/// Since a `ClassType` must be specialized, apply the default specialization to any
|
/// Since a `ClassType` must be specialized, apply the default specialization to any
|
||||||
/// unspecialized generic class literal.
|
/// unspecialized generic class literal.
|
||||||
|
|
@ -5289,15 +5297,15 @@ impl<'db> Type<'db> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
_ if class.is_typed_dict(db) => {
|
_ if class.is_typed_dict(db) => {
|
||||||
Type::TypedDict(TypedDictType::new(db, ClassType::NonGeneric(*class)))
|
TypedDictType::from(db, ClassType::NonGeneric(*class))
|
||||||
}
|
}
|
||||||
_ => Type::instance(db, class.default_specialization(db)),
|
_ => Type::instance(db, class.default_specialization(db)),
|
||||||
};
|
};
|
||||||
Ok(ty)
|
Ok(ty)
|
||||||
}
|
}
|
||||||
Type::GenericAlias(alias) if alias.is_typed_dict(db) => Ok(Type::TypedDict(
|
Type::GenericAlias(alias) if alias.is_typed_dict(db) => {
|
||||||
TypedDictType::new(db, ClassType::from(*alias)),
|
Ok(TypedDictType::from(db, ClassType::from(*alias)))
|
||||||
)),
|
}
|
||||||
Type::GenericAlias(alias) => Ok(Type::instance(db, ClassType::from(*alias))),
|
Type::GenericAlias(alias) => Ok(Type::instance(db, ClassType::from(*alias))),
|
||||||
|
|
||||||
Type::SubclassOf(_)
|
Type::SubclassOf(_)
|
||||||
|
|
@ -5644,6 +5652,7 @@ impl<'db> Type<'db> {
|
||||||
return KnownClass::Dict
|
return KnownClass::Dict
|
||||||
.to_specialized_class_type(db, [KnownClass::Str.to_instance(db), Type::object(db)])
|
.to_specialized_class_type(db, [KnownClass::Str.to_instance(db), Type::object(db)])
|
||||||
.map(Type::from)
|
.map(Type::from)
|
||||||
|
// Guard against user-customized typesheds with a broken `dict` class
|
||||||
.unwrap_or_else(Type::unknown);
|
.unwrap_or_else(Type::unknown);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -9000,6 +9009,15 @@ pub struct TypedDictType<'db> {
|
||||||
impl get_size2::GetSize for TypedDictType<'_> {}
|
impl get_size2::GetSize for TypedDictType<'_> {}
|
||||||
|
|
||||||
impl<'db> TypedDictType<'db> {
|
impl<'db> TypedDictType<'db> {
|
||||||
|
pub(crate) fn from(db: &'db dyn Db, defining_class: ClassType<'db>) -> Type<'db> {
|
||||||
|
Type::TypedDict(Self::new(db, defining_class))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn items(self, db: &'db dyn Db) -> FxOrderMap<Name, Field<'db>> {
|
||||||
|
let (class_literal, specialization) = self.defining_class(db).class_literal(db);
|
||||||
|
class_literal.fields(db, specialization, CodeGeneratorKind::TypedDict)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn apply_type_mapping<'a>(
|
pub(crate) fn apply_type_mapping<'a>(
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
use std::hash::BuildHasherDefault;
|
|
||||||
use std::sync::{LazyLock, Mutex};
|
use std::sync::{LazyLock, Mutex};
|
||||||
|
|
||||||
use super::TypeVarVariance;
|
use super::TypeVarVariance;
|
||||||
|
|
@ -9,6 +8,7 @@ use super::{
|
||||||
function::{FunctionDecorators, FunctionType},
|
function::{FunctionDecorators, FunctionType},
|
||||||
infer_expression_type, infer_unpack_types,
|
infer_expression_type, infer_unpack_types,
|
||||||
};
|
};
|
||||||
|
use crate::FxOrderMap;
|
||||||
use crate::module_resolver::KnownModule;
|
use crate::module_resolver::KnownModule;
|
||||||
use crate::semantic_index::definition::{Definition, DefinitionState};
|
use crate::semantic_index::definition::{Definition, DefinitionState};
|
||||||
use crate::semantic_index::scope::NodeWithScopeKind;
|
use crate::semantic_index::scope::NodeWithScopeKind;
|
||||||
|
|
@ -23,9 +23,9 @@ use crate::types::signatures::{CallableSignature, Parameter, Parameters, Signatu
|
||||||
use crate::types::tuple::TupleSpec;
|
use crate::types::tuple::TupleSpec;
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
BareTypeAliasType, Binding, BoundSuperError, BoundSuperType, CallableType, DataclassParams,
|
BareTypeAliasType, Binding, BoundSuperError, BoundSuperType, CallableType, DataclassParams,
|
||||||
DeprecatedInstance, KnownInstanceType, TypeAliasType, TypeMapping, TypeRelation,
|
DeprecatedInstance, KnownInstanceType, StringLiteralType, TypeAliasType, TypeMapping,
|
||||||
TypeTransformer, TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind, declaration_type,
|
TypeRelation, TypeTransformer, TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind,
|
||||||
infer_definition_types, todo_type,
|
declaration_type, infer_definition_types, todo_type,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
Db, FxIndexMap, FxOrderSet, Program,
|
Db, FxIndexMap, FxOrderSet, Program,
|
||||||
|
|
@ -54,9 +54,7 @@ use ruff_db::parsed::{ParsedModuleRef, parsed_module};
|
||||||
use ruff_python_ast::name::Name;
|
use ruff_python_ast::name::Name;
|
||||||
use ruff_python_ast::{self as ast, PythonVersion};
|
use ruff_python_ast::{self as ast, PythonVersion};
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
use rustc_hash::{FxHashSet, FxHasher};
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
type FxOrderMap<K, V> = ordermap::map::OrderMap<K, V, BuildHasherDefault<FxHasher>>;
|
|
||||||
|
|
||||||
fn explicit_bases_cycle_recover<'db>(
|
fn explicit_bases_cycle_recover<'db>(
|
||||||
_db: &'db dyn Db,
|
_db: &'db dyn Db,
|
||||||
|
|
@ -1097,11 +1095,11 @@ impl MethodDecorator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Metadata regarding a dataclass field/attribute.
|
/// Metadata regarding a dataclass field/attribute or a `TypedDict` "item" / key-value pair.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub(crate) struct DataclassField<'db> {
|
pub(crate) struct Field<'db> {
|
||||||
/// The declared type of the field
|
/// The declared type of the field
|
||||||
pub(crate) field_ty: Type<'db>,
|
pub(crate) declared_ty: Type<'db>,
|
||||||
|
|
||||||
/// The type of the default value for this field
|
/// The type of the default value for this field
|
||||||
pub(crate) default_ty: Option<Type<'db>>,
|
pub(crate) default_ty: Option<Type<'db>>,
|
||||||
|
|
@ -1858,8 +1856,8 @@ impl<'db> ClassLiteral<'db> {
|
||||||
let mut kw_only_field_seen = false;
|
let mut kw_only_field_seen = false;
|
||||||
for (
|
for (
|
||||||
field_name,
|
field_name,
|
||||||
DataclassField {
|
Field {
|
||||||
mut field_ty,
|
declared_ty: mut field_ty,
|
||||||
mut default_ty,
|
mut default_ty,
|
||||||
init_only: _,
|
init_only: _,
|
||||||
init,
|
init,
|
||||||
|
|
@ -2038,17 +2036,28 @@ impl<'db> ClassLiteral<'db> {
|
||||||
Some(CallableType::function_like(db, signature))
|
Some(CallableType::function_like(db, signature))
|
||||||
}
|
}
|
||||||
(CodeGeneratorKind::TypedDict, "__getitem__") => {
|
(CodeGeneratorKind::TypedDict, "__getitem__") => {
|
||||||
// TODO: synthesize a set of overloads with precise types
|
let fields = self.fields(db, specialization, field_policy);
|
||||||
let signature = Signature::new(
|
|
||||||
|
// Add (key -> value type) overloads for all TypedDict items ("fields"):
|
||||||
|
let overloads = fields.iter().map(|(name, field)| {
|
||||||
|
let key_type = Type::StringLiteral(StringLiteralType::new(db, name.as_str()));
|
||||||
|
|
||||||
|
Signature::new(
|
||||||
Parameters::new([
|
Parameters::new([
|
||||||
Parameter::positional_only(Some(Name::new_static("self")))
|
Parameter::positional_only(Some(Name::new_static("self")))
|
||||||
.with_annotated_type(instance_ty),
|
.with_annotated_type(instance_ty),
|
||||||
Parameter::positional_only(Some(Name::new_static("key"))),
|
Parameter::positional_only(Some(Name::new_static("key")))
|
||||||
|
.with_annotated_type(key_type),
|
||||||
]),
|
]),
|
||||||
Some(todo_type!("Support for `TypedDict`")),
|
Some(field.declared_ty),
|
||||||
);
|
)
|
||||||
|
});
|
||||||
|
|
||||||
Some(CallableType::function_like(db, signature))
|
Some(Type::Callable(CallableType::new(
|
||||||
|
db,
|
||||||
|
CallableSignature::from_overloads(overloads),
|
||||||
|
true,
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
(CodeGeneratorKind::TypedDict, "get") => {
|
(CodeGeneratorKind::TypedDict, "get") => {
|
||||||
// TODO: synthesize a set of overloads with precise types
|
// TODO: synthesize a set of overloads with precise types
|
||||||
|
|
@ -2143,7 +2152,7 @@ impl<'db> ClassLiteral<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
specialization: Option<Specialization<'db>>,
|
specialization: Option<Specialization<'db>>,
|
||||||
field_policy: CodeGeneratorKind,
|
field_policy: CodeGeneratorKind,
|
||||||
) -> FxOrderMap<Name, DataclassField<'db>> {
|
) -> FxOrderMap<Name, Field<'db>> {
|
||||||
if field_policy == CodeGeneratorKind::NamedTuple {
|
if field_policy == CodeGeneratorKind::NamedTuple {
|
||||||
// NamedTuples do not allow multiple inheritance, so it is sufficient to enumerate the
|
// NamedTuples do not allow multiple inheritance, so it is sufficient to enumerate the
|
||||||
// fields of this class only.
|
// fields of this class only.
|
||||||
|
|
@ -2190,7 +2199,7 @@ impl<'db> ClassLiteral<'db> {
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
specialization: Option<Specialization<'db>>,
|
specialization: Option<Specialization<'db>>,
|
||||||
) -> FxOrderMap<Name, DataclassField<'db>> {
|
) -> FxOrderMap<Name, Field<'db>> {
|
||||||
let mut attributes = FxOrderMap::default();
|
let mut attributes = FxOrderMap::default();
|
||||||
|
|
||||||
let class_body_scope = self.body_scope(db);
|
let class_body_scope = self.body_scope(db);
|
||||||
|
|
@ -2242,8 +2251,8 @@ impl<'db> ClassLiteral<'db> {
|
||||||
|
|
||||||
attributes.insert(
|
attributes.insert(
|
||||||
symbol.name().clone(),
|
symbol.name().clone(),
|
||||||
DataclassField {
|
Field {
|
||||||
field_ty: attr_ty.apply_optional_specialization(db, specialization),
|
declared_ty: attr_ty.apply_optional_specialization(db, specialization),
|
||||||
default_ty,
|
default_ty,
|
||||||
init_only: attr.is_init_var(),
|
init_only: attr.is_init_var(),
|
||||||
init,
|
init,
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ pub(crate) fn register_lints(registry: &mut LintRegistryBuilder) {
|
||||||
registry.register_lint(&INSTANCE_LAYOUT_CONFLICT);
|
registry.register_lint(&INSTANCE_LAYOUT_CONFLICT);
|
||||||
registry.register_lint(&INCONSISTENT_MRO);
|
registry.register_lint(&INCONSISTENT_MRO);
|
||||||
registry.register_lint(&INDEX_OUT_OF_BOUNDS);
|
registry.register_lint(&INDEX_OUT_OF_BOUNDS);
|
||||||
|
registry.register_lint(&INVALID_KEY);
|
||||||
registry.register_lint(&INVALID_ARGUMENT_TYPE);
|
registry.register_lint(&INVALID_ARGUMENT_TYPE);
|
||||||
registry.register_lint(&INVALID_RETURN_TYPE);
|
registry.register_lint(&INVALID_RETURN_TYPE);
|
||||||
registry.register_lint(&INVALID_ASSIGNMENT);
|
registry.register_lint(&INVALID_ASSIGNMENT);
|
||||||
|
|
@ -489,6 +490,31 @@ declare_lint! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare_lint! {
|
||||||
|
/// ## What it does
|
||||||
|
/// Checks for subscript accesses with invalid keys.
|
||||||
|
///
|
||||||
|
/// ## Why is this bad?
|
||||||
|
/// Using an invalid key will raise a `KeyError` at runtime.
|
||||||
|
///
|
||||||
|
/// ## Examples
|
||||||
|
/// ```python
|
||||||
|
/// from typing import TypedDict
|
||||||
|
///
|
||||||
|
/// class Person(TypedDict):
|
||||||
|
/// name: str
|
||||||
|
/// age: int
|
||||||
|
///
|
||||||
|
/// alice = Person(name="Alice", age=30)
|
||||||
|
/// alice["height"] # KeyError: 'height'
|
||||||
|
/// ```
|
||||||
|
pub(crate) static INVALID_KEY = {
|
||||||
|
summary: "detects invalid subscript accesses",
|
||||||
|
status: LintStatus::preview("1.0.0"),
|
||||||
|
default_level: Level::Error,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
declare_lint! {
|
declare_lint! {
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Detects call arguments whose type is not assignable to the corresponding typed parameter.
|
/// Detects call arguments whose type is not assignable to the corresponding typed parameter.
|
||||||
|
|
@ -2591,3 +2617,29 @@ pub(super) fn hint_if_stdlib_submodule_exists_on_other_versions(
|
||||||
|
|
||||||
add_inferred_python_version_hint_to_diagnostic(db, &mut diagnostic, "resolving modules");
|
add_inferred_python_version_hint_to_diagnostic(db, &mut diagnostic, "resolving modules");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Suggest a name from `existing_names` that is similar to `wrong_name`.
|
||||||
|
pub(super) fn did_you_mean<S: AsRef<str>, T: AsRef<str>>(
|
||||||
|
existing_names: impl Iterator<Item = S>,
|
||||||
|
wrong_name: T,
|
||||||
|
) -> Option<String> {
|
||||||
|
if wrong_name.as_ref().len() < 3 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
existing_names
|
||||||
|
.filter(|ref id| id.as_ref().len() >= 2)
|
||||||
|
.map(|ref id| {
|
||||||
|
(
|
||||||
|
id.as_ref().to_string(),
|
||||||
|
strsim::damerau_levenshtein(
|
||||||
|
&id.as_ref().to_lowercase(),
|
||||||
|
&wrong_name.as_ref().to_lowercase(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.min_by_key(|(_, dist)| *dist)
|
||||||
|
// Heuristic to filter out bad matches
|
||||||
|
.filter(|(_, dist)| *dist <= 3)
|
||||||
|
.map(|(id, _)| id)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -90,21 +90,21 @@ use crate::semantic_index::{
|
||||||
ApplicableConstraints, EnclosingSnapshotResult, SemanticIndex, place_table, semantic_index,
|
ApplicableConstraints, EnclosingSnapshotResult, SemanticIndex, place_table, semantic_index,
|
||||||
};
|
};
|
||||||
use crate::types::call::{Binding, Bindings, CallArguments, CallError, CallErrorKind};
|
use crate::types::call::{Binding, Bindings, CallArguments, CallError, CallErrorKind};
|
||||||
use crate::types::class::{CodeGeneratorKind, DataclassField, MetaclassErrorKind, SliceLiteral};
|
use crate::types::class::{CodeGeneratorKind, Field, MetaclassErrorKind, SliceLiteral};
|
||||||
use crate::types::diagnostic::{
|
use crate::types::diagnostic::{
|
||||||
self, CALL_NON_CALLABLE, CONFLICTING_DECLARATIONS, CONFLICTING_METACLASS,
|
self, CALL_NON_CALLABLE, CONFLICTING_DECLARATIONS, CONFLICTING_METACLASS,
|
||||||
CYCLIC_CLASS_DEFINITION, DIVISION_BY_ZERO, DUPLICATE_KW_ONLY, INCONSISTENT_MRO,
|
CYCLIC_CLASS_DEFINITION, DIVISION_BY_ZERO, DUPLICATE_KW_ONLY, INCONSISTENT_MRO,
|
||||||
INVALID_ARGUMENT_TYPE, INVALID_ASSIGNMENT, INVALID_ATTRIBUTE_ACCESS, INVALID_BASE,
|
INVALID_ARGUMENT_TYPE, INVALID_ASSIGNMENT, INVALID_ATTRIBUTE_ACCESS, INVALID_BASE,
|
||||||
INVALID_DECLARATION, INVALID_GENERIC_CLASS, INVALID_PARAMETER_DEFAULT, INVALID_TYPE_FORM,
|
INVALID_DECLARATION, INVALID_GENERIC_CLASS, INVALID_KEY, INVALID_PARAMETER_DEFAULT,
|
||||||
INVALID_TYPE_GUARD_CALL, INVALID_TYPE_VARIABLE_CONSTRAINTS, IncompatibleBases,
|
INVALID_TYPE_FORM, INVALID_TYPE_GUARD_CALL, INVALID_TYPE_VARIABLE_CONSTRAINTS,
|
||||||
POSSIBLY_UNBOUND_IMPLICIT_CALL, POSSIBLY_UNBOUND_IMPORT, TypeCheckDiagnostics,
|
IncompatibleBases, POSSIBLY_UNBOUND_IMPLICIT_CALL, POSSIBLY_UNBOUND_IMPORT,
|
||||||
UNDEFINED_REVEAL, UNRESOLVED_ATTRIBUTE, UNRESOLVED_GLOBAL, UNRESOLVED_IMPORT,
|
TypeCheckDiagnostics, UNDEFINED_REVEAL, UNRESOLVED_ATTRIBUTE, UNRESOLVED_GLOBAL,
|
||||||
UNRESOLVED_REFERENCE, UNSUPPORTED_OPERATOR, report_implicit_return_type,
|
UNRESOLVED_IMPORT, UNRESOLVED_REFERENCE, UNSUPPORTED_OPERATOR, did_you_mean,
|
||||||
report_instance_layout_conflict, report_invalid_argument_number_to_special_form,
|
report_implicit_return_type, report_instance_layout_conflict,
|
||||||
report_invalid_arguments_to_annotated, report_invalid_arguments_to_callable,
|
report_invalid_argument_number_to_special_form, report_invalid_arguments_to_annotated,
|
||||||
report_invalid_assignment, report_invalid_attribute_assignment,
|
report_invalid_arguments_to_callable, report_invalid_assignment,
|
||||||
report_invalid_generator_function_return_type, report_invalid_return_type,
|
report_invalid_attribute_assignment, report_invalid_generator_function_return_type,
|
||||||
report_possibly_unbound_attribute,
|
report_invalid_return_type, report_possibly_unbound_attribute,
|
||||||
};
|
};
|
||||||
use crate::types::enums::is_enum_class;
|
use crate::types::enums::is_enum_class;
|
||||||
use crate::types::function::{
|
use crate::types::function::{
|
||||||
|
|
@ -1352,8 +1352,13 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
let specialization = None;
|
let specialization = None;
|
||||||
let mut kw_only_field_names = vec![];
|
let mut kw_only_field_names = vec![];
|
||||||
|
|
||||||
for (name, DataclassField { field_ty, .. }) in
|
for (
|
||||||
class.fields(self.db(), specialization, field_policy)
|
name,
|
||||||
|
Field {
|
||||||
|
declared_ty: field_ty,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
) in class.fields(self.db(), specialization, field_policy)
|
||||||
{
|
{
|
||||||
let Some(instance) = field_ty.into_nominal_instance() else {
|
let Some(instance) = field_ty.into_nominal_instance() else {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -1909,17 +1914,17 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
// TODO: also consider qualifiers on the attribute
|
// TODO: also consider qualifiers on the attribute
|
||||||
return (ty, is_modifiable);
|
return (ty, is_modifiable);
|
||||||
}
|
}
|
||||||
} else if let AnyNodeRef::ExprSubscript(ast::ExprSubscript {
|
} else if let AnyNodeRef::ExprSubscript(
|
||||||
value,
|
subscript @ ast::ExprSubscript {
|
||||||
slice,
|
value, slice, ctx, ..
|
||||||
ctx,
|
},
|
||||||
..
|
) = node
|
||||||
}) = node
|
|
||||||
{
|
{
|
||||||
let value_ty = self.infer_expression(value);
|
let value_ty = self.infer_expression(value);
|
||||||
let slice_ty = self.infer_expression(slice);
|
let slice_ty = self.infer_expression(slice);
|
||||||
let result_ty = self
|
let result_ty = self.infer_subscript_expression_types(
|
||||||
.infer_subscript_expression_types(value, value_ty, slice_ty, *ctx);
|
subscript, value_ty, slice_ty, *ctx,
|
||||||
|
);
|
||||||
return (result_ty, is_modifiable);
|
return (result_ty, is_modifiable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2030,6 +2035,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
// pyright. TODO: Other standard library classes may also be considered safe. Also,
|
// pyright. TODO: Other standard library classes may also be considered safe. Also,
|
||||||
// subclasses of these safe classes that do not override `__getitem__/__setitem__`
|
// subclasses of these safe classes that do not override `__getitem__/__setitem__`
|
||||||
// may be considered safe.
|
// may be considered safe.
|
||||||
|
let is_safe_mutable_class = || {
|
||||||
let safe_mutable_classes = [
|
let safe_mutable_classes = [
|
||||||
KnownClass::List.to_instance(db),
|
KnownClass::List.to_instance(db),
|
||||||
KnownClass::Dict.to_instance(db),
|
KnownClass::Dict.to_instance(db),
|
||||||
|
|
@ -2041,13 +2047,17 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
SpecialFormType::OrderedDict.instance_fallback(db),
|
SpecialFormType::OrderedDict.instance_fallback(db),
|
||||||
SpecialFormType::TypedDict.instance_fallback(db),
|
SpecialFormType::TypedDict.instance_fallback(db),
|
||||||
];
|
];
|
||||||
if safe_mutable_classes.iter().all(|safe_mutable_class| {
|
|
||||||
!value_ty.is_equivalent_to(db, *safe_mutable_class)
|
safe_mutable_classes.iter().any(|safe_mutable_class| {
|
||||||
&& value_ty
|
value_ty.is_equivalent_to(db, *safe_mutable_class)
|
||||||
|
|| value_ty
|
||||||
.generic_origin(db)
|
.generic_origin(db)
|
||||||
.zip(safe_mutable_class.generic_origin(db))
|
.zip(safe_mutable_class.generic_origin(db))
|
||||||
.is_none_or(|(l, r)| l != r)
|
.is_some_and(|(l, r)| l == r)
|
||||||
}) {
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
if !value_ty.is_typed_dict() && !is_safe_mutable_class() {
|
||||||
bound_ty = declared_ty;
|
bound_ty = declared_ty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8454,7 +8464,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
ExprContext::Store => {
|
ExprContext::Store => {
|
||||||
let value_ty = self.infer_expression(value);
|
let value_ty = self.infer_expression(value);
|
||||||
let slice_ty = self.infer_expression(slice);
|
let slice_ty = self.infer_expression(slice);
|
||||||
self.infer_subscript_expression_types(value, value_ty, slice_ty, *ctx);
|
self.infer_subscript_expression_types(subscript, value_ty, slice_ty, *ctx);
|
||||||
Type::Never
|
Type::Never
|
||||||
}
|
}
|
||||||
ExprContext::Del => {
|
ExprContext::Del => {
|
||||||
|
|
@ -8464,7 +8474,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
ExprContext::Invalid => {
|
ExprContext::Invalid => {
|
||||||
let value_ty = self.infer_expression(value);
|
let value_ty = self.infer_expression(value);
|
||||||
let slice_ty = self.infer_expression(slice);
|
let slice_ty = self.infer_expression(slice);
|
||||||
self.infer_subscript_expression_types(value, value_ty, slice_ty, *ctx);
|
self.infer_subscript_expression_types(subscript, value_ty, slice_ty, *ctx);
|
||||||
Type::unknown()
|
Type::unknown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8493,7 +8503,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
// Even if we can obtain the subscript type based on the assignments, we still perform default type inference
|
// Even if we can obtain the subscript type based on the assignments, we still perform default type inference
|
||||||
// (to store the expression type and to report errors).
|
// (to store the expression type and to report errors).
|
||||||
let slice_ty = self.infer_expression(slice);
|
let slice_ty = self.infer_expression(slice);
|
||||||
self.infer_subscript_expression_types(value, value_ty, slice_ty, *ctx);
|
self.infer_subscript_expression_types(subscript, value_ty, slice_ty, *ctx);
|
||||||
return ty;
|
return ty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8532,7 +8542,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let slice_ty = self.infer_expression(slice);
|
let slice_ty = self.infer_expression(slice);
|
||||||
let result_ty = self.infer_subscript_expression_types(value, value_ty, slice_ty, *ctx);
|
let result_ty = self.infer_subscript_expression_types(subscript, value_ty, slice_ty, *ctx);
|
||||||
self.narrow_expr_with_applicable_constraints(subscript, result_ty, &constraint_keys)
|
self.narrow_expr_with_applicable_constraints(subscript, result_ty, &constraint_keys)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -8583,7 +8593,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
|
|
||||||
fn infer_subscript_expression_types(
|
fn infer_subscript_expression_types(
|
||||||
&self,
|
&self,
|
||||||
value_node: &'ast ast::Expr,
|
subscript: &ast::ExprSubscript,
|
||||||
value_ty: Type<'db>,
|
value_ty: Type<'db>,
|
||||||
slice_ty: Type<'db>,
|
slice_ty: Type<'db>,
|
||||||
expr_context: ExprContext,
|
expr_context: ExprContext,
|
||||||
|
|
@ -8591,12 +8601,14 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
let db = self.db();
|
let db = self.db();
|
||||||
let context = &self.context;
|
let context = &self.context;
|
||||||
|
|
||||||
|
let value_node = subscript.value.as_ref();
|
||||||
|
|
||||||
let inferred = match (value_ty, slice_ty) {
|
let inferred = match (value_ty, slice_ty) {
|
||||||
(Type::NominalInstance(instance), _)
|
(Type::NominalInstance(instance), _)
|
||||||
if instance.class.is_known(db, KnownClass::VersionInfo) =>
|
if instance.class.is_known(db, KnownClass::VersionInfo) =>
|
||||||
{
|
{
|
||||||
Some(self.infer_subscript_expression_types(
|
Some(self.infer_subscript_expression_types(
|
||||||
value_node,
|
subscript,
|
||||||
Type::version_info_tuple(db),
|
Type::version_info_tuple(db),
|
||||||
slice_ty,
|
slice_ty,
|
||||||
expr_context,
|
expr_context,
|
||||||
|
|
@ -8604,7 +8616,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
}
|
}
|
||||||
|
|
||||||
(Type::Union(union), _) => Some(union.map(db, |element| {
|
(Type::Union(union), _) => Some(union.map(db, |element| {
|
||||||
self.infer_subscript_expression_types(value_node, *element, slice_ty, expr_context)
|
self.infer_subscript_expression_types(subscript, *element, slice_ty, expr_context)
|
||||||
})),
|
})),
|
||||||
|
|
||||||
// TODO: we can map over the intersection and fold the results back into an intersection,
|
// TODO: we can map over the intersection and fold the results back into an intersection,
|
||||||
|
|
@ -8735,7 +8747,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
Type::Tuple(_) | Type::StringLiteral(_) | Type::BytesLiteral(_),
|
Type::Tuple(_) | Type::StringLiteral(_) | Type::BytesLiteral(_),
|
||||||
Type::BooleanLiteral(bool),
|
Type::BooleanLiteral(bool),
|
||||||
) => Some(self.infer_subscript_expression_types(
|
) => Some(self.infer_subscript_expression_types(
|
||||||
value_node,
|
subscript,
|
||||||
value_ty,
|
value_ty,
|
||||||
Type::IntLiteral(i64::from(bool)),
|
Type::IntLiteral(i64::from(bool)),
|
||||||
expr_context,
|
expr_context,
|
||||||
|
|
@ -8830,7 +8842,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
//
|
//
|
||||||
// See: https://docs.python.org/3/reference/datamodel.html#class-getitem-versus-getitem
|
// See: https://docs.python.org/3/reference/datamodel.html#class-getitem-versus-getitem
|
||||||
match value_ty.try_call_dunder(db, "__getitem__", CallArguments::positional([slice_ty])) {
|
match value_ty.try_call_dunder(db, "__getitem__", CallArguments::positional([slice_ty])) {
|
||||||
Ok(outcome) => return outcome.return_type(db),
|
Ok(outcome) => {
|
||||||
|
return outcome.return_type(db);
|
||||||
|
}
|
||||||
Err(err @ CallDunderError::PossiblyUnbound { .. }) => {
|
Err(err @ CallDunderError::PossiblyUnbound { .. }) => {
|
||||||
if let Some(builder) =
|
if let Some(builder) =
|
||||||
context.report_lint(&POSSIBLY_UNBOUND_IMPLICIT_CALL, value_node)
|
context.report_lint(&POSSIBLY_UNBOUND_IMPLICIT_CALL, value_node)
|
||||||
|
|
@ -8856,6 +8870,50 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CallErrorKind::BindingError => {
|
CallErrorKind::BindingError => {
|
||||||
|
if let Some(typed_dict) = value_ty.into_typed_dict() {
|
||||||
|
let slice_node = subscript.slice.as_ref();
|
||||||
|
|
||||||
|
if let Some(builder) = context.report_lint(&INVALID_KEY, slice_node) {
|
||||||
|
match slice_ty {
|
||||||
|
Type::StringLiteral(key) => {
|
||||||
|
let key = key.value(db);
|
||||||
|
let typed_dict_name = value_ty.display(db);
|
||||||
|
|
||||||
|
let mut diagnostic = builder.into_diagnostic(format_args!(
|
||||||
|
"Invalid key access on TypedDict `{typed_dict_name}`",
|
||||||
|
));
|
||||||
|
|
||||||
|
diagnostic.annotate(
|
||||||
|
self.context.secondary(value_node).message(
|
||||||
|
format_args!("TypedDict `{typed_dict_name}`"),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
let items = typed_dict.items(db);
|
||||||
|
let existing_keys =
|
||||||
|
items.iter().map(|(name, _)| name.as_str());
|
||||||
|
|
||||||
|
diagnostic.set_primary_message(format!(
|
||||||
|
"Unknown key \"{key}\"{hint}",
|
||||||
|
hint = if let Some(suggestion) =
|
||||||
|
did_you_mean(existing_keys, key)
|
||||||
|
{
|
||||||
|
format!(" - did you mean \"{suggestion}\"?")
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
diagnostic
|
||||||
|
}
|
||||||
|
_ => builder.into_diagnostic(format_args!(
|
||||||
|
"TypedDict `{}` cannot be indexed with a key of type `{}`",
|
||||||
|
value_ty.display(db),
|
||||||
|
slice_ty.display(db),
|
||||||
|
)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if let Some(builder) =
|
if let Some(builder) =
|
||||||
context.report_lint(&INVALID_ARGUMENT_TYPE, value_node)
|
context.report_lint(&INVALID_ARGUMENT_TYPE, value_node)
|
||||||
{
|
{
|
||||||
|
|
@ -8868,6 +8926,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
CallErrorKind::PossiblyNotCallable => {
|
CallErrorKind::PossiblyNotCallable => {
|
||||||
if let Some(builder) = context.report_lint(&CALL_NON_CALLABLE, value_node) {
|
if let Some(builder) = context.report_lint(&CALL_NON_CALLABLE, value_node) {
|
||||||
builder.into_diagnostic(format_args!(
|
builder.into_diagnostic(format_args!(
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,9 @@ use crate::types::cyclic::PairVisitor;
|
||||||
use crate::types::enums::is_single_member_enum;
|
use crate::types::enums::is_single_member_enum;
|
||||||
use crate::types::protocol_class::walk_protocol_interface;
|
use crate::types::protocol_class::walk_protocol_interface;
|
||||||
use crate::types::tuple::TupleType;
|
use crate::types::tuple::TupleType;
|
||||||
use crate::types::{DynamicType, TypeMapping, TypeRelation, TypeTransformer, TypeVarInstance};
|
use crate::types::{
|
||||||
|
DynamicType, TypeMapping, TypeRelation, TypeTransformer, TypeVarInstance, TypedDictType,
|
||||||
|
};
|
||||||
use crate::{Db, FxOrderSet};
|
use crate::{Db, FxOrderSet};
|
||||||
|
|
||||||
pub(super) use synthesized_protocol::SynthesizedProtocolType;
|
pub(super) use synthesized_protocol::SynthesizedProtocolType;
|
||||||
|
|
@ -24,10 +26,16 @@ impl<'db> Type<'db> {
|
||||||
(ClassType::Generic(alias), Some(KnownClass::Tuple)) => {
|
(ClassType::Generic(alias), Some(KnownClass::Tuple)) => {
|
||||||
Self::tuple(TupleType::new(db, alias.specialization(db).tuple(db)))
|
Self::tuple(TupleType::new(db, alias.specialization(db).tuple(db)))
|
||||||
}
|
}
|
||||||
_ if class.class_literal(db).0.is_protocol(db) => {
|
_ => {
|
||||||
|
let class_literal = class.class_literal(db).0;
|
||||||
|
if class_literal.is_protocol(db) {
|
||||||
Self::ProtocolInstance(ProtocolInstanceType::from_class(class))
|
Self::ProtocolInstance(ProtocolInstanceType::from_class(class))
|
||||||
|
} else if class_literal.is_typed_dict(db) {
|
||||||
|
TypedDictType::from(db, class)
|
||||||
|
} else {
|
||||||
|
Self::NominalInstance(NominalInstanceType::from_class(class))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => Self::NominalInstance(NominalInstanceType::from_class(class)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -521,6 +521,16 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"invalid-key": {
|
||||||
|
"title": "detects invalid subscript accesses",
|
||||||
|
"description": "## What it does\nChecks for subscript accesses with invalid keys.\n\n## Why is this bad?\nUsing an invalid key will raise a `KeyError` at runtime.\n\n## Examples\n```python\nfrom typing import TypedDict\n\nclass Person(TypedDict):\n name: str\n age: int\n\nalice = Person(name=\"Alice\", age=30)\nalice[\"height\"] # KeyError: 'height'\n```",
|
||||||
|
"default": "error",
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/Level"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"invalid-legacy-type-variable": {
|
"invalid-legacy-type-variable": {
|
||||||
"title": "detects invalid legacy type variables",
|
"title": "detects invalid legacy type variables",
|
||||||
"description": "## What it does\nChecks for the creation of invalid legacy `TypeVar`s\n\n## Why is this bad?\nThere are several requirements that you must follow when creating a legacy `TypeVar`.\n\n## Examples\n```python\nfrom typing import TypeVar\n\nT = TypeVar(\"T\") # okay\nQ = TypeVar(\"S\") # error: TypeVar name must match the variable it's assigned to\nT = TypeVar(\"T\") # error: TypeVars should not be redefined\n\n# error: TypeVar must be immediately assigned to a variable\ndef f(t: TypeVar(\"U\")): ...\n```\n\n## References\n- [Typing spec: Generics](https://typing.python.org/en/latest/spec/generics.html#introduction)",
|
"description": "## What it does\nChecks for the creation of invalid legacy `TypeVar`s\n\n## Why is this bad?\nThere are several requirements that you must follow when creating a legacy `TypeVar`.\n\n## Examples\n```python\nfrom typing import TypeVar\n\nT = TypeVar(\"T\") # okay\nQ = TypeVar(\"S\") # error: TypeVar name must match the variable it's assigned to\nT = TypeVar(\"T\") # error: TypeVars should not be redefined\n\n# error: TypeVar must be immediately assigned to a variable\ndef f(t: TypeVar(\"U\")): ...\n```\n\n## References\n- [Typing spec: Generics](https://typing.python.org/en/latest/spec/generics.html#introduction)",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue