mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 13:51:16 +00:00
[ty] Strict validation of protocol members (#17750)
This commit is contained in:
parent
e0f4cec7a1
commit
656fc335f2
9 changed files with 619 additions and 78 deletions
165
crates/ty/docs/rules.md
generated
165
crates/ty/docs/rules.md
generated
|
@ -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#L104)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L109)
|
||||||
</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#L148)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L153)
|
||||||
</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#L174)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L179)
|
||||||
</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#L199)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L204)
|
||||||
</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#L225)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L230)
|
||||||
</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#L290)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L295)
|
||||||
</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#L311)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L316)
|
||||||
</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#L479)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L519)
|
||||||
</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#L503)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L543)
|
||||||
</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#L343)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L348)
|
||||||
</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#L548)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L588)
|
||||||
</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#L588)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L628)
|
||||||
</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#L1622)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1662)
|
||||||
</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-await) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-await) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L610)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L650)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -562,7 +562,7 @@ asyncio.run(main())
|
||||||
<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#L640)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L680)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -584,7 +584,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#L691)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L731)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -609,7 +609,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#L712)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L752)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -636,7 +636,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#L735)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L775)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -678,7 +678,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#L771)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L811)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -709,7 +709,7 @@ class C[U](Generic[T]): ...
|
||||||
<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-key) ·
|
[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#L523)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L563)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -738,7 +738,7 @@ alice["height"] # KeyError: 'height'
|
||||||
<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#L797)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L837)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -771,7 +771,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#L846)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L886)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -803,7 +803,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-named-tuple) ·
|
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-named-tuple) ·
|
||||||
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L453)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L493)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -833,7 +833,7 @@ TypeError: can only inherit from a NamedTuple type and Generic
|
||||||
<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#L873)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L913)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -881,7 +881,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#L916)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L956)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -905,12 +905,12 @@ 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#L425)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L430)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
||||||
Checks for invalidly defined protocol classes.
|
Checks for protocol classes that will raise `TypeError` at runtime.
|
||||||
|
|
||||||
**Why is this bad?**
|
**Why is this bad?**
|
||||||
|
|
||||||
|
@ -937,7 +937,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#L936)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L976)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
Checks for `raise` statements that raise non-exceptions or use invalid
|
Checks for `raise` statements that raise non-exceptions or use invalid
|
||||||
|
@ -984,7 +984,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#L569)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L609)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1007,7 +1007,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#L979)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1019)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1061,7 +1061,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#L825)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L865)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1086,7 +1086,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#L1018)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1058)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1114,7 +1114,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#L1042)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1082)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1142,7 +1142,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#L1094)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1134)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1174,7 +1174,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#L1066)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1106)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1206,7 +1206,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#L1122)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1162)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1239,7 +1239,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#L1151)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1191)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1262,7 +1262,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#L1170)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1210)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1289,7 +1289,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#L1193)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1233)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1311,7 +1311,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#L1211)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1251)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1335,7 +1335,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#L1262)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1302)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1389,7 +1389,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#L1598)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1638)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1417,7 +1417,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#L1353)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1393)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1444,7 +1444,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#L1398)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1438)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1469,7 +1469,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#L1376)
|
[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**
|
||||||
|
@ -1495,7 +1495,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#L1419)
|
[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**
|
||||||
|
@ -1539,7 +1539,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#L1476)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1516)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1564,7 +1564,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#L1497)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1537)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1590,7 +1590,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#L1519)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1559)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1613,7 +1613,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#L1538)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1578)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1636,7 +1636,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#L1231)
|
[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**
|
||||||
|
@ -1671,7 +1671,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#L1557)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1597)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1697,7 +1697,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#L1579)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1619)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1715,12 +1715,51 @@ l = list(range(10))
|
||||||
l[1:10:0] # ValueError: slice step cannot be zero
|
l[1:10:0] # ValueError: slice step cannot be zero
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## `ambiguous-protocol-member`
|
||||||
|
|
||||||
|
<small>
|
||||||
|
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%20ambiguous-protocol-member) ·
|
||||||
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L458)
|
||||||
|
</small>
|
||||||
|
|
||||||
|
**What it does**
|
||||||
|
|
||||||
|
Checks for protocol classes with members that will lead to ambiguous interfaces.
|
||||||
|
|
||||||
|
**Why is this bad?**
|
||||||
|
|
||||||
|
Assigning to an undeclared variable in a protocol class leads to an ambiguous
|
||||||
|
interface which may lead to the type checker inferring unexpected things. It's
|
||||||
|
recommended to ensure that all members of a protocol class are explicitly declared.
|
||||||
|
|
||||||
|
**Examples**
|
||||||
|
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import Protocol
|
||||||
|
|
||||||
|
class BaseProto(Protocol):
|
||||||
|
a: int # fine (explicitly declared as `int`)
|
||||||
|
def method_member(self) -> int: ... # fine: a method definition using `def` is considered a declaration
|
||||||
|
c = "some variable" # error: no explicit declaration, leading to ambiguity
|
||||||
|
b = method_member # error: no explicit declaration, leading to ambiguity
|
||||||
|
|
||||||
|
# error: this creates implicit assignments of `d` and `e` in the protocol class body.
|
||||||
|
# Were they really meant to be considered protocol members?
|
||||||
|
for d, e in enumerate(range(42)):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class SubProto(BaseProto, Protocol):
|
||||||
|
a = 42 # fine (declared in superclass)
|
||||||
|
```
|
||||||
|
|
||||||
## `deprecated`
|
## `deprecated`
|
||||||
|
|
||||||
<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#L269)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L274)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1773,7 +1812,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#L1283)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1323)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1799,7 +1838,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#L122)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L127)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1829,7 +1868,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#L1305)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1345)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1859,7 +1898,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#L1650)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1690)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1884,7 +1923,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#L1458)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1498)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1935,7 +1974,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#L1671)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1711)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -1989,7 +2028,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#L658)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L698)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -2026,7 +2065,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#L251)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L256)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
@ -2048,7 +2087,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#L1331)
|
[View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1371)
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
**What it does**
|
**What it does**
|
||||||
|
|
|
@ -482,6 +482,8 @@ reveal_type(get_protocol_members(Baz2))
|
||||||
|
|
||||||
## Protocol members in statically known branches
|
## Protocol members in statically known branches
|
||||||
|
|
||||||
|
<!-- snapshot-diagnostics -->
|
||||||
|
|
||||||
The list of protocol members does not include any members declared in branches that are statically
|
The list of protocol members does not include any members declared in branches that are statically
|
||||||
known to be unreachable:
|
known to be unreachable:
|
||||||
|
|
||||||
|
@ -492,7 +494,7 @@ python-version = "3.9"
|
||||||
|
|
||||||
```py
|
```py
|
||||||
import sys
|
import sys
|
||||||
from typing_extensions import Protocol, get_protocol_members
|
from typing_extensions import Protocol, get_protocol_members, reveal_type
|
||||||
|
|
||||||
class Foo(Protocol):
|
class Foo(Protocol):
|
||||||
if sys.version_info >= (3, 10):
|
if sys.version_info >= (3, 10):
|
||||||
|
@ -501,7 +503,7 @@ class Foo(Protocol):
|
||||||
def c(self) -> None: ...
|
def c(self) -> None: ...
|
||||||
else:
|
else:
|
||||||
d: int
|
d: int
|
||||||
e = 56
|
e = 56 # error: [ambiguous-protocol-member]
|
||||||
def f(self) -> None: ...
|
def f(self) -> None: ...
|
||||||
|
|
||||||
reveal_type(get_protocol_members(Foo)) # revealed: frozenset[Literal["d", "e", "f"]]
|
reveal_type(get_protocol_members(Foo)) # revealed: frozenset[Literal["d", "e", "f"]]
|
||||||
|
@ -797,9 +799,9 @@ def f(arg: HasXWithDefault):
|
||||||
```
|
```
|
||||||
|
|
||||||
Assignments in a class body of a protocol -- of any kind -- are not permitted by ty unless the
|
Assignments in a class body of a protocol -- of any kind -- are not permitted by ty unless the
|
||||||
symbol being assigned to is also explicitly declared in the protocol's class body. Note that this is
|
symbol being assigned to is also explicitly declared in the body of the protocol class or one of its
|
||||||
stricter validation of protocol members than many other type checkers currently apply (as of
|
superclasses. Note that this is stricter validation of protocol members than many other type
|
||||||
2025/04/21).
|
checkers currently apply (as of 2025/04/21).
|
||||||
|
|
||||||
The reason for this strict validation is that undeclared variables in the class body would lead to
|
The reason for this strict validation is that undeclared variables in the class body would lead to
|
||||||
an ambiguous interface being declared by the protocol.
|
an ambiguous interface being declared by the protocol.
|
||||||
|
@ -823,24 +825,75 @@ class LotsOfBindings(Protocol):
|
||||||
|
|
||||||
class Nested: ... # also weird, but we should also probably allow it
|
class Nested: ... # also weird, but we should also probably allow it
|
||||||
class NestedProtocol(Protocol): ... # same here...
|
class NestedProtocol(Protocol): ... # same here...
|
||||||
e = 72 # TODO: this should error with `[invalid-protocol]` (`e` is not declared)
|
e = 72 # error: [ambiguous-protocol-member]
|
||||||
|
|
||||||
f, g = (1, 2) # TODO: this should error with `[invalid-protocol]` (`f` and `g` are not declared)
|
# error: [ambiguous-protocol-member] "Consider adding an annotation, e.g. `f: int = ...`"
|
||||||
|
# error: [ambiguous-protocol-member] "Consider adding an annotation, e.g. `g: int = ...`"
|
||||||
|
f, g = (1, 2)
|
||||||
|
|
||||||
h: int = (i := 3) # TODO: this should error with `[invalid-protocol]` (`i` is not declared)
|
h: int = (i := 3) # error: [ambiguous-protocol-member]
|
||||||
|
|
||||||
for j in range(42): # TODO: this should error with `[invalid-protocol]` (`j` is not declared)
|
for j in range(42): # error: [ambiguous-protocol-member]
|
||||||
pass
|
pass
|
||||||
|
|
||||||
with MyContext() as k: # TODO: this should error with `[invalid-protocol]` (`k` is not declared)
|
with MyContext() as k: # error: [ambiguous-protocol-member]
|
||||||
pass
|
pass
|
||||||
|
|
||||||
match object():
|
match object():
|
||||||
case l: # TODO: this should error with `[invalid-protocol]` (`l` is not declared)
|
case l: # error: [ambiguous-protocol-member]
|
||||||
...
|
...
|
||||||
|
|
||||||
# revealed: frozenset[Literal["Nested", "NestedProtocol", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"]]
|
# revealed: frozenset[Literal["Nested", "NestedProtocol", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"]]
|
||||||
reveal_type(get_protocol_members(LotsOfBindings))
|
reveal_type(get_protocol_members(LotsOfBindings))
|
||||||
|
|
||||||
|
class Foo(Protocol):
|
||||||
|
a: int
|
||||||
|
|
||||||
|
class Bar(Foo, Protocol):
|
||||||
|
a = 42 # fine, because it's declared in the superclass
|
||||||
|
|
||||||
|
reveal_type(get_protocol_members(Bar)) # revealed: frozenset[Literal["a"]]
|
||||||
|
```
|
||||||
|
|
||||||
|
A binding-without-declaration will not be reported if it occurs in a branch that we can statically
|
||||||
|
determine to be unreachable. The reason is that we don't consider it to be a protocol member at all
|
||||||
|
if all definitions for the variable are in unreachable blocks:
|
||||||
|
|
||||||
|
```py
|
||||||
|
import sys
|
||||||
|
|
||||||
|
class Protocol694(Protocol):
|
||||||
|
if sys.version_info > (3, 694):
|
||||||
|
x = 42 # no error!
|
||||||
|
```
|
||||||
|
|
||||||
|
If there are multiple bindings of the variable in the class body, however, and at least one of the
|
||||||
|
bindings occurs in a block of code that is understood to be (possibly) reachable, a diagnostic will
|
||||||
|
be reported. The diagnostic will be attached to the first binding that occurs in the class body,
|
||||||
|
even if that first definition occurs in an unreachable block:
|
||||||
|
|
||||||
|
```py
|
||||||
|
class Protocol695(Protocol):
|
||||||
|
if sys.version_info > (3, 695):
|
||||||
|
x = 42
|
||||||
|
else:
|
||||||
|
x = 42
|
||||||
|
|
||||||
|
x = 56 # error: [ambiguous-protocol-member]
|
||||||
|
```
|
||||||
|
|
||||||
|
In order for the variable to be considered declared, the declaration of the variable must also take
|
||||||
|
place in a block of code that is understood to be (possibly) reachable:
|
||||||
|
|
||||||
|
```py
|
||||||
|
class Protocol696(Protocol):
|
||||||
|
if sys.version_info > (3, 696):
|
||||||
|
x: int
|
||||||
|
else:
|
||||||
|
x = 42 # error: [ambiguous-protocol-member]
|
||||||
|
y: int
|
||||||
|
|
||||||
|
y = 56 # no error
|
||||||
```
|
```
|
||||||
|
|
||||||
Attribute members are allowed to have assignments in methods on the protocol class, just like
|
Attribute members are allowed to have assignments in methods on the protocol class, just like
|
||||||
|
@ -943,6 +996,40 @@ static_assert(not is_assignable_to(HasX, Foo))
|
||||||
static_assert(not is_subtype_of(HasX, Foo))
|
static_assert(not is_subtype_of(HasX, Foo))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Diagnostics for protocols with invalid attribute members
|
||||||
|
|
||||||
|
This is a short appendix to the previous section with the `snapshot-diagnostics` directive enabled
|
||||||
|
(enabling snapshots for the previous section in its entirety would lead to a huge snapshot, since
|
||||||
|
it's a large section).
|
||||||
|
|
||||||
|
<!-- snapshot-diagnostics -->
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import Protocol
|
||||||
|
|
||||||
|
def coinflip() -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
|
class A(Protocol):
|
||||||
|
# The `x` and `y` members attempt to use Python-2-style type comments
|
||||||
|
# to indicate that the type should be `int | None` and `str` respectively,
|
||||||
|
# but we don't support those
|
||||||
|
|
||||||
|
# error: [ambiguous-protocol-member]
|
||||||
|
a = None # type: int
|
||||||
|
# error: [ambiguous-protocol-member]
|
||||||
|
b = ... # type: str
|
||||||
|
|
||||||
|
if coinflip():
|
||||||
|
c = 1 # error: [ambiguous-protocol-member]
|
||||||
|
else:
|
||||||
|
c = 2
|
||||||
|
|
||||||
|
# error: [ambiguous-protocol-member]
|
||||||
|
for d in range(42):
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
## Equivalence of protocols
|
## Equivalence of protocols
|
||||||
|
|
||||||
Two protocols are considered equivalent types if they specify the same interface, even if they have
|
Two protocols are considered equivalent types if they specify the same interface, even if they have
|
||||||
|
|
|
@ -0,0 +1,140 @@
|
||||||
|
---
|
||||||
|
source: crates/ty_test/src/lib.rs
|
||||||
|
expression: snapshot
|
||||||
|
---
|
||||||
|
---
|
||||||
|
mdtest name: protocols.md - Protocols - Diagnostics for protocols with invalid attribute members
|
||||||
|
mdtest path: crates/ty_python_semantic/resources/mdtest/protocols.md
|
||||||
|
---
|
||||||
|
|
||||||
|
# Python source files
|
||||||
|
|
||||||
|
## mdtest_snippet.py
|
||||||
|
|
||||||
|
```
|
||||||
|
1 | from typing import Protocol
|
||||||
|
2 |
|
||||||
|
3 | def coinflip() -> bool:
|
||||||
|
4 | return True
|
||||||
|
5 |
|
||||||
|
6 | class A(Protocol):
|
||||||
|
7 | # The `x` and `y` members attempt to use Python-2-style type comments
|
||||||
|
8 | # to indicate that the type should be `int | None` and `str` respectively,
|
||||||
|
9 | # but we don't support those
|
||||||
|
10 |
|
||||||
|
11 | # error: [ambiguous-protocol-member]
|
||||||
|
12 | a = None # type: int
|
||||||
|
13 | # error: [ambiguous-protocol-member]
|
||||||
|
14 | b = ... # type: str
|
||||||
|
15 |
|
||||||
|
16 | if coinflip():
|
||||||
|
17 | c = 1 # error: [ambiguous-protocol-member]
|
||||||
|
18 | else:
|
||||||
|
19 | c = 2
|
||||||
|
20 |
|
||||||
|
21 | # error: [ambiguous-protocol-member]
|
||||||
|
22 | for d in range(42):
|
||||||
|
23 | pass
|
||||||
|
```
|
||||||
|
|
||||||
|
# Diagnostics
|
||||||
|
|
||||||
|
```
|
||||||
|
warning[ambiguous-protocol-member]: Cannot assign to undeclared variable in the body of a protocol class
|
||||||
|
--> src/mdtest_snippet.py:12:5
|
||||||
|
|
|
||||||
|
11 | # error: [ambiguous-protocol-member]
|
||||||
|
12 | a = None # type: int
|
||||||
|
| ^^^^^^^^ Consider adding an annotation for `a`
|
||||||
|
13 | # error: [ambiguous-protocol-member]
|
||||||
|
14 | b = ... # type: str
|
||||||
|
|
|
||||||
|
info: Assigning to an undeclared variable in a protocol class leads to an ambiguous interface
|
||||||
|
--> src/mdtest_snippet.py:6:7
|
||||||
|
|
|
||||||
|
4 | return True
|
||||||
|
5 |
|
||||||
|
6 | class A(Protocol):
|
||||||
|
| ^^^^^^^^^^^ `A` declared as a protocol here
|
||||||
|
7 | # The `x` and `y` members attempt to use Python-2-style type comments
|
||||||
|
8 | # to indicate that the type should be `int | None` and `str` respectively,
|
||||||
|
|
|
||||||
|
info: No declarations found for `a` in the body of `A` or any of its superclasses
|
||||||
|
info: rule `ambiguous-protocol-member` is enabled by default
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
warning[ambiguous-protocol-member]: Cannot assign to undeclared variable in the body of a protocol class
|
||||||
|
--> src/mdtest_snippet.py:14:5
|
||||||
|
|
|
||||||
|
12 | a = None # type: int
|
||||||
|
13 | # error: [ambiguous-protocol-member]
|
||||||
|
14 | b = ... # type: str
|
||||||
|
| ^^^^^^^ Consider adding an annotation for `b`
|
||||||
|
15 |
|
||||||
|
16 | if coinflip():
|
||||||
|
|
|
||||||
|
info: Assigning to an undeclared variable in a protocol class leads to an ambiguous interface
|
||||||
|
--> src/mdtest_snippet.py:6:7
|
||||||
|
|
|
||||||
|
4 | return True
|
||||||
|
5 |
|
||||||
|
6 | class A(Protocol):
|
||||||
|
| ^^^^^^^^^^^ `A` declared as a protocol here
|
||||||
|
7 | # The `x` and `y` members attempt to use Python-2-style type comments
|
||||||
|
8 | # to indicate that the type should be `int | None` and `str` respectively,
|
||||||
|
|
|
||||||
|
info: No declarations found for `b` in the body of `A` or any of its superclasses
|
||||||
|
info: rule `ambiguous-protocol-member` is enabled by default
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
warning[ambiguous-protocol-member]: Cannot assign to undeclared variable in the body of a protocol class
|
||||||
|
--> src/mdtest_snippet.py:17:9
|
||||||
|
|
|
||||||
|
16 | if coinflip():
|
||||||
|
17 | c = 1 # error: [ambiguous-protocol-member]
|
||||||
|
| ^^^^^ Consider adding an annotation, e.g. `c: int = ...`
|
||||||
|
18 | else:
|
||||||
|
19 | c = 2
|
||||||
|
|
|
||||||
|
info: Assigning to an undeclared variable in a protocol class leads to an ambiguous interface
|
||||||
|
--> src/mdtest_snippet.py:6:7
|
||||||
|
|
|
||||||
|
4 | return True
|
||||||
|
5 |
|
||||||
|
6 | class A(Protocol):
|
||||||
|
| ^^^^^^^^^^^ `A` declared as a protocol here
|
||||||
|
7 | # The `x` and `y` members attempt to use Python-2-style type comments
|
||||||
|
8 | # to indicate that the type should be `int | None` and `str` respectively,
|
||||||
|
|
|
||||||
|
info: No declarations found for `c` in the body of `A` or any of its superclasses
|
||||||
|
info: rule `ambiguous-protocol-member` is enabled by default
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
warning[ambiguous-protocol-member]: Cannot assign to undeclared variable in the body of a protocol class
|
||||||
|
--> src/mdtest_snippet.py:22:9
|
||||||
|
|
|
||||||
|
21 | # error: [ambiguous-protocol-member]
|
||||||
|
22 | for d in range(42):
|
||||||
|
| ^ `d` is not declared as a protocol member
|
||||||
|
23 | pass
|
||||||
|
|
|
||||||
|
info: Assigning to an undeclared variable in a protocol class leads to an ambiguous interface
|
||||||
|
--> src/mdtest_snippet.py:6:7
|
||||||
|
|
|
||||||
|
4 | return True
|
||||||
|
5 |
|
||||||
|
6 | class A(Protocol):
|
||||||
|
| ^^^^^^^^^^^ `A` declared as a protocol here
|
||||||
|
7 | # The `x` and `y` members attempt to use Python-2-style type comments
|
||||||
|
8 | # to indicate that the type should be `int | None` and `str` respectively,
|
||||||
|
|
|
||||||
|
info: No declarations found for `d` in the body of `A` or any of its superclasses
|
||||||
|
info: rule `ambiguous-protocol-member` is enabled by default
|
||||||
|
|
||||||
|
```
|
|
@ -0,0 +1,68 @@
|
||||||
|
---
|
||||||
|
source: crates/ty_test/src/lib.rs
|
||||||
|
expression: snapshot
|
||||||
|
---
|
||||||
|
---
|
||||||
|
mdtest name: protocols.md - Protocols - Protocol members in statically known branches
|
||||||
|
mdtest path: crates/ty_python_semantic/resources/mdtest/protocols.md
|
||||||
|
---
|
||||||
|
|
||||||
|
# Python source files
|
||||||
|
|
||||||
|
## mdtest_snippet.py
|
||||||
|
|
||||||
|
```
|
||||||
|
1 | import sys
|
||||||
|
2 | from typing_extensions import Protocol, get_protocol_members, reveal_type
|
||||||
|
3 |
|
||||||
|
4 | class Foo(Protocol):
|
||||||
|
5 | if sys.version_info >= (3, 10):
|
||||||
|
6 | a: int
|
||||||
|
7 | b = 42
|
||||||
|
8 | def c(self) -> None: ...
|
||||||
|
9 | else:
|
||||||
|
10 | d: int
|
||||||
|
11 | e = 56 # error: [ambiguous-protocol-member]
|
||||||
|
12 | def f(self) -> None: ...
|
||||||
|
13 |
|
||||||
|
14 | reveal_type(get_protocol_members(Foo)) # revealed: frozenset[Literal["d", "e", "f"]]
|
||||||
|
```
|
||||||
|
|
||||||
|
# Diagnostics
|
||||||
|
|
||||||
|
```
|
||||||
|
warning[ambiguous-protocol-member]: Cannot assign to undeclared variable in the body of a protocol class
|
||||||
|
--> src/mdtest_snippet.py:11:9
|
||||||
|
|
|
||||||
|
9 | else:
|
||||||
|
10 | d: int
|
||||||
|
11 | e = 56 # error: [ambiguous-protocol-member]
|
||||||
|
| ^^^^^^ Consider adding an annotation, e.g. `e: int = ...`
|
||||||
|
12 | def f(self) -> None: ...
|
||||||
|
|
|
||||||
|
info: Assigning to an undeclared variable in a protocol class leads to an ambiguous interface
|
||||||
|
--> src/mdtest_snippet.py:4:7
|
||||||
|
|
|
||||||
|
2 | from typing_extensions import Protocol, get_protocol_members, reveal_type
|
||||||
|
3 |
|
||||||
|
4 | class Foo(Protocol):
|
||||||
|
| ^^^^^^^^^^^^^ `Foo` declared as a protocol here
|
||||||
|
5 | if sys.version_info >= (3, 10):
|
||||||
|
6 | a: int
|
||||||
|
|
|
||||||
|
info: No declarations found for `e` in the body of `Foo` or any of its superclasses
|
||||||
|
info: rule `ambiguous-protocol-member` is enabled by default
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
info[revealed-type]: Revealed type
|
||||||
|
--> src/mdtest_snippet.py:14:13
|
||||||
|
|
|
||||||
|
12 | def f(self) -> None: ...
|
||||||
|
13 |
|
||||||
|
14 | reveal_type(get_protocol_members(Foo)) # revealed: frozenset[Literal["d", "e", "f"]]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^ `frozenset[Literal["d", "e", "f"]]`
|
||||||
|
|
|
||||||
|
|
||||||
|
```
|
|
@ -702,6 +702,10 @@ impl DefinitionKind<'_> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn is_unannotated_assignment(&self) -> bool {
|
||||||
|
matches!(self, DefinitionKind::Assignment(_))
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn as_typevar(&self) -> Option<&AstNodeRef<ast::TypeParamTypeVar>> {
|
pub(crate) fn as_typevar(&self) -> Option<&AstNodeRef<ast::TypeParamTypeVar>> {
|
||||||
match self {
|
match self {
|
||||||
DefinitionKind::TypeVar(type_var) => Some(type_var),
|
DefinitionKind::TypeVar(type_var) => Some(type_var),
|
||||||
|
|
|
@ -7,8 +7,9 @@ use super::{
|
||||||
};
|
};
|
||||||
use crate::lint::{Level, LintRegistryBuilder, LintStatus};
|
use crate::lint::{Level, LintRegistryBuilder, LintStatus};
|
||||||
use crate::semantic_index::SemanticIndex;
|
use crate::semantic_index::SemanticIndex;
|
||||||
|
use crate::semantic_index::definition::Definition;
|
||||||
|
use crate::semantic_index::place::{PlaceTable, ScopedPlaceId};
|
||||||
use crate::suppression::FileSuppressionId;
|
use crate::suppression::FileSuppressionId;
|
||||||
use crate::types::LintDiagnosticGuard;
|
|
||||||
use crate::types::class::{Field, SolidBase, SolidBaseKind};
|
use crate::types::class::{Field, SolidBase, SolidBaseKind};
|
||||||
use crate::types::function::KnownFunction;
|
use crate::types::function::KnownFunction;
|
||||||
use crate::types::string_annotation::{
|
use crate::types::string_annotation::{
|
||||||
|
@ -16,6 +17,9 @@ use crate::types::string_annotation::{
|
||||||
IMPLICIT_CONCATENATED_STRING_TYPE_ANNOTATION, INVALID_SYNTAX_IN_FORWARD_ANNOTATION,
|
IMPLICIT_CONCATENATED_STRING_TYPE_ANNOTATION, INVALID_SYNTAX_IN_FORWARD_ANNOTATION,
|
||||||
RAW_STRING_TYPE_ANNOTATION,
|
RAW_STRING_TYPE_ANNOTATION,
|
||||||
};
|
};
|
||||||
|
use crate::types::{
|
||||||
|
DynamicType, LintDiagnosticGuard, Protocol, ProtocolInstanceType, SubclassOfInner, binding_type,
|
||||||
|
};
|
||||||
use crate::types::{SpecialFormType, Type, protocol_class::ProtocolClassLiteral};
|
use crate::types::{SpecialFormType, Type, protocol_class::ProtocolClassLiteral};
|
||||||
use crate::util::diagnostics::format_enumeration;
|
use crate::util::diagnostics::format_enumeration;
|
||||||
use crate::{Db, FxIndexMap, FxOrderMap, Module, ModuleName, Program, declare_lint};
|
use crate::{Db, FxIndexMap, FxOrderMap, Module, ModuleName, Program, declare_lint};
|
||||||
|
@ -29,6 +33,7 @@ use std::fmt::Formatter;
|
||||||
|
|
||||||
/// Registers all known type check lints.
|
/// Registers all known type check lints.
|
||||||
pub(crate) fn register_lints(registry: &mut LintRegistryBuilder) {
|
pub(crate) fn register_lints(registry: &mut LintRegistryBuilder) {
|
||||||
|
registry.register_lint(&AMBIGUOUS_PROTOCOL_MEMBER);
|
||||||
registry.register_lint(&CALL_NON_CALLABLE);
|
registry.register_lint(&CALL_NON_CALLABLE);
|
||||||
registry.register_lint(&POSSIBLY_UNBOUND_IMPLICIT_CALL);
|
registry.register_lint(&POSSIBLY_UNBOUND_IMPLICIT_CALL);
|
||||||
registry.register_lint(&CONFLICTING_ARGUMENT_FORMS);
|
registry.register_lint(&CONFLICTING_ARGUMENT_FORMS);
|
||||||
|
@ -424,7 +429,7 @@ declare_lint! {
|
||||||
|
|
||||||
declare_lint! {
|
declare_lint! {
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks for invalidly defined protocol classes.
|
/// Checks for protocol classes that will raise `TypeError` at runtime.
|
||||||
///
|
///
|
||||||
/// ## Why is this bad?
|
/// ## Why is this bad?
|
||||||
/// An invalidly defined protocol class may lead to the type checker inferring
|
/// An invalidly defined protocol class may lead to the type checker inferring
|
||||||
|
@ -450,6 +455,41 @@ declare_lint! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare_lint! {
|
||||||
|
/// ## What it does
|
||||||
|
/// Checks for protocol classes with members that will lead to ambiguous interfaces.
|
||||||
|
///
|
||||||
|
/// ## Why is this bad?
|
||||||
|
/// Assigning to an undeclared variable in a protocol class leads to an ambiguous
|
||||||
|
/// interface which may lead to the type checker inferring unexpected things. It's
|
||||||
|
/// recommended to ensure that all members of a protocol class are explicitly declared.
|
||||||
|
///
|
||||||
|
/// ## Examples
|
||||||
|
///
|
||||||
|
/// ```py
|
||||||
|
/// from typing import Protocol
|
||||||
|
///
|
||||||
|
/// class BaseProto(Protocol):
|
||||||
|
/// a: int # fine (explicitly declared as `int`)
|
||||||
|
/// def method_member(self) -> int: ... # fine: a method definition using `def` is considered a declaration
|
||||||
|
/// c = "some variable" # error: no explicit declaration, leading to ambiguity
|
||||||
|
/// b = method_member # error: no explicit declaration, leading to ambiguity
|
||||||
|
///
|
||||||
|
/// # error: this creates implicit assignments of `d` and `e` in the protocol class body.
|
||||||
|
/// # Were they really meant to be considered protocol members?
|
||||||
|
/// for d, e in enumerate(range(42)):
|
||||||
|
/// pass
|
||||||
|
///
|
||||||
|
/// class SubProto(BaseProto, Protocol):
|
||||||
|
/// a = 42 # fine (declared in superclass)
|
||||||
|
/// ```
|
||||||
|
pub(crate) static AMBIGUOUS_PROTOCOL_MEMBER = {
|
||||||
|
summary: "detects protocol classes with ambiguous interfaces",
|
||||||
|
status: LintStatus::preview("1.0.0"),
|
||||||
|
default_level: Level::Warn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
declare_lint! {
|
declare_lint! {
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks for invalidly defined `NamedTuple` classes.
|
/// Checks for invalidly defined `NamedTuple` classes.
|
||||||
|
@ -2456,6 +2496,95 @@ pub(crate) fn report_attempted_protocol_instantiation(
|
||||||
diagnostic.sub(class_def_diagnostic);
|
diagnostic.sub(class_def_diagnostic);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn report_undeclared_protocol_member(
|
||||||
|
context: &InferContext,
|
||||||
|
definition: Definition,
|
||||||
|
protocol_class: ProtocolClassLiteral,
|
||||||
|
class_symbol_table: &PlaceTable,
|
||||||
|
) {
|
||||||
|
/// We want to avoid suggesting an annotation for e.g. `x = None`,
|
||||||
|
/// because the user almost certainly doesn't want to write `x: None = None`.
|
||||||
|
/// We also want to avoid suggesting invalid syntax such as `x: <class 'int'> = int`.
|
||||||
|
fn should_give_hint<'db>(db: &'db dyn Db, ty: Type<'db>) -> bool {
|
||||||
|
let class = match ty {
|
||||||
|
Type::ProtocolInstance(ProtocolInstanceType {
|
||||||
|
inner: Protocol::FromClass(_),
|
||||||
|
..
|
||||||
|
}) => return true,
|
||||||
|
Type::SubclassOf(subclass_of) => match subclass_of.subclass_of() {
|
||||||
|
SubclassOfInner::Class(class) => class,
|
||||||
|
SubclassOfInner::Dynamic(DynamicType::Any) => return true,
|
||||||
|
SubclassOfInner::Dynamic(_) => return false,
|
||||||
|
},
|
||||||
|
Type::NominalInstance(instance) => instance.class(db),
|
||||||
|
_ => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
!matches!(
|
||||||
|
class.known(db),
|
||||||
|
Some(KnownClass::NoneType | KnownClass::EllipsisType)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let db = context.db();
|
||||||
|
|
||||||
|
let Some(builder) = context.report_lint(
|
||||||
|
&AMBIGUOUS_PROTOCOL_MEMBER,
|
||||||
|
definition.full_range(db, context.module()),
|
||||||
|
) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let ScopedPlaceId::Symbol(symbol_id) = definition.place(db) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let symbol_name = class_symbol_table.symbol(symbol_id).name();
|
||||||
|
let class_name = protocol_class.name(db);
|
||||||
|
|
||||||
|
let mut diagnostic = builder
|
||||||
|
.into_diagnostic("Cannot assign to undeclared variable in the body of a protocol class");
|
||||||
|
|
||||||
|
if definition.kind(db).is_unannotated_assignment() {
|
||||||
|
let binding_type = binding_type(db, definition);
|
||||||
|
|
||||||
|
let suggestion = binding_type
|
||||||
|
.literal_fallback_instance(db)
|
||||||
|
.unwrap_or(binding_type);
|
||||||
|
|
||||||
|
if should_give_hint(db, suggestion) {
|
||||||
|
diagnostic.set_primary_message(format_args!(
|
||||||
|
"Consider adding an annotation, e.g. `{symbol_name}: {} = ...`",
|
||||||
|
suggestion.display(db)
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
diagnostic.set_primary_message(format_args!(
|
||||||
|
"Consider adding an annotation for `{symbol_name}`"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
diagnostic.set_primary_message(format_args!(
|
||||||
|
"`{symbol_name}` is not declared as a protocol member"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut class_def_diagnostic = SubDiagnostic::new(
|
||||||
|
SubDiagnosticSeverity::Info,
|
||||||
|
"Assigning to an undeclared variable in a protocol class \
|
||||||
|
leads to an ambiguous interface",
|
||||||
|
);
|
||||||
|
class_def_diagnostic.annotate(
|
||||||
|
Annotation::primary(protocol_class.header_span(db))
|
||||||
|
.message(format_args!("`{class_name}` declared as a protocol here",)),
|
||||||
|
);
|
||||||
|
diagnostic.sub(class_def_diagnostic);
|
||||||
|
|
||||||
|
diagnostic.info(format_args!(
|
||||||
|
"No declarations found for `{symbol_name}` \
|
||||||
|
in the body of `{class_name}` or any of its superclasses"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn report_duplicate_bases(
|
pub(crate) fn report_duplicate_bases(
|
||||||
context: &InferContext,
|
context: &InferContext,
|
||||||
class: ClassLiteral,
|
class: ClassLiteral,
|
||||||
|
|
|
@ -1434,6 +1434,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(protocol) = class.into_protocol_class(self.db()) {
|
||||||
|
protocol.validate_members(&self.context, self.index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,10 @@ use ruff_python_ast::name::Name;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use super::TypeVarVariance;
|
use super::TypeVarVariance;
|
||||||
use crate::semantic_index::place_table;
|
use crate::semantic_index::place::ScopedPlaceId;
|
||||||
|
use crate::semantic_index::{SemanticIndex, place_table};
|
||||||
|
use crate::types::context::InferContext;
|
||||||
|
use crate::types::diagnostic::report_undeclared_protocol_member;
|
||||||
use crate::{
|
use crate::{
|
||||||
Db, FxOrderSet,
|
Db, FxOrderSet,
|
||||||
place::{Boundness, Place, PlaceAndQualifiers, place_from_bindings, place_from_declarations},
|
place::{Boundness, Place, PlaceAndQualifiers, place_from_bindings, place_from_declarations},
|
||||||
|
@ -55,6 +58,59 @@ impl<'db> ProtocolClassLiteral<'db> {
|
||||||
self.known_function_decorators(db)
|
self.known_function_decorators(db)
|
||||||
.contains(&KnownFunction::RuntimeCheckable)
|
.contains(&KnownFunction::RuntimeCheckable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Iterate through the body of the protocol class. Check that all definitions
|
||||||
|
/// in the protocol class body are either explicitly declared directly in the
|
||||||
|
/// class body, or are declared in a superclass of the protocol class.
|
||||||
|
pub(super) fn validate_members(self, context: &InferContext, index: &SemanticIndex<'db>) {
|
||||||
|
let db = context.db();
|
||||||
|
let interface = self.interface(db);
|
||||||
|
let class_place_table = index.place_table(self.body_scope(db).file_scope_id(db));
|
||||||
|
|
||||||
|
for (symbol_id, mut bindings_iterator) in
|
||||||
|
use_def_map(db, self.body_scope(db)).all_end_of_scope_symbol_bindings()
|
||||||
|
{
|
||||||
|
let symbol_name = class_place_table.symbol(symbol_id).name();
|
||||||
|
|
||||||
|
if !interface.includes_member(db, symbol_name) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let has_declaration = self
|
||||||
|
.iter_mro(db, None)
|
||||||
|
.filter_map(ClassBase::into_class)
|
||||||
|
.any(|superclass| {
|
||||||
|
let superclass_scope = superclass.class_literal(db).0.body_scope(db);
|
||||||
|
let Some(scoped_symbol_id) =
|
||||||
|
place_table(db, superclass_scope).symbol_id(symbol_name)
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
!place_from_declarations(
|
||||||
|
db,
|
||||||
|
index
|
||||||
|
.use_def_map(superclass_scope.file_scope_id(db))
|
||||||
|
.end_of_scope_declarations(ScopedPlaceId::Symbol(scoped_symbol_id)),
|
||||||
|
)
|
||||||
|
.into_place_and_conflicting_declarations()
|
||||||
|
.0
|
||||||
|
.place
|
||||||
|
.is_unbound()
|
||||||
|
});
|
||||||
|
|
||||||
|
if has_declaration {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(first_definition) =
|
||||||
|
bindings_iterator.find_map(|binding| binding.binding.definition())
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
report_undeclared_protocol_member(context, first_definition, self, class_place_table);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> Deref for ProtocolClassLiteral<'db> {
|
impl<'db> Deref for ProtocolClassLiteral<'db> {
|
||||||
|
@ -147,6 +203,10 @@ impl<'db> ProtocolInterface<'db> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn includes_member(self, db: &'db dyn Db, name: &str) -> bool {
|
||||||
|
self.inner(db).contains_key(name)
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn instance_member(self, db: &'db dyn Db, name: &str) -> PlaceAndQualifiers<'db> {
|
pub(super) fn instance_member(self, db: &'db dyn Db, name: &str) -> PlaceAndQualifiers<'db> {
|
||||||
self.member_by_name(db, name)
|
self.member_by_name(db, name)
|
||||||
.map(|member| PlaceAndQualifiers {
|
.map(|member| PlaceAndQualifiers {
|
||||||
|
|
12
ty.schema.json
generated
12
ty.schema.json
generated
|
@ -271,6 +271,16 @@
|
||||||
"Rules": {
|
"Rules": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"ambiguous-protocol-member": {
|
||||||
|
"title": "detects protocol classes with ambiguous interfaces",
|
||||||
|
"description": "## What it does\nChecks for protocol classes with members that will lead to ambiguous interfaces.\n\n## Why is this bad?\nAssigning to an undeclared variable in a protocol class leads to an ambiguous\ninterface which may lead to the type checker inferring unexpected things. It's\nrecommended to ensure that all members of a protocol class are explicitly declared.\n\n## Examples\n\n```py\nfrom typing import Protocol\n\nclass BaseProto(Protocol):\n a: int # fine (explicitly declared as `int`)\n def method_member(self) -> int: ... # fine: a method definition using `def` is considered a declaration\n c = \"some variable\" # error: no explicit declaration, leading to ambiguity\n b = method_member # error: no explicit declaration, leading to ambiguity\n\n # error: this creates implicit assignments of `d` and `e` in the protocol class body.\n # Were they really meant to be considered protocol members?\n for d, e in enumerate(range(42)):\n pass\n\nclass SubProto(BaseProto, Protocol):\n a = 42 # fine (declared in superclass)\n```",
|
||||||
|
"default": "warn",
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/Level"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"byte-string-type-annotation": {
|
"byte-string-type-annotation": {
|
||||||
"title": "detects byte strings in type annotation positions",
|
"title": "detects byte strings in type annotation positions",
|
||||||
"description": "## What it does\nChecks for byte-strings in type annotation positions.\n\n## Why is this bad?\nStatic analysis tools like ty can't analyze type annotations that use byte-string notation.\n\n## Examples\n```python\ndef test(): -> b\"int\":\n ...\n```\n\nUse instead:\n```python\ndef test(): -> \"int\":\n ...\n```",
|
"description": "## What it does\nChecks for byte-strings in type annotation positions.\n\n## Why is this bad?\nStatic analysis tools like ty can't analyze type annotations that use byte-string notation.\n\n## Examples\n```python\ndef test(): -> b\"int\":\n ...\n```\n\nUse instead:\n```python\ndef test(): -> \"int\":\n ...\n```",
|
||||||
|
@ -593,7 +603,7 @@
|
||||||
},
|
},
|
||||||
"invalid-protocol": {
|
"invalid-protocol": {
|
||||||
"title": "detects invalid protocol class definitions",
|
"title": "detects invalid protocol class definitions",
|
||||||
"description": "## What it does\nChecks for invalidly defined protocol classes.\n\n## Why is this bad?\nAn invalidly defined protocol class may lead to the type checker inferring\nunexpected things. It may also lead to `TypeError`s at runtime.\n\n## Examples\nA `Protocol` class cannot inherit from a non-`Protocol` class;\nthis raises a `TypeError` at runtime:\n\n```pycon\n>>> from typing import Protocol\n>>> class Foo(int, Protocol): ...\n...\nTraceback (most recent call last):\n File \"<python-input-1>\", line 1, in <module>\n class Foo(int, Protocol): ...\nTypeError: Protocols can only inherit from other protocols, got <class 'int'>\n```",
|
"description": "## What it does\nChecks for protocol classes that will raise `TypeError` at runtime.\n\n## Why is this bad?\nAn invalidly defined protocol class may lead to the type checker inferring\nunexpected things. It may also lead to `TypeError`s at runtime.\n\n## Examples\nA `Protocol` class cannot inherit from a non-`Protocol` class;\nthis raises a `TypeError` at runtime:\n\n```pycon\n>>> from typing import Protocol\n>>> class Foo(int, Protocol): ...\n...\nTraceback (most recent call last):\n File \"<python-input-1>\", line 1, in <module>\n class Foo(int, Protocol): ...\nTypeError: Protocols can only inherit from other protocols, got <class 'int'>\n```",
|
||||||
"default": "error",
|
"default": "error",
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue