mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-20 04:29:47 +00:00
[ty] implement typing.NewType by adding Type::NewTypeInstance
This commit is contained in:
parent
039a69fa8c
commit
5f3e086ee4
25 changed files with 1343 additions and 191 deletions
164
crates/ty/docs/rules.md
generated
164
crates/ty/docs/rules.md
generated
|
|
@ -39,7 +39,7 @@ def test(): -> "int":
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20call-non-callable" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L118" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L120" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -63,7 +63,7 @@ Calling a non-callable object will raise a `TypeError` at runtime.
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-argument-forms" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L162" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L164" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -95,7 +95,7 @@ f(int) # error
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-declarations" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L188" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L190" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -126,7 +126,7 @@ a = 1
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-metaclass" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L213" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L215" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -158,7 +158,7 @@ class C(A, B): ...
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20cyclic-class-definition" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L239" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L241" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -190,7 +190,7 @@ class B(A): ...
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20duplicate-base" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L304" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L306" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -217,7 +217,7 @@ class B(A, A): ...
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.12">0.0.1-alpha.12</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20duplicate-kw-only" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L325" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L327" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -329,7 +329,7 @@ def test(): -> "Literal[5]":
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20inconsistent-mro" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L529" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L531" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -359,7 +359,7 @@ class C(A, B): ...
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20index-out-of-bounds" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L553" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L555" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -385,7 +385,7 @@ t[3] # IndexError: tuple index out of range
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.12">0.0.1-alpha.12</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20instance-layout-conflict" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L357" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L359" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -474,7 +474,7 @@ an atypical memory layout.
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-argument-type" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L607" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L609" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -501,7 +501,7 @@ func("foo") # error: [invalid-argument-type]
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-assignment" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L647" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L649" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -529,7 +529,7 @@ a: int = ''
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-attribute-access" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1782" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1808" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -563,7 +563,7 @@ C.instance_var = 3 # error: Cannot assign to instance variable
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.19">0.0.1-alpha.19</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-await" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L669" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L671" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -599,7 +599,7 @@ asyncio.run(main())
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-base" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L699" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L701" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -623,7 +623,7 @@ class A(42): ... # error: [invalid-base]
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-context-manager" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L750" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L752" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -650,7 +650,7 @@ with 1:
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-declaration" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L771" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L773" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -679,7 +679,7 @@ a: str
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-exception-caught" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L794" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L796" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -723,7 +723,7 @@ except ZeroDivisionError:
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-generic-class" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L830" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L832" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -756,7 +756,7 @@ class C[U](Generic[T]): ...
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.17">0.0.1-alpha.17</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-key" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L574" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L576" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -795,7 +795,7 @@ carol = Person(name="Carol", age=25) # typo!
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-legacy-type-variable" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L856" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L858" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -830,7 +830,7 @@ def f(t: TypeVar("U")): ...
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-metaclass" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L929" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L955" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -864,7 +864,7 @@ class B(metaclass=f): ...
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.19">0.0.1-alpha.19</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-named-tuple" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L503" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L505" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -890,13 +890,43 @@ in a class's bases list.
|
|||
TypeError: can only inherit from a NamedTuple type and Generic
|
||||
```
|
||||
|
||||
## `invalid-newtype`
|
||||
|
||||
<small>
|
||||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Preview (since <a href="https://github.com/astral-sh/ty/releases/tag/1.0.0">1.0.0</a>) ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-newtype" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L931" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
**What it does**
|
||||
|
||||
Checks for the creation of invalid `NewType`s
|
||||
|
||||
**Why is this bad?**
|
||||
|
||||
There are several requirements that you must follow when creating a `NewType`.
|
||||
|
||||
**Examples**
|
||||
|
||||
```python
|
||||
from typing import NewType
|
||||
|
||||
def get_name() -> str: ...
|
||||
|
||||
Foo = NewType("Foo", int) # okay
|
||||
Bar = NewType(get_name(), int) # error: The first argument to `NewType` must be a string literal
|
||||
Baz = NewType("Baz", int | str) # error: invalid base for `typing.NewType`
|
||||
```
|
||||
|
||||
## `invalid-overload`
|
||||
|
||||
<small>
|
||||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-overload" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L956" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L982" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -946,7 +976,7 @@ def foo(x: int) -> int: ...
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-parameter-default" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1055" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1081" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -972,7 +1002,7 @@ def f(a: int = ''): ...
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-paramspec" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L884" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L886" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1003,7 +1033,7 @@ P2 = ParamSpec("S2") # error: ParamSpec name must match the variable it's assig
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-protocol" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L439" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L441" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1037,7 +1067,7 @@ TypeError: Protocols can only inherit from other protocols, got <class 'int'>
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-raise" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1075" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1101" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1086,7 +1116,7 @@ def g():
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-return-type" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L628" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L630" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1111,7 +1141,7 @@ def func() -> int:
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-super-argument" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1118" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1144" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1169,7 +1199,7 @@ TODO #14889
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.6">0.0.1-alpha.6</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-alias-type" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L908" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L910" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1196,7 +1226,7 @@ NewAlias = TypeAliasType(get_name(), int) # error: TypeAliasType name mus
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-checking-constant" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1157" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1183" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1226,7 +1256,7 @@ TYPE_CHECKING = ''
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-form" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1181" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1207" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1256,7 +1286,7 @@ b: Annotated[int] # `Annotated` expects at least two arguments
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.11">0.0.1-alpha.11</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-guard-call" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1233" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1259" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1290,7 +1320,7 @@ f(10) # Error
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.11">0.0.1-alpha.11</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-guard-definition" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1205" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1231" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1324,7 +1354,7 @@ class C:
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-variable-constraints" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1261" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1287" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1359,7 +1389,7 @@ T = TypeVar('T', bound=str) # valid bound TypeVar
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20missing-argument" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1290" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1316" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1384,7 +1414,7 @@ func() # TypeError: func() missing 1 required positional argument: 'x'
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.20">0.0.1-alpha.20</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20missing-typed-dict-key" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1883" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1909" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1417,7 +1447,7 @@ alice["age"] # KeyError
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20no-matching-overload" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1309" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1335" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1446,7 +1476,7 @@ func("string") # error: [no-matching-overload]
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20non-subscriptable" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1332" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1358" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1470,7 +1500,7 @@ Subscripting an object that does not support it will raise a `TypeError` at runt
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20not-iterable" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1350" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1376" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1496,7 +1526,7 @@ for i in 34: # TypeError: 'int' object is not iterable
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20parameter-already-assigned" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1401" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1427" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1523,7 +1553,7 @@ f(1, x=2) # Error raised here
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.22">0.0.1-alpha.22</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20positional-only-parameter-as-kwarg" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1636" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1662" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1581,7 +1611,7 @@ def test(): -> "int":
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20static-assert-error" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1758" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1784" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1611,7 +1641,7 @@ static_assert(int(2.0 * 3.0) == 6) # error: does not have a statically known tr
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20subclass-of-final-class" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1492" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1518" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1640,7 +1670,7 @@ class B(A): ... # Error raised here
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20too-many-positional-arguments" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1537" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1563" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1667,7 +1697,7 @@ f("foo") # Error raised here
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20type-assertion-failure" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1515" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1541" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1695,7 +1725,7 @@ def _(x: int):
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unavailable-implicit-super-arguments" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1558" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1584" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1741,7 +1771,7 @@ class A:
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unknown-argument" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1615" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1641" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1768,7 +1798,7 @@ f(x=1, y=2) # Error raised here
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-attribute" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1657" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1683" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1796,7 +1826,7 @@ A().foo # AttributeError: 'A' object has no attribute 'foo'
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-import" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1679" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1705" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1821,7 +1851,7 @@ import foo # ModuleNotFoundError: No module named 'foo'
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-reference" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1698" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1724" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1846,7 +1876,7 @@ print(x) # NameError: name 'x' is not defined
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-bool-conversion" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1370" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1396" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1883,7 +1913,7 @@ b1 < b2 < b1 # exception raised here
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-operator" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1717" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1743" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1911,7 +1941,7 @@ A() + A() # TypeError: unsupported operand type(s) for +: 'A' and 'A'
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'error'."><code>error</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20zero-stepsize-in-slice" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1739" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1765" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1936,7 +1966,7 @@ l[1:10:0] # ValueError: slice step cannot be zero
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.20">0.0.1-alpha.20</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20ambiguous-protocol-member" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L468" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L470" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -1977,7 +2007,7 @@ class SubProto(BaseProto, Protocol):
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.16">0.0.1-alpha.16</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20deprecated" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L283" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L285" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -2065,7 +2095,7 @@ a = 20 / 0 # type: ignore
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.22">0.0.1-alpha.22</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-missing-attribute" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1422" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1448" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -2093,7 +2123,7 @@ A.c # AttributeError: type object 'A' has no attribute 'c'
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.22">0.0.1-alpha.22</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-missing-implicit-call" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L136" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L138" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -2125,7 +2155,7 @@ A()[0] # TypeError: 'A' object is not subscriptable
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.22">0.0.1-alpha.22</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-missing-import" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1444" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1470" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -2157,7 +2187,7 @@ from module import a # ImportError: cannot import name 'a' from 'module'
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20redundant-cast" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1810" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1836" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -2184,7 +2214,7 @@ cast(int, f()) # Redundant
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20undefined-reveal" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1597" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1623" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -2208,7 +2238,7 @@ reveal_type(1) # NameError: name 'reveal_type' is not defined
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.15">0.0.1-alpha.15</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-global" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1831" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1857" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -2266,7 +2296,7 @@ def g():
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.7">0.0.1-alpha.7</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-base" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L717" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L719" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -2305,7 +2335,7 @@ class D(C): ... # error: [unsupported-base]
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'warn'."><code>warn</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.22">0.0.1-alpha.22</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20useless-overload-body" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L999" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1025" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -2368,7 +2398,7 @@ def foo(x: int | str) -> int | str:
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'ignore'."><code>ignore</code></a> ·
|
||||
Preview (since <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a>) ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20division-by-zero" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L265" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L267" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
@ -2392,7 +2422,7 @@ Dividing by zero raises a `ZeroDivisionError` at runtime.
|
|||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of 'ignore'."><code>ignore</code></a> ·
|
||||
Added in <a href="https://github.com/astral-sh/ty/releases/tag/0.0.1-alpha.1">0.0.1-alpha.1</a> ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unresolved-reference" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1470" target="_blank">View source</a>
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1496" target="_blank">View source</a>
|
||||
</small>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -127,7 +127,8 @@ impl<'db> Completion<'db> {
|
|||
Type::NominalInstance(_)
|
||||
| Type::PropertyInstance(_)
|
||||
| Type::BoundSuper(_)
|
||||
| Type::TypedDict(_) => CompletionKind::Struct,
|
||||
| Type::TypedDict(_)
|
||||
| Type::NewTypeInstance(_) => CompletionKind::Struct,
|
||||
Type::IntLiteral(_)
|
||||
| Type::BooleanLiteral(_)
|
||||
| Type::TypeIs(_)
|
||||
|
|
|
|||
|
|
@ -209,16 +209,11 @@ impl<'db> DefinitionsOrTargets<'db> {
|
|||
ty_python_semantic::types::TypeDefinition::Module(module) => {
|
||||
ResolvedDefinition::Module(module.file(db)?)
|
||||
}
|
||||
ty_python_semantic::types::TypeDefinition::Class(definition) => {
|
||||
ResolvedDefinition::Definition(definition)
|
||||
}
|
||||
ty_python_semantic::types::TypeDefinition::Function(definition) => {
|
||||
ResolvedDefinition::Definition(definition)
|
||||
}
|
||||
ty_python_semantic::types::TypeDefinition::TypeVar(definition) => {
|
||||
ResolvedDefinition::Definition(definition)
|
||||
}
|
||||
ty_python_semantic::types::TypeDefinition::TypeAlias(definition) => {
|
||||
ty_python_semantic::types::TypeDefinition::Class(definition)
|
||||
| ty_python_semantic::types::TypeDefinition::Function(definition)
|
||||
| ty_python_semantic::types::TypeDefinition::TypeVar(definition)
|
||||
| ty_python_semantic::types::TypeDefinition::TypeAlias(definition)
|
||||
| ty_python_semantic::types::TypeDefinition::NewType(definition) => {
|
||||
ResolvedDefinition::Definition(definition)
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
# NewType
|
||||
|
||||
Currently, ty doesn't support `typing.NewType` in type annotations.
|
||||
|
||||
## Valid forms
|
||||
|
||||
```py
|
||||
|
|
@ -12,13 +10,389 @@ X = GenericAlias(type, ())
|
|||
A = NewType("A", int)
|
||||
# TODO: typeshed for `typing.GenericAlias` uses `type` for the first argument. `NewType` should be special-cased
|
||||
# to be compatible with `type`
|
||||
# error: [invalid-argument-type] "Argument to function `__new__` is incorrect: Expected `type`, found `NewType`"
|
||||
# error: [invalid-argument-type] "Argument to function `__new__` is incorrect: Expected `type`, found `<NewType pseudo-class 'A'>`"
|
||||
B = GenericAlias(A, ())
|
||||
|
||||
def _(
|
||||
a: A,
|
||||
b: B,
|
||||
):
|
||||
reveal_type(a) # revealed: @Todo(Support for `typing.NewType` instances in type expressions)
|
||||
reveal_type(a) # revealed: A
|
||||
reveal_type(b) # revealed: @Todo(Support for `typing.GenericAlias` instances in type expressions)
|
||||
```
|
||||
|
||||
## Subtyping
|
||||
|
||||
The basic purpose of `NewType` is that it acts like a subtype of its base, but not the exact same
|
||||
type (i.e. not an alias).
|
||||
|
||||
```py
|
||||
from typing_extensions import NewType
|
||||
from ty_extensions import static_assert, is_subtype_of, is_equivalent_to
|
||||
|
||||
Foo = NewType("Foo", int)
|
||||
Bar = NewType("Bar", Foo)
|
||||
|
||||
static_assert(is_subtype_of(Foo, int))
|
||||
static_assert(not is_equivalent_to(Foo, int))
|
||||
|
||||
static_assert(is_subtype_of(Bar, Foo))
|
||||
static_assert(is_subtype_of(Bar, int))
|
||||
static_assert(not is_equivalent_to(Bar, Foo))
|
||||
|
||||
Foo(42)
|
||||
Foo(Foo(42)) # allowed: `Foo` is a subtype of `int`.
|
||||
Foo(Bar(Foo(42))) # allowed: `Bar` is a subtype of `int`.
|
||||
Foo(True) # allowed: `bool` is a subtype of `int`.
|
||||
Foo("forty-two") # error: [invalid-argument-type] "Argument is incorrect: Expected `int`, found `Literal["forty-two"]`"
|
||||
|
||||
def f(_: int): ...
|
||||
def g(_: Foo): ...
|
||||
def h(_: Bar): ...
|
||||
|
||||
f(42)
|
||||
f(Foo(42))
|
||||
f(Bar(Foo(42)))
|
||||
|
||||
g(42) # error: [invalid-argument-type] "Argument to function `g` is incorrect: Expected `Foo`, found `Literal[42]`"
|
||||
g(Foo(42))
|
||||
g(Bar(Foo(42)))
|
||||
|
||||
h(42) # error: [invalid-argument-type] "Argument to function `h` is incorrect: Expected `Bar`, found `Literal[42]`"
|
||||
h(Foo(42)) # error: [invalid-argument-type] "Argument to function `h` is incorrect: Expected `Bar`, found `Foo`"
|
||||
h(Bar(Foo(42)))
|
||||
```
|
||||
|
||||
## Member and method lookup work
|
||||
|
||||
```py
|
||||
from typing_extensions import NewType
|
||||
|
||||
class Foo:
|
||||
foo_member: str = "hello"
|
||||
def foo_method(self) -> int:
|
||||
return 42
|
||||
|
||||
Bar = NewType("Bar", Foo)
|
||||
Baz = NewType("Baz", Bar)
|
||||
baz = Baz(Bar(Foo()))
|
||||
reveal_type(baz.foo_member) # revealed: str
|
||||
reveal_type(baz.foo_method()) # revealed: int
|
||||
```
|
||||
|
||||
We also infer member access on the `NewType` pseudo-type itself correctly:
|
||||
|
||||
```py
|
||||
reveal_type(Bar.__supertype__) # revealed: type | NewType
|
||||
reveal_type(Baz.__supertype__) # revealed: type | NewType
|
||||
```
|
||||
|
||||
## `NewType` wrapper functions are `Callable`
|
||||
|
||||
```py
|
||||
from collections.abc import Callable
|
||||
from typing_extensions import NewType
|
||||
from ty_extensions import CallableTypeOf
|
||||
|
||||
Foo = NewType("Foo", int)
|
||||
|
||||
def _(obj: CallableTypeOf[Foo]):
|
||||
reveal_type(obj) # revealed: (int, /) -> Foo
|
||||
|
||||
def f(_: Callable[[int], Foo]): ...
|
||||
|
||||
f(Foo)
|
||||
map(Foo, [1, 2, 3])
|
||||
|
||||
def g(_: Callable[[str], Foo]): ...
|
||||
|
||||
g(Foo) # error: [invalid-argument-type]
|
||||
```
|
||||
|
||||
## `NewType` instances are `Callable` if the base type is
|
||||
|
||||
```py
|
||||
from typing import NewType, Callable, Any
|
||||
from ty_extensions import CallableTypeOf
|
||||
|
||||
N = NewType("N", int)
|
||||
i = N(42)
|
||||
|
||||
y: Callable[..., Any] = i # error: [invalid-assignment] "Object of type `N` is not assignable to `(...) -> Any`"
|
||||
|
||||
# error: [invalid-type-form] "Expected the first argument to `ty_extensions.CallableTypeOf` to be a callable object, but got an object of type `N`"
|
||||
def f(x: CallableTypeOf[i]):
|
||||
reveal_type(x) # revealed: Unknown
|
||||
|
||||
class SomethingCallable:
|
||||
def __call__(self, a: str) -> bytes:
|
||||
raise NotImplementedError
|
||||
|
||||
N2 = NewType("N2", SomethingCallable)
|
||||
j = N2(SomethingCallable())
|
||||
|
||||
z: Callable[[str], bytes] = j # fine
|
||||
|
||||
def g(x: CallableTypeOf[j]):
|
||||
reveal_type(x) # revealed: (a: str) -> bytes
|
||||
```
|
||||
|
||||
## The name must be a string literal
|
||||
|
||||
```py
|
||||
from typing_extensions import NewType
|
||||
|
||||
def _(name: str) -> None:
|
||||
_ = NewType(name, int) # error: [invalid-newtype] "The first argument to `NewType` must be a string literal"
|
||||
```
|
||||
|
||||
However, the literal doesn't necessarily need to be inline, as long as we infer it:
|
||||
|
||||
```py
|
||||
name = "Foo"
|
||||
Foo = NewType(name, int)
|
||||
reveal_type(Foo) # revealed: <NewType pseudo-class 'Foo'>
|
||||
```
|
||||
|
||||
## The second argument must be a class type or another newtype
|
||||
|
||||
Other typing constructs like `Union` are not allowed.
|
||||
|
||||
```py
|
||||
from typing_extensions import NewType
|
||||
|
||||
# error: [invalid-newtype] "invalid base for `typing.NewType`"
|
||||
Foo = NewType("Foo", int | str)
|
||||
```
|
||||
|
||||
We don't emit the "invalid base" diagnostic for `Unknown`, because that typically results from other
|
||||
errors that already have a diagnostic, and there's no need to pile on. For example, this mistake
|
||||
gives you an "Int literals are not allowed" error, and we'd rather not see an "invalid base" error
|
||||
on top of that:
|
||||
|
||||
```py
|
||||
# error: [invalid-type-form] "Int literals are not allowed in this context in a type expression"
|
||||
Foo = NewType("Foo", 42)
|
||||
```
|
||||
|
||||
## A `NewType` definition must be a simple variable assignment
|
||||
|
||||
```py
|
||||
from typing import NewType
|
||||
|
||||
N: NewType = NewType("N", int) # error: [invalid-newtype] "A `NewType` definition must be a simple variable assignment"
|
||||
```
|
||||
|
||||
## Newtypes can be cyclic in various ways
|
||||
|
||||
Cyclic newtypes are kind of silly, but it's possible for the user to express them, and it's
|
||||
important that we don't go into infinite recursive loops and crash with a stack overflow. In fact,
|
||||
this is *why* base type evaluation is deferred; otherwise Salsa itself would crash.
|
||||
|
||||
```py
|
||||
from typing_extensions import NewType, reveal_type, cast
|
||||
|
||||
# Define a directly cyclic newtype.
|
||||
A = NewType("A", "A")
|
||||
reveal_type(A) # revealed: <NewType pseudo-class 'A'>
|
||||
|
||||
# Typechecking still works. We can't construct an `A` "honestly", but we can `cast` into one.
|
||||
a: A
|
||||
a = 42 # error: [invalid-assignment] "Object of type `Literal[42]` is not assignable to `A`"
|
||||
a = A(42) # error: [invalid-argument-type] "Argument is incorrect: Expected `A`, found `Literal[42]`"
|
||||
a = cast(A, 42)
|
||||
reveal_type(a) # revealed: A
|
||||
|
||||
# A newtype cycle might involve more than one step.
|
||||
B = NewType("B", "C")
|
||||
C = NewType("C", "B")
|
||||
reveal_type(B) # revealed: <NewType pseudo-class 'B'>
|
||||
reveal_type(C) # revealed: <NewType pseudo-class 'C'>
|
||||
b: B = cast(B, 42)
|
||||
c: C = C(b)
|
||||
reveal_type(b) # revealed: B
|
||||
reveal_type(c) # revealed: C
|
||||
# Cyclic types behave in surprising ways. These assignments are legal, even though B and C aren't
|
||||
# the same type, because each of them is a subtype of the other.
|
||||
b = c
|
||||
c = b
|
||||
|
||||
# Another newtype could inherit from a cyclic one.
|
||||
D = NewType("D", C)
|
||||
reveal_type(D) # revealed: <NewType pseudo-class 'D'>
|
||||
d: D
|
||||
d = D(42) # error: [invalid-argument-type] "Argument is incorrect: Expected `C`, found `Literal[42]`"
|
||||
d = D(c)
|
||||
d = D(b) # Allowed, the same surprise as above. B and C are subtypes of each other.
|
||||
reveal_type(d) # revealed: D
|
||||
```
|
||||
|
||||
Normal classes can't inherit from newtypes, but generic classes can be parametrized with them, so we
|
||||
also need to detect "ordinary" type cycles that happen to involve a newtype.
|
||||
|
||||
```py
|
||||
E = NewType("E", list["E"])
|
||||
reveal_type(E) # revealed: <NewType pseudo-class 'E'>
|
||||
e: E = E([])
|
||||
reveal_type(e) # revealed: E
|
||||
reveal_type(E(E(E(E(E([])))))) # revealed: E
|
||||
reveal_type(E([E([E([]), E([E([])])]), E([])])) # revealed: E
|
||||
E(["foo"]) # error: [invalid-argument-type]
|
||||
E(E(E(["foo"]))) # error: [invalid-argument-type]
|
||||
```
|
||||
|
||||
## `NewType` wrapping preserves singleton-ness and single-valued-ness
|
||||
|
||||
```py
|
||||
from typing_extensions import NewType
|
||||
from ty_extensions import is_singleton, is_single_valued, static_assert
|
||||
from types import EllipsisType
|
||||
|
||||
A = NewType("A", EllipsisType)
|
||||
static_assert(is_singleton(A))
|
||||
static_assert(is_single_valued(A))
|
||||
reveal_type(type(A(...)) is EllipsisType) # revealed: Literal[True]
|
||||
# TODO: This should be `Literal[True]` also.
|
||||
reveal_type(A(...) is ...) # revealed: bool
|
||||
|
||||
B = NewType("B", int)
|
||||
static_assert(not is_singleton(B))
|
||||
static_assert(not is_single_valued(B))
|
||||
```
|
||||
|
||||
## `NewType`s of tuples can be iterated/unpacked
|
||||
|
||||
```py
|
||||
from typing import NewType
|
||||
|
||||
N = NewType("N", tuple[int, str])
|
||||
|
||||
a, b = N((1, "foo"))
|
||||
|
||||
reveal_type(a) # revealed: int
|
||||
reveal_type(b) # revealed: str
|
||||
```
|
||||
|
||||
## `isinstance` of a `NewType` instance and its base class is inferred as `Literal[True]`
|
||||
|
||||
```py
|
||||
from typing import NewType
|
||||
|
||||
N = NewType("N", int)
|
||||
|
||||
def f(x: N):
|
||||
reveal_type(isinstance(x, int)) # revealed: Literal[True]
|
||||
```
|
||||
|
||||
However, a `NewType` isn't a real class, so it isn't a valid second argument to `isinstance`:
|
||||
|
||||
```py
|
||||
def f(x: N):
|
||||
# error: [invalid-argument-type] "Argument to function `isinstance` is incorrect"
|
||||
reveal_type(isinstance(x, N)) # revealed: bool
|
||||
```
|
||||
|
||||
Because of that, we don't generate any narrowing constraints for it:
|
||||
|
||||
```py
|
||||
def f(x: N | str):
|
||||
if isinstance(x, N): # error: [invalid-argument-type]
|
||||
reveal_type(x) # revealed: N | str
|
||||
else:
|
||||
reveal_type(x) # revealed: N | str
|
||||
```
|
||||
|
||||
## Trying to subclass a `NewType` produces an error matching CPython
|
||||
|
||||
<!-- snapshot-diagnostics -->
|
||||
|
||||
```py
|
||||
from typing import NewType
|
||||
|
||||
X = NewType("X", int)
|
||||
|
||||
class Foo(X): ... # error: [invalid-base]
|
||||
```
|
||||
|
||||
## Don't narrow `NewType`-wrapped `Enum`s inside of match arms
|
||||
|
||||
`Literal[Foo.X]` is actually disjoint from `N` here:
|
||||
|
||||
```py
|
||||
from enum import Enum
|
||||
from typing import NewType
|
||||
|
||||
class Foo(Enum):
|
||||
X = 0
|
||||
Y = 1
|
||||
|
||||
N = NewType("N", Foo)
|
||||
|
||||
def f(x: N):
|
||||
match x:
|
||||
case Foo.X:
|
||||
reveal_type(x) # revealed: N
|
||||
case Foo.Y:
|
||||
reveal_type(x) # revealed: N
|
||||
case _:
|
||||
reveal_type(x) # revealed: N
|
||||
```
|
||||
|
||||
## We don't support `NewType` on Python 3.9
|
||||
|
||||
We implement `typing.NewType` as a `KnownClass`, but in Python 3.9 it's actually a function, so all
|
||||
we get is the `Any` annotations from typeshed. However, `typing_extensions.NewType` is always a
|
||||
class. This could be improved in the future, but Python 3.9 is now end-of-life, so it's not
|
||||
high-priority.
|
||||
|
||||
```toml
|
||||
[environment]
|
||||
python-version = "3.9"
|
||||
```
|
||||
|
||||
```py
|
||||
from typing import NewType
|
||||
|
||||
Foo = NewType("Foo", int)
|
||||
reveal_type(Foo) # revealed: Any
|
||||
reveal_type(Foo(42)) # revealed: Any
|
||||
|
||||
from typing_extensions import NewType
|
||||
|
||||
Bar = NewType("Bar", int)
|
||||
reveal_type(Bar) # revealed: <NewType pseudo-class 'Bar'>
|
||||
reveal_type(Bar(42)) # revealed: Bar
|
||||
```
|
||||
|
||||
## The base of a `NewType` can't be a protocol class or a `TypedDict`
|
||||
|
||||
<!-- snapshot-diagnostics -->
|
||||
|
||||
```py
|
||||
from typing import NewType, Protocol, TypedDict
|
||||
|
||||
class Id(Protocol):
|
||||
code: int
|
||||
|
||||
UserId = NewType("UserId", Id) # error: [invalid-newtype]
|
||||
|
||||
class Foo(TypedDict):
|
||||
a: int
|
||||
|
||||
Bar = NewType("Bar", Foo) # error: [invalid-newtype]
|
||||
```
|
||||
|
||||
## TODO: A `NewType` cannot be generic
|
||||
|
||||
```py
|
||||
from typing import Any, NewType, TypeVar
|
||||
|
||||
# All of these are allowed.
|
||||
A = NewType("A", list)
|
||||
B = NewType("B", list[int])
|
||||
B = NewType("B", list[Any])
|
||||
|
||||
# But a free typevar is not allowed.
|
||||
T = TypeVar("T")
|
||||
C = NewType("C", list[T]) # TODO: should be "error: [invalid-newtype]"
|
||||
```
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ synthesized `Protocol`s that cannot be upcast to, or interpreted as, a non-`obje
|
|||
|
||||
```py
|
||||
import types
|
||||
from typing_extensions import Callable, TypeIs, Literal, TypedDict
|
||||
from typing_extensions import Callable, TypeIs, Literal, NewType, TypedDict
|
||||
|
||||
def f(): ...
|
||||
|
||||
|
|
@ -81,6 +81,8 @@ class SomeTypedDict(TypedDict):
|
|||
x: int
|
||||
y: bytes
|
||||
|
||||
N = NewType("N", int)
|
||||
|
||||
# revealed: <super: <class 'object'>, FunctionType>
|
||||
reveal_type(super(object, f))
|
||||
# revealed: <super: <class 'object'>, WrapperDescriptorType>
|
||||
|
|
@ -95,6 +97,8 @@ reveal_type(super(object, Alias))
|
|||
reveal_type(super(object, Foo().method))
|
||||
# revealed: <super: <class 'object'>, property>
|
||||
reveal_type(super(object, Foo.some_property))
|
||||
# revealed: <super: <class 'object'>, int>
|
||||
reveal_type(super(object, N(42)))
|
||||
|
||||
def g(x: object) -> TypeIs[list[object]]:
|
||||
return isinstance(x, list)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
---
|
||||
source: crates/ty_test/src/lib.rs
|
||||
expression: snapshot
|
||||
---
|
||||
---
|
||||
mdtest name: new_types.md - NewType - The base of a `NewType` can't be a protocol class or a `TypedDict`
|
||||
mdtest path: crates/ty_python_semantic/resources/mdtest/annotations/new_types.md
|
||||
---
|
||||
|
||||
# Python source files
|
||||
|
||||
## mdtest_snippet.py
|
||||
|
||||
```
|
||||
1 | from typing import NewType, Protocol, TypedDict
|
||||
2 |
|
||||
3 | class Id(Protocol):
|
||||
4 | code: int
|
||||
5 |
|
||||
6 | UserId = NewType("UserId", Id) # error: [invalid-newtype]
|
||||
7 |
|
||||
8 | class Foo(TypedDict):
|
||||
9 | a: int
|
||||
10 |
|
||||
11 | Bar = NewType("Bar", Foo) # error: [invalid-newtype]
|
||||
```
|
||||
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
error[invalid-newtype]: invalid base for `typing.NewType`
|
||||
--> src/mdtest_snippet.py:6:28
|
||||
|
|
||||
4 | code: int
|
||||
5 |
|
||||
6 | UserId = NewType("UserId", Id) # error: [invalid-newtype]
|
||||
| ^^ type `Id`
|
||||
7 |
|
||||
8 | class Foo(TypedDict):
|
||||
|
|
||||
info: The base of a `NewType` is not allowed to be a protocol class.
|
||||
info: rule `invalid-newtype` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error[invalid-newtype]: invalid base for `typing.NewType`
|
||||
--> src/mdtest_snippet.py:11:22
|
||||
|
|
||||
9 | a: int
|
||||
10 |
|
||||
11 | Bar = NewType("Bar", Foo) # error: [invalid-newtype]
|
||||
| ^^^ type `Foo`
|
||||
|
|
||||
info: The base of a `NewType` is not allowed to be a `TypedDict`.
|
||||
info: rule `invalid-newtype` is enabled by default
|
||||
|
||||
```
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
---
|
||||
source: crates/ty_test/src/lib.rs
|
||||
expression: snapshot
|
||||
---
|
||||
---
|
||||
mdtest name: new_types.md - NewType - Trying to subclass a `NewType` produces an error matching CPython
|
||||
mdtest path: crates/ty_python_semantic/resources/mdtest/annotations/new_types.md
|
||||
---
|
||||
|
||||
# Python source files
|
||||
|
||||
## mdtest_snippet.py
|
||||
|
||||
```
|
||||
1 | from typing import NewType
|
||||
2 |
|
||||
3 | X = NewType("X", int)
|
||||
4 |
|
||||
5 | class Foo(X): ... # error: [invalid-base]
|
||||
```
|
||||
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
error[invalid-base]: Cannot subclass an instance of NewType
|
||||
--> src/mdtest_snippet.py:5:11
|
||||
|
|
||||
3 | X = NewType("X", int)
|
||||
4 |
|
||||
5 | class Foo(X): ... # error: [invalid-base]
|
||||
| ^
|
||||
|
|
||||
info: Perhaps you were looking for: `Foo = NewType('Foo', X)`
|
||||
info: Definition of class `Foo` will raise `TypeError` at runtime
|
||||
info: rule `invalid-base` is enabled by default
|
||||
|
||||
```
|
||||
|
|
@ -46,7 +46,7 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/class/super.md
|
|||
32 | reveal_type(super(C, C()).aa) # revealed: int
|
||||
33 | reveal_type(super(C, C()).bb) # revealed: int
|
||||
34 | import types
|
||||
35 | from typing_extensions import Callable, TypeIs, Literal, TypedDict
|
||||
35 | from typing_extensions import Callable, TypeIs, Literal, NewType, TypedDict
|
||||
36 |
|
||||
37 | def f(): ...
|
||||
38 |
|
||||
|
|
@ -61,59 +61,63 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/class/super.md
|
|||
47 | x: int
|
||||
48 | y: bytes
|
||||
49 |
|
||||
50 | # revealed: <super: <class 'object'>, FunctionType>
|
||||
51 | reveal_type(super(object, f))
|
||||
52 | # revealed: <super: <class 'object'>, WrapperDescriptorType>
|
||||
53 | reveal_type(super(object, types.FunctionType.__get__))
|
||||
54 | # revealed: <super: <class 'object'>, GenericAlias>
|
||||
55 | reveal_type(super(object, Foo[int]))
|
||||
56 | # revealed: <super: <class 'object'>, _SpecialForm>
|
||||
57 | reveal_type(super(object, Literal))
|
||||
58 | # revealed: <super: <class 'object'>, TypeAliasType>
|
||||
59 | reveal_type(super(object, Alias))
|
||||
60 | # revealed: <super: <class 'object'>, MethodType>
|
||||
61 | reveal_type(super(object, Foo().method))
|
||||
62 | # revealed: <super: <class 'object'>, property>
|
||||
63 | reveal_type(super(object, Foo.some_property))
|
||||
64 |
|
||||
65 | def g(x: object) -> TypeIs[list[object]]:
|
||||
66 | return isinstance(x, list)
|
||||
67 |
|
||||
68 | def _(x: object, y: SomeTypedDict, z: Callable[[int, str], bool]):
|
||||
69 | if hasattr(x, "bar"):
|
||||
70 | # revealed: <Protocol with members 'bar'>
|
||||
71 | reveal_type(x)
|
||||
72 | # error: [invalid-super-argument]
|
||||
73 | # revealed: Unknown
|
||||
74 | reveal_type(super(object, x))
|
||||
75 |
|
||||
76 | # error: [invalid-super-argument]
|
||||
77 | # revealed: Unknown
|
||||
78 | reveal_type(super(object, z))
|
||||
50 | N = NewType("N", int)
|
||||
51 |
|
||||
52 | # revealed: <super: <class 'object'>, FunctionType>
|
||||
53 | reveal_type(super(object, f))
|
||||
54 | # revealed: <super: <class 'object'>, WrapperDescriptorType>
|
||||
55 | reveal_type(super(object, types.FunctionType.__get__))
|
||||
56 | # revealed: <super: <class 'object'>, GenericAlias>
|
||||
57 | reveal_type(super(object, Foo[int]))
|
||||
58 | # revealed: <super: <class 'object'>, _SpecialForm>
|
||||
59 | reveal_type(super(object, Literal))
|
||||
60 | # revealed: <super: <class 'object'>, TypeAliasType>
|
||||
61 | reveal_type(super(object, Alias))
|
||||
62 | # revealed: <super: <class 'object'>, MethodType>
|
||||
63 | reveal_type(super(object, Foo().method))
|
||||
64 | # revealed: <super: <class 'object'>, property>
|
||||
65 | reveal_type(super(object, Foo.some_property))
|
||||
66 | # revealed: <super: <class 'object'>, int>
|
||||
67 | reveal_type(super(object, N(42)))
|
||||
68 |
|
||||
69 | def g(x: object) -> TypeIs[list[object]]:
|
||||
70 | return isinstance(x, list)
|
||||
71 |
|
||||
72 | def _(x: object, y: SomeTypedDict, z: Callable[[int, str], bool]):
|
||||
73 | if hasattr(x, "bar"):
|
||||
74 | # revealed: <Protocol with members 'bar'>
|
||||
75 | reveal_type(x)
|
||||
76 | # error: [invalid-super-argument]
|
||||
77 | # revealed: Unknown
|
||||
78 | reveal_type(super(object, x))
|
||||
79 |
|
||||
80 | is_list = g(x)
|
||||
81 | # revealed: TypeIs[list[object] @ x]
|
||||
82 | reveal_type(is_list)
|
||||
83 | # revealed: <super: <class 'object'>, bool>
|
||||
84 | reveal_type(super(object, is_list))
|
||||
85 |
|
||||
86 | # revealed: <super: <class 'object'>, dict[Literal["x", "y"], int | bytes]>
|
||||
87 | reveal_type(super(object, y))
|
||||
88 |
|
||||
89 | # The first argument to `super()` must be an actual class object;
|
||||
90 | # instances of `GenericAlias` are not accepted at runtime:
|
||||
91 | #
|
||||
92 | # error: [invalid-super-argument]
|
||||
93 | # revealed: Unknown
|
||||
94 | reveal_type(super(list[int], []))
|
||||
95 | class Super:
|
||||
96 | def method(self) -> int:
|
||||
97 | return 42
|
||||
98 |
|
||||
99 | class Sub(Super):
|
||||
100 | def method(self: Sub) -> int:
|
||||
101 | # revealed: <super: <class 'Sub'>, Sub>
|
||||
102 | return reveal_type(super(self.__class__, self)).method()
|
||||
80 | # error: [invalid-super-argument]
|
||||
81 | # revealed: Unknown
|
||||
82 | reveal_type(super(object, z))
|
||||
83 |
|
||||
84 | is_list = g(x)
|
||||
85 | # revealed: TypeIs[list[object] @ x]
|
||||
86 | reveal_type(is_list)
|
||||
87 | # revealed: <super: <class 'object'>, bool>
|
||||
88 | reveal_type(super(object, is_list))
|
||||
89 |
|
||||
90 | # revealed: <super: <class 'object'>, dict[Literal["x", "y"], int | bytes]>
|
||||
91 | reveal_type(super(object, y))
|
||||
92 |
|
||||
93 | # The first argument to `super()` must be an actual class object;
|
||||
94 | # instances of `GenericAlias` are not accepted at runtime:
|
||||
95 | #
|
||||
96 | # error: [invalid-super-argument]
|
||||
97 | # revealed: Unknown
|
||||
98 | reveal_type(super(list[int], []))
|
||||
99 | class Super:
|
||||
100 | def method(self) -> int:
|
||||
101 | return 42
|
||||
102 |
|
||||
103 | class Sub(Super):
|
||||
104 | def method(self: Sub) -> int:
|
||||
105 | # revealed: <super: <class 'Sub'>, Sub>
|
||||
106 | return reveal_type(super(self.__class__, self)).method()
|
||||
```
|
||||
|
||||
# Diagnostics
|
||||
|
|
@ -206,14 +210,14 @@ info: rule `unresolved-attribute` is enabled by default
|
|||
|
||||
```
|
||||
error[invalid-super-argument]: `<Protocol with members 'bar'>` is an abstract/structural type in `super(<class 'object'>, <Protocol with members 'bar'>)` call
|
||||
--> src/mdtest_snippet.py:74:21
|
||||
--> src/mdtest_snippet.py:78:21
|
||||
|
|
||||
72 | # error: [invalid-super-argument]
|
||||
73 | # revealed: Unknown
|
||||
74 | reveal_type(super(object, x))
|
||||
76 | # error: [invalid-super-argument]
|
||||
77 | # revealed: Unknown
|
||||
78 | reveal_type(super(object, x))
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
75 |
|
||||
76 | # error: [invalid-super-argument]
|
||||
79 |
|
||||
80 | # error: [invalid-super-argument]
|
||||
|
|
||||
info: rule `invalid-super-argument` is enabled by default
|
||||
|
||||
|
|
@ -221,14 +225,14 @@ info: rule `invalid-super-argument` is enabled by default
|
|||
|
||||
```
|
||||
error[invalid-super-argument]: `(int, str, /) -> bool` is an abstract/structural type in `super(<class 'object'>, (int, str, /) -> bool)` call
|
||||
--> src/mdtest_snippet.py:78:17
|
||||
--> src/mdtest_snippet.py:82:17
|
||||
|
|
||||
76 | # error: [invalid-super-argument]
|
||||
77 | # revealed: Unknown
|
||||
78 | reveal_type(super(object, z))
|
||||
80 | # error: [invalid-super-argument]
|
||||
81 | # revealed: Unknown
|
||||
82 | reveal_type(super(object, z))
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
79 |
|
||||
80 | is_list = g(x)
|
||||
83 |
|
||||
84 | is_list = g(x)
|
||||
|
|
||||
info: rule `invalid-super-argument` is enabled by default
|
||||
|
||||
|
|
@ -236,15 +240,15 @@ info: rule `invalid-super-argument` is enabled by default
|
|||
|
||||
```
|
||||
error[invalid-super-argument]: `types.GenericAlias` instance `list[int]` is not a valid class
|
||||
--> src/mdtest_snippet.py:94:13
|
||||
|
|
||||
92 | # error: [invalid-super-argument]
|
||||
93 | # revealed: Unknown
|
||||
94 | reveal_type(super(list[int], []))
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
95 | class Super:
|
||||
96 | def method(self) -> int:
|
||||
|
|
||||
--> src/mdtest_snippet.py:98:13
|
||||
|
|
||||
96 | # error: [invalid-super-argument]
|
||||
97 | # revealed: Unknown
|
||||
98 | reveal_type(super(list[int], []))
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
99 | class Super:
|
||||
100 | def method(self) -> int:
|
||||
|
|
||||
info: rule `invalid-super-argument` is enabled by default
|
||||
|
||||
```
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ use crate::types::generics::{
|
|||
use crate::types::infer::infer_unpack_types;
|
||||
use crate::types::mro::{Mro, MroError, MroIterator};
|
||||
pub(crate) use crate::types::narrow::infer_narrowing_constraint;
|
||||
use crate::types::newtype::NewType;
|
||||
use crate::types::signatures::{ParameterForm, walk_signature};
|
||||
use crate::types::tuple::{TupleSpec, TupleSpecBuilder};
|
||||
pub(crate) use crate::types::typed_dict::{TypedDictParams, TypedDictType, walk_typed_dict_type};
|
||||
|
|
@ -98,6 +99,7 @@ mod instance;
|
|||
mod member;
|
||||
mod mro;
|
||||
mod narrow;
|
||||
mod newtype;
|
||||
mod protocol_class;
|
||||
mod signatures;
|
||||
mod special_form;
|
||||
|
|
@ -783,6 +785,13 @@ pub enum Type<'db> {
|
|||
TypedDict(TypedDictType<'db>),
|
||||
/// An aliased type (lazily not-yet-unpacked to its value type).
|
||||
TypeAlias(TypeAliasType<'db>),
|
||||
/// The set of Python objects that belong to a `typing.NewType` subtype. Note that
|
||||
/// `typing.NewType` itself is a `Type::ClassLiteral` with `KnownClass::NewType`, and the
|
||||
/// identity callables it returns (which behave like subtypes in type expressions) are of
|
||||
/// `Type::KnownInstance` with `KnownInstanceType::NewType`. This `Type` refers to the objects
|
||||
/// wrapped/returned by a specific one of those identity callables, or by another that inherits
|
||||
/// from it.
|
||||
NewTypeInstance(NewType<'db>),
|
||||
}
|
||||
|
||||
#[salsa::tracked]
|
||||
|
|
@ -1420,6 +1429,13 @@ impl<'db> Type<'db> {
|
|||
self
|
||||
}
|
||||
Type::TypeAlias(alias) => alias.value_type(db).normalized_impl(db, visitor),
|
||||
Type::NewTypeInstance(newtype) => {
|
||||
visitor.visit(self, || {
|
||||
Type::NewTypeInstance(newtype.map_base_class_type(db, |class_type| {
|
||||
class_type.normalized_impl(db, visitor)
|
||||
}))
|
||||
})
|
||||
}
|
||||
Type::LiteralString
|
||||
| Type::AlwaysFalsy
|
||||
| Type::AlwaysTruthy
|
||||
|
|
@ -1482,7 +1498,8 @@ impl<'db> Type<'db> {
|
|||
| Type::BoundSuper(_)
|
||||
| Type::TypeIs(_)
|
||||
| Type::TypedDict(_)
|
||||
| Type::TypeAlias(_) => false,
|
||||
| Type::TypeAlias(_)
|
||||
| Type::NewTypeInstance(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1520,6 +1537,10 @@ impl<'db> Type<'db> {
|
|||
|
||||
Type::GenericAlias(alias) => Some(ClassType::Generic(alias).into_callable(db)),
|
||||
|
||||
Type::NewTypeInstance(newtype) => {
|
||||
Type::instance(db, newtype.base_class_type(db)).try_upcast_to_callable(db)
|
||||
}
|
||||
|
||||
// TODO: This is unsound so in future we can consider an opt-in option to disable it.
|
||||
Type::SubclassOf(subclass_of_ty) => match subclass_of_ty.subclass_of() {
|
||||
SubclassOfInner::Class(class) => Some(class.into_callable(db)),
|
||||
|
|
@ -1549,6 +1570,15 @@ impl<'db> Type<'db> {
|
|||
false,
|
||||
))),
|
||||
|
||||
Type::KnownInstance(KnownInstanceType::NewType(newtype)) => Some(CallableType::single(
|
||||
db,
|
||||
Signature::new(
|
||||
Parameters::new([Parameter::positional_only(None)
|
||||
.with_annotated_type(newtype.base(db).instance_type(db))]),
|
||||
Some(Type::NewTypeInstance(newtype)),
|
||||
),
|
||||
)),
|
||||
|
||||
Type::Never
|
||||
| Type::DataclassTransformer(_)
|
||||
| Type::AlwaysTruthy
|
||||
|
|
@ -2429,6 +2459,22 @@ impl<'db> Type<'db> {
|
|||
})
|
||||
}
|
||||
|
||||
(Type::NewTypeInstance(self_newtype), Type::NewTypeInstance(target_newtype)) => {
|
||||
self_newtype.has_relation_to_impl(db, target_newtype)
|
||||
}
|
||||
|
||||
(
|
||||
Type::NewTypeInstance(self_newtype),
|
||||
Type::NominalInstance(target_nominal_instance),
|
||||
) => self_newtype.base_class_type(db).has_relation_to_impl(
|
||||
db,
|
||||
target_nominal_instance.class(db),
|
||||
inferable,
|
||||
relation,
|
||||
relation_visitor,
|
||||
disjointness_visitor,
|
||||
),
|
||||
|
||||
(Type::PropertyInstance(_), _) => {
|
||||
KnownClass::Property.to_instance(db).has_relation_to_impl(
|
||||
db,
|
||||
|
|
@ -2448,14 +2494,15 @@ impl<'db> Type<'db> {
|
|||
disjointness_visitor,
|
||||
),
|
||||
|
||||
// Other than the special cases enumerated above, `Instance` types and typevars are
|
||||
// never subtypes of any other variants
|
||||
// Other than the special cases enumerated above, nominal-instance types,
|
||||
// newtype-instance types, and typevars are never subtypes of any other variants
|
||||
(Type::TypeVar(bound_typevar), _) => {
|
||||
// All inferable cases should have been handled above
|
||||
assert!(!bound_typevar.is_inferable(db, inferable));
|
||||
ConstraintSet::from(false)
|
||||
}
|
||||
(Type::NominalInstance(_), _) => ConstraintSet::from(false),
|
||||
(Type::NewTypeInstance(_), _) => ConstraintSet::from(false),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2529,6 +2576,10 @@ impl<'db> Type<'db> {
|
|||
})
|
||||
}
|
||||
|
||||
(Type::NewTypeInstance(self_newtype), Type::NewTypeInstance(other_newtype)) => {
|
||||
ConstraintSet::from(self_newtype.is_equivalent_to_impl(db, other_newtype))
|
||||
}
|
||||
|
||||
(Type::NominalInstance(first), Type::NominalInstance(second)) => {
|
||||
first.is_equivalent_to_impl(db, second, inferable, visitor)
|
||||
}
|
||||
|
|
@ -3288,6 +3339,19 @@ impl<'db> Type<'db> {
|
|||
)
|
||||
}),
|
||||
|
||||
(Type::NewTypeInstance(left), Type::NewTypeInstance(right)) => {
|
||||
left.is_disjoint_from_impl(db, right)
|
||||
}
|
||||
(Type::NewTypeInstance(newtype), other) | (other, Type::NewTypeInstance(newtype)) => {
|
||||
Type::instance(db, newtype.base_class_type(db)).is_disjoint_from_impl(
|
||||
db,
|
||||
other,
|
||||
inferable,
|
||||
disjointness_visitor,
|
||||
relation_visitor,
|
||||
)
|
||||
}
|
||||
|
||||
(Type::PropertyInstance(_), other) | (other, Type::PropertyInstance(_)) => {
|
||||
KnownClass::Property.to_instance(db).is_disjoint_from_impl(
|
||||
db,
|
||||
|
|
@ -3432,6 +3496,9 @@ impl<'db> Type<'db> {
|
|||
Type::TypeIs(type_is) => type_is.is_bound(db),
|
||||
Type::TypedDict(_) => false,
|
||||
Type::TypeAlias(alias) => alias.value_type(db).is_singleton(db),
|
||||
Type::NewTypeInstance(newtype) => {
|
||||
Type::instance(db, newtype.base_class_type(db)).is_singleton(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3482,6 +3549,9 @@ impl<'db> Type<'db> {
|
|||
}
|
||||
|
||||
Type::NominalInstance(instance) => instance.is_single_valued(db),
|
||||
Type::NewTypeInstance(newtype) => {
|
||||
Type::instance(db, newtype.base_class_type(db)).is_single_valued(db)
|
||||
}
|
||||
|
||||
Type::BoundSuper(_) => {
|
||||
// At runtime two super instances never compare equal, even if their arguments are identical.
|
||||
|
|
@ -3645,7 +3715,8 @@ impl<'db> Type<'db> {
|
|||
| Type::ProtocolInstance(_)
|
||||
| Type::PropertyInstance(_)
|
||||
| Type::TypeIs(_)
|
||||
| Type::TypedDict(_) => None,
|
||||
| Type::TypedDict(_)
|
||||
| Type::NewTypeInstance(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3732,6 +3803,7 @@ impl<'db> Type<'db> {
|
|||
Type::Dynamic(_) | Type::Never => Place::bound(self).into(),
|
||||
|
||||
Type::NominalInstance(instance) => instance.class(db).instance_member(db, name),
|
||||
Type::NewTypeInstance(newtype) => newtype.base_class_type(db).instance_member(db, name),
|
||||
|
||||
Type::ProtocolInstance(protocol) => protocol.instance_member(db, name),
|
||||
|
||||
|
|
@ -4404,6 +4476,7 @@ impl<'db> Type<'db> {
|
|||
|
||||
Type::NominalInstance(..)
|
||||
| Type::ProtocolInstance(..)
|
||||
| Type::NewTypeInstance(..)
|
||||
| Type::BooleanLiteral(..)
|
||||
| Type::IntLiteral(..)
|
||||
| Type::StringLiteral(..)
|
||||
|
|
@ -4842,6 +4915,8 @@ impl<'db> Type<'db> {
|
|||
.value_type(db)
|
||||
.try_bool_impl(db, allow_short_circuit, visitor)
|
||||
})?,
|
||||
Type::NewTypeInstance(newtype) => Type::instance(db, newtype.base_class_type(db))
|
||||
.try_bool_impl(db, allow_short_circuit, visitor)?,
|
||||
};
|
||||
|
||||
Ok(truthiness)
|
||||
|
|
@ -5528,7 +5603,7 @@ impl<'db> Type<'db> {
|
|||
SubclassOfInner::Class(class) => Type::from(class).bindings(db),
|
||||
},
|
||||
|
||||
Type::NominalInstance(_) | Type::ProtocolInstance(_) => {
|
||||
Type::NominalInstance(_) | Type::ProtocolInstance(_) | Type::NewTypeInstance(_) => {
|
||||
// Note that for objects that have a (possibly not callable!) `__call__` attribute,
|
||||
// we will get the signature of the `__call__` attribute, but will pass in the type
|
||||
// of the original object as the "callable type". That ensures that we get errors
|
||||
|
|
@ -5581,6 +5656,16 @@ impl<'db> Type<'db> {
|
|||
|
||||
Type::EnumLiteral(enum_literal) => enum_literal.enum_class_instance(db).bindings(db),
|
||||
|
||||
Type::KnownInstance(KnownInstanceType::NewType(newtype)) => Binding::single(
|
||||
self,
|
||||
Signature::new(
|
||||
Parameters::new([Parameter::positional_only(None)
|
||||
.with_annotated_type(newtype.base(db).instance_type(db))]),
|
||||
Some(Type::NewTypeInstance(newtype)),
|
||||
),
|
||||
)
|
||||
.into(),
|
||||
|
||||
Type::KnownInstance(known_instance) => {
|
||||
known_instance.instance_fallback(db).bindings(db)
|
||||
}
|
||||
|
|
@ -5716,6 +5801,7 @@ impl<'db> Type<'db> {
|
|||
|
||||
match ty {
|
||||
Type::NominalInstance(nominal) => nominal.tuple_spec(db),
|
||||
Type::NewTypeInstance(newtype) => non_async_special_case(db, Type::instance(db, newtype.base_class_type(db))),
|
||||
Type::GenericAlias(alias) if alias.origin(db).is_tuple(db) => {
|
||||
Some(Cow::Owned(TupleSpec::homogeneous(todo_type!(
|
||||
"*tuple[] annotations"
|
||||
|
|
@ -6346,6 +6432,9 @@ impl<'db> Type<'db> {
|
|||
Type::ClassLiteral(class) => Some(Type::instance(db, class.default_specialization(db))),
|
||||
Type::GenericAlias(alias) => Some(Type::instance(db, ClassType::from(alias))),
|
||||
Type::SubclassOf(subclass_of_ty) => Some(subclass_of_ty.to_instance(db)),
|
||||
Type::KnownInstance(KnownInstanceType::NewType(newtype)) => {
|
||||
Some(Type::NewTypeInstance(newtype))
|
||||
}
|
||||
Type::Union(union) => union.to_instance(db),
|
||||
// If there is no bound or constraints on a typevar `T`, `T: object` implicitly, which
|
||||
// has no instance type. Otherwise, synthesize a typevar with bound or constraints
|
||||
|
|
@ -6376,7 +6465,8 @@ impl<'db> Type<'db> {
|
|||
| Type::AlwaysTruthy
|
||||
| Type::AlwaysFalsy
|
||||
| Type::TypeIs(_)
|
||||
| Type::TypedDict(_) => None,
|
||||
| Type::TypedDict(_)
|
||||
| Type::NewTypeInstance(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -6455,6 +6545,7 @@ impl<'db> Type<'db> {
|
|||
|
||||
Type::KnownInstance(known_instance) => match known_instance {
|
||||
KnownInstanceType::TypeAliasType(alias) => Ok(Type::TypeAlias(*alias)),
|
||||
KnownInstanceType::NewType(newtype) => Ok(Type::NewTypeInstance(*newtype)),
|
||||
KnownInstanceType::TypeVar(typevar) => {
|
||||
let index = semantic_index(db, scope_id.file(db));
|
||||
Ok(bind_typevar(
|
||||
|
|
@ -6669,9 +6760,6 @@ impl<'db> Type<'db> {
|
|||
Some(KnownClass::TypeVarTuple) => Ok(todo_type!(
|
||||
"Support for `typing.TypeVarTuple` instances in type expressions"
|
||||
)),
|
||||
Some(KnownClass::NewType) => Ok(todo_type!(
|
||||
"Support for `typing.NewType` instances in type expressions"
|
||||
)),
|
||||
Some(KnownClass::GenericAlias) => Ok(todo_type!(
|
||||
"Support for `typing.GenericAlias` instances in type expressions"
|
||||
)),
|
||||
|
|
@ -6690,6 +6778,13 @@ impl<'db> Type<'db> {
|
|||
.value_type(db)
|
||||
.in_type_expression(db, scope_id, typevar_binding_context)
|
||||
}
|
||||
|
||||
Type::NewTypeInstance(_) => Err(InvalidTypeExpressionError {
|
||||
invalid_expressions: smallvec::smallvec_inline![
|
||||
InvalidTypeExpression::InvalidType(*self, scope_id)
|
||||
],
|
||||
fallback_type: Type::unknown(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -6764,6 +6859,7 @@ impl<'db> Type<'db> {
|
|||
// understand a more specific meta type in order to correctly handle `__getitem__`.
|
||||
Type::TypedDict(typed_dict) => SubclassOfType::from(db, typed_dict.defining_class()),
|
||||
Type::TypeAlias(alias) => alias.value_type(db).to_meta_type(db),
|
||||
Type::NewTypeInstance(newtype) => Type::from(newtype.base_class_type(db)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -6873,8 +6969,8 @@ impl<'db> Type<'db> {
|
|||
| TypeMapping::ReplaceParameterDefaults
|
||||
| TypeMapping::BindLegacyTypevars(_) => self,
|
||||
TypeMapping::Materialize(materialization_kind) => {
|
||||
Type::TypeVar(bound_typevar.materialize_impl(db, *materialization_kind, visitor))
|
||||
}
|
||||
Type::TypeVar(bound_typevar.materialize_impl(db, *materialization_kind, visitor))
|
||||
}
|
||||
}
|
||||
|
||||
Type::KnownInstance(KnownInstanceType::TypeVar(typevar)) => match type_mapping {
|
||||
|
|
@ -6909,6 +7005,12 @@ impl<'db> Type<'db> {
|
|||
instance.apply_type_mapping_impl(db, type_mapping, tcx, visitor)
|
||||
},
|
||||
|
||||
Type::NewTypeInstance(newtype) => visitor.visit(self, || {
|
||||
Type::NewTypeInstance(newtype.map_base_class_type(db, |class_type| {
|
||||
class_type.apply_type_mapping_impl(db, type_mapping, tcx, visitor)
|
||||
}))
|
||||
}),
|
||||
|
||||
Type::ProtocolInstance(instance) => {
|
||||
// TODO: Add tests for materialization once subtyping/assignability is implemented for
|
||||
// protocols. It _might_ require changing the logic here because:
|
||||
|
|
@ -7150,6 +7252,12 @@ impl<'db> Type<'db> {
|
|||
instance.find_legacy_typevars_impl(db, binding_context, typevars, visitor);
|
||||
}
|
||||
|
||||
Type::NewTypeInstance(_) => {
|
||||
// A newtype can never be constructed from an unspecialized generic class, so it is
|
||||
// impossible that we could ever find any legacy typevars in a newtype instance or
|
||||
// its underlying class.
|
||||
}
|
||||
|
||||
Type::SubclassOf(subclass_of) => {
|
||||
subclass_of.find_legacy_typevars_impl(db, binding_context, typevars, visitor);
|
||||
}
|
||||
|
|
@ -7305,6 +7413,7 @@ impl<'db> Type<'db> {
|
|||
},
|
||||
|
||||
Self::TypeAlias(alias) => alias.value_type(db).definition(db),
|
||||
Self::NewTypeInstance(newtype) => Some(TypeDefinition::NewType(newtype.definition(db))),
|
||||
|
||||
Self::StringLiteral(_)
|
||||
| Self::BooleanLiteral(_)
|
||||
|
|
@ -7528,7 +7637,8 @@ impl<'db> VarianceInferable<'db> for Type<'db> {
|
|||
| Type::BoundSuper(_)
|
||||
| Type::TypeVar(_)
|
||||
| Type::TypedDict(_)
|
||||
| Type::TypeAlias(_) => TypeVarVariance::Bivariant,
|
||||
| Type::TypeAlias(_)
|
||||
| Type::NewTypeInstance(_) => TypeVarVariance::Bivariant,
|
||||
};
|
||||
|
||||
tracing::trace!(
|
||||
|
|
@ -7726,6 +7836,10 @@ pub enum KnownInstanceType<'db> {
|
|||
|
||||
/// A single instance of `typing.Annotated`
|
||||
Annotated(InternedType<'db>),
|
||||
|
||||
/// An identity callable created with `typing.NewType(name, base)`, which behaves like a
|
||||
/// subtype of `base` in type expressions. See the `struct NewType` payload for an example.
|
||||
NewType(NewType<'db>),
|
||||
}
|
||||
|
||||
fn walk_known_instance_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
|
||||
|
|
@ -7760,6 +7874,11 @@ fn walk_known_instance_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
|
|||
KnownInstanceType::Literal(ty) | KnownInstanceType::Annotated(ty) => {
|
||||
visitor.visit_type(db, ty.inner(db));
|
||||
}
|
||||
KnownInstanceType::NewType(newtype) => {
|
||||
if let ClassType::Generic(generic_alias) = newtype.base_class_type(db) {
|
||||
visitor.visit_generic_alias_type(db, generic_alias);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -7799,6 +7918,10 @@ impl<'db> KnownInstanceType<'db> {
|
|||
Self::UnionType(list) => Self::UnionType(list.normalized_impl(db, visitor)),
|
||||
Self::Literal(ty) => Self::Literal(ty.normalized_impl(db, visitor)),
|
||||
Self::Annotated(ty) => Self::Annotated(ty.normalized_impl(db, visitor)),
|
||||
Self::NewType(newtype) => Self::NewType(
|
||||
newtype
|
||||
.map_base_class_type(db, |class_type| class_type.normalized_impl(db, visitor)),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -7819,6 +7942,7 @@ impl<'db> KnownInstanceType<'db> {
|
|||
Self::UnionType(_) => KnownClass::UnionType,
|
||||
Self::Literal(_) => KnownClass::GenericAlias,
|
||||
Self::Annotated(_) => KnownClass::GenericAlias,
|
||||
Self::NewType(_) => KnownClass::NewType,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -7903,6 +8027,9 @@ impl<'db> KnownInstanceType<'db> {
|
|||
KnownInstanceType::Annotated(_) => {
|
||||
f.write_str("<typing.Annotated special form>")
|
||||
}
|
||||
KnownInstanceType::NewType(declaration) => {
|
||||
write!(f, "<NewType pseudo-class '{}'>", declaration.name(self.db))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -404,6 +404,9 @@ impl<'db> BoundSuperType<'db> {
|
|||
.to_specialized_instance(db, [key_builder.build(), value_builder.build()]),
|
||||
);
|
||||
}
|
||||
Type::NewTypeInstance(newtype) => {
|
||||
return delegate_to(Type::instance(db, newtype.base_class_type(db)));
|
||||
}
|
||||
Type::Callable(callable) if callable.is_function_like(db) => {
|
||||
return delegate_to(KnownClass::FunctionType.to_instance(db));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -358,6 +358,14 @@ pub enum ClassType<'db> {
|
|||
|
||||
#[salsa::tracked]
|
||||
impl<'db> ClassType<'db> {
|
||||
/// Return a `ClassType` representing the class `builtins.object`
|
||||
pub(super) fn object(db: &'db dyn Db) -> Self {
|
||||
KnownClass::Object
|
||||
.to_class_literal(db)
|
||||
.to_class_type(db)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub(super) const fn is_generic(self) -> bool {
|
||||
matches!(self, Self::Generic(_))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,6 +137,12 @@ impl<'db> ClassBase<'db> {
|
|||
|
||||
Type::TypeAlias(alias) => Self::try_from_type(db, alias.value_type(db), subclass),
|
||||
|
||||
Type::NewTypeInstance(newtype) => ClassBase::try_from_type(
|
||||
db,
|
||||
Type::instance(db, newtype.base_class_type(db)),
|
||||
subclass,
|
||||
),
|
||||
|
||||
Type::PropertyInstance(_)
|
||||
| Type::BooleanLiteral(_)
|
||||
| Type::FunctionLiteral(_)
|
||||
|
|
@ -169,7 +175,11 @@ impl<'db> ClassBase<'db> {
|
|||
| KnownInstanceType::Field(_)
|
||||
| KnownInstanceType::ConstraintSet(_)
|
||||
| KnownInstanceType::UnionType(_)
|
||||
| KnownInstanceType::Literal(_) => None,
|
||||
| KnownInstanceType::Literal(_)
|
||||
// A class inheriting from a newtype would make intuitive sense, but newtype
|
||||
// wrappers are just identity callables at runtime, so this sort of inheritance
|
||||
// doesn't work and isn't allowed.
|
||||
| KnownInstanceType::NewType(_) => None,
|
||||
KnownInstanceType::Annotated(ty) => Self::try_from_type(db, ty.inner(db), subclass),
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ pub enum TypeDefinition<'db> {
|
|||
Function(Definition<'db>),
|
||||
TypeVar(Definition<'db>),
|
||||
TypeAlias(Definition<'db>),
|
||||
NewType(Definition<'db>),
|
||||
}
|
||||
|
||||
impl TypeDefinition<'_> {
|
||||
|
|
@ -21,7 +22,8 @@ impl TypeDefinition<'_> {
|
|||
Self::Class(definition)
|
||||
| Self::Function(definition)
|
||||
| Self::TypeVar(definition)
|
||||
| Self::TypeAlias(definition) => {
|
||||
| Self::TypeAlias(definition)
|
||||
| Self::NewType(definition) => {
|
||||
let module = parsed_module(db, definition.file(db)).load(db);
|
||||
Some(definition.focus_range(db, &module))
|
||||
}
|
||||
|
|
@ -38,7 +40,8 @@ impl TypeDefinition<'_> {
|
|||
Self::Class(definition)
|
||||
| Self::Function(definition)
|
||||
| Self::TypeVar(definition)
|
||||
| Self::TypeAlias(definition) => {
|
||||
| Self::TypeAlias(definition)
|
||||
| Self::NewType(definition) => {
|
||||
let module = parsed_module(db, definition.file(db)).load(db);
|
||||
Some(definition.full_range(db, &module))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use crate::semantic_index::definition::{Definition, DefinitionKind};
|
|||
use crate::semantic_index::place::{PlaceTable, ScopedPlaceId};
|
||||
use crate::semantic_index::{global_scope, place_table};
|
||||
use crate::suppression::FileSuppressionId;
|
||||
use crate::types::KnownInstanceType;
|
||||
use crate::types::call::CallError;
|
||||
use crate::types::class::{DisjointBase, DisjointBaseKind, Field};
|
||||
use crate::types::function::KnownFunction;
|
||||
|
|
@ -65,6 +66,7 @@ pub(crate) fn register_lints(registry: &mut LintRegistryBuilder) {
|
|||
registry.register_lint(&INVALID_LEGACY_TYPE_VARIABLE);
|
||||
registry.register_lint(&INVALID_PARAMSPEC);
|
||||
registry.register_lint(&INVALID_TYPE_ALIAS_TYPE);
|
||||
registry.register_lint(&INVALID_NEWTYPE);
|
||||
registry.register_lint(&INVALID_METACLASS);
|
||||
registry.register_lint(&INVALID_OVERLOAD);
|
||||
registry.register_lint(&USELESS_OVERLOAD_BODY);
|
||||
|
|
@ -926,6 +928,30 @@ declare_lint! {
|
|||
}
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// ## What it does
|
||||
/// Checks for the creation of invalid `NewType`s
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// There are several requirements that you must follow when creating a `NewType`.
|
||||
///
|
||||
/// ## Examples
|
||||
/// ```python
|
||||
/// from typing import NewType
|
||||
///
|
||||
/// def get_name() -> str: ...
|
||||
///
|
||||
/// Foo = NewType("Foo", int) # okay
|
||||
/// Bar = NewType(get_name(), int) # error: The first argument to `NewType` must be a string literal
|
||||
/// Baz = NewType("Baz", int | str) # error: invalid base for `typing.NewType`
|
||||
/// ```
|
||||
pub(crate) static INVALID_NEWTYPE = {
|
||||
summary: "detects invalid NewType definitions",
|
||||
status: LintStatus::preview("1.0.0"),
|
||||
default_level: Level::Error,
|
||||
}
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// ## What it does
|
||||
/// Checks for arguments to `metaclass=` that are invalid.
|
||||
|
|
@ -2898,6 +2924,24 @@ pub(crate) fn report_invalid_or_unsupported_base(
|
|||
return;
|
||||
}
|
||||
|
||||
if let Type::KnownInstance(KnownInstanceType::NewType(newtype)) = base_type {
|
||||
let Some(builder) = context.report_lint(&INVALID_BASE, base_node) else {
|
||||
return;
|
||||
};
|
||||
let mut diagnostic = builder.into_diagnostic("Cannot subclass an instance of NewType");
|
||||
diagnostic.info(format_args!(
|
||||
"Perhaps you were looking for: `{} = NewType('{}', {})`",
|
||||
class.name(context.db()),
|
||||
class.name(context.db()),
|
||||
newtype.name(context.db()),
|
||||
));
|
||||
diagnostic.info(format_args!(
|
||||
"Definition of class `{}` will raise `TypeError` at runtime",
|
||||
class.name(context.db())
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
let tuple_of_types = Type::homogeneous_tuple(db, instance_of_type);
|
||||
|
||||
let explain_mro_entries = |diagnostic: &mut LintDiagnosticGuard| {
|
||||
|
|
|
|||
|
|
@ -618,6 +618,7 @@ impl Display for DisplayRepresentation<'_> {
|
|||
.fmt(f),
|
||||
}
|
||||
}
|
||||
Type::NewTypeInstance(newtype) => f.write_str(newtype.name(self.db)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1101,6 +1101,11 @@ fn is_instance_truthiness<'db>(
|
|||
|
||||
Type::NominalInstance(..) => always_true_if(is_instance(&ty)),
|
||||
|
||||
Type::NewTypeInstance(newtype) => always_true_if(is_instance(&Type::instance(
|
||||
db,
|
||||
newtype.base_class_type(db),
|
||||
))),
|
||||
|
||||
Type::BooleanLiteral(..)
|
||||
| Type::BytesLiteral(..)
|
||||
| Type::IntLiteral(..)
|
||||
|
|
|
|||
|
|
@ -128,6 +128,10 @@ impl<'db> AllMembers<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
Type::NewTypeInstance(newtype) => {
|
||||
self.extend_with_type(db, Type::instance(db, newtype.base_class_type(db)));
|
||||
}
|
||||
|
||||
Type::ClassLiteral(class_literal) if class_literal.is_typed_dict(db) => {
|
||||
self.extend_with_type(db, KnownClass::TypedDictFallback.to_class_literal(db));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,8 +59,8 @@ use crate::types::diagnostic::{
|
|||
DIVISION_BY_ZERO, DUPLICATE_KW_ONLY, INCONSISTENT_MRO, INVALID_ARGUMENT_TYPE,
|
||||
INVALID_ASSIGNMENT, INVALID_ATTRIBUTE_ACCESS, INVALID_BASE, INVALID_DECLARATION,
|
||||
INVALID_GENERIC_CLASS, INVALID_KEY, INVALID_LEGACY_TYPE_VARIABLE, INVALID_METACLASS,
|
||||
INVALID_NAMED_TUPLE, INVALID_OVERLOAD, INVALID_PARAMETER_DEFAULT, INVALID_PARAMSPEC,
|
||||
INVALID_PROTOCOL, INVALID_TYPE_FORM, INVALID_TYPE_GUARD_CALL,
|
||||
INVALID_NAMED_TUPLE, INVALID_NEWTYPE, INVALID_OVERLOAD, INVALID_PARAMETER_DEFAULT,
|
||||
INVALID_PARAMSPEC, INVALID_PROTOCOL, INVALID_TYPE_FORM, INVALID_TYPE_GUARD_CALL,
|
||||
INVALID_TYPE_VARIABLE_CONSTRAINTS, IncompatibleBases, NON_SUBSCRIPTABLE,
|
||||
POSSIBLY_MISSING_IMPLICIT_CALL, POSSIBLY_MISSING_IMPORT, SUBCLASS_OF_FINAL_CLASS,
|
||||
UNDEFINED_REVEAL, UNRESOLVED_ATTRIBUTE, UNRESOLVED_GLOBAL, UNRESOLVED_IMPORT,
|
||||
|
|
@ -90,6 +90,7 @@ use crate::types::generics::{
|
|||
use crate::types::infer::nearest_enclosing_function;
|
||||
use crate::types::instance::SliceLiteral;
|
||||
use crate::types::mro::MroErrorKind;
|
||||
use crate::types::newtype::NewType;
|
||||
use crate::types::signatures::Signature;
|
||||
use crate::types::subclass_of::SubclassOfInner;
|
||||
use crate::types::tuple::{Tuple, TupleLength, TupleSpec, TupleType};
|
||||
|
|
@ -3884,7 +3885,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
| Type::AlwaysTruthy
|
||||
| Type::AlwaysFalsy
|
||||
| Type::TypeIs(_)
|
||||
| Type::TypedDict(_) => {
|
||||
| Type::TypedDict(_)
|
||||
| Type::NewTypeInstance(_) => {
|
||||
// TODO: We could use the annotated parameter type of `__setattr__` as type context here.
|
||||
// However, we would still have to perform the first inference without type context.
|
||||
let value_ty = infer_value_ty(self, TypeContext::default());
|
||||
|
|
@ -4454,6 +4456,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
Some(KnownClass::ParamSpec) => {
|
||||
self.infer_paramspec(target, call_expr, definition)
|
||||
}
|
||||
Some(KnownClass::NewType) => {
|
||||
self.infer_newtype_expression(target, call_expr, definition)
|
||||
}
|
||||
Some(_) | None => {
|
||||
self.infer_call_expression_impl(call_expr, callable_type, tcx)
|
||||
}
|
||||
|
|
@ -4892,14 +4897,114 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
)))
|
||||
}
|
||||
|
||||
fn infer_newtype_expression(
|
||||
&mut self,
|
||||
target: &ast::Expr,
|
||||
call_expr: &ast::ExprCall,
|
||||
definition: Definition<'db>,
|
||||
) -> Type<'db> {
|
||||
fn error<'db>(
|
||||
context: &InferContext<'db, '_>,
|
||||
message: impl std::fmt::Display,
|
||||
node: impl Ranged,
|
||||
) -> Type<'db> {
|
||||
if let Some(builder) = context.report_lint(&INVALID_NEWTYPE, node) {
|
||||
builder.into_diagnostic(message);
|
||||
}
|
||||
Type::unknown()
|
||||
}
|
||||
|
||||
let db = self.db();
|
||||
let arguments = &call_expr.arguments;
|
||||
|
||||
if !arguments.keywords.is_empty() {
|
||||
return error(
|
||||
&self.context,
|
||||
"Keyword arguments are not supported in `NewType` creation",
|
||||
call_expr,
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(starred) = arguments.args.iter().find(|arg| arg.is_starred_expr()) {
|
||||
return error(
|
||||
&self.context,
|
||||
"Starred arguments are not supported in `NewType` creation",
|
||||
starred,
|
||||
);
|
||||
}
|
||||
|
||||
if arguments.args.len() != 2 {
|
||||
return error(
|
||||
&self.context,
|
||||
format!(
|
||||
"Wrong number of arguments in `NewType` creation, expected 2, found {}",
|
||||
arguments.args.len()
|
||||
),
|
||||
call_expr,
|
||||
);
|
||||
}
|
||||
|
||||
let name_param_ty = self.infer_expression(&arguments.args[0], TypeContext::default());
|
||||
|
||||
let Some(name) = name_param_ty.as_string_literal().map(|name| name.value(db)) else {
|
||||
return error(
|
||||
&self.context,
|
||||
"The first argument to `NewType` must be a string literal",
|
||||
call_expr,
|
||||
);
|
||||
};
|
||||
|
||||
let ast::Expr::Name(ast::ExprName {
|
||||
id: target_name, ..
|
||||
}) = target
|
||||
else {
|
||||
return error(
|
||||
&self.context,
|
||||
"A `NewType` definition must be a simple variable assignment",
|
||||
target,
|
||||
);
|
||||
};
|
||||
|
||||
if name != target_name {
|
||||
return error(
|
||||
&self.context,
|
||||
format_args!(
|
||||
"The name of a `NewType` (`{name}`) must match \
|
||||
the name of the variable it is assigned to (`{target_name}`)"
|
||||
),
|
||||
target,
|
||||
);
|
||||
}
|
||||
|
||||
// Inference of `tp` must be deferred, to avoid cycles.
|
||||
self.deferred.insert(definition, self.multi_inference_state);
|
||||
|
||||
Type::KnownInstance(KnownInstanceType::NewType(NewType::new(
|
||||
db,
|
||||
ast::name::Name::from(name),
|
||||
definition,
|
||||
None,
|
||||
)))
|
||||
}
|
||||
|
||||
fn infer_assignment_deferred(&mut self, value: &ast::Expr) {
|
||||
// Infer deferred bounds/constraints/defaults of a legacy TypeVar / ParamSpec.
|
||||
// Infer deferred bounds/constraints/defaults of a legacy TypeVar / ParamSpec / NewType.
|
||||
let ast::Expr::Call(ast::ExprCall {
|
||||
func, arguments, ..
|
||||
}) = value
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let func_ty = self
|
||||
.try_expression_type(func)
|
||||
.unwrap_or_else(|| self.infer_expression(func, TypeContext::default()));
|
||||
let known_class = func_ty
|
||||
.as_class_literal()
|
||||
.and_then(|cls| cls.known(self.db()));
|
||||
if let Some(KnownClass::NewType) = known_class {
|
||||
self.infer_newtype_assignment_deferred(arguments);
|
||||
return;
|
||||
}
|
||||
for arg in arguments.args.iter().skip(1) {
|
||||
self.infer_type_expression(arg);
|
||||
}
|
||||
|
|
@ -4907,12 +5012,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
self.infer_type_expression(&bound.value);
|
||||
}
|
||||
if let Some(default) = arguments.find_keyword("default") {
|
||||
let func_ty = self
|
||||
.try_expression_type(func)
|
||||
.unwrap_or_else(|| self.infer_expression(func, TypeContext::default()));
|
||||
if func_ty.as_class_literal().is_some_and(|class_literal| {
|
||||
class_literal.is_known(self.db(), KnownClass::ParamSpec)
|
||||
}) {
|
||||
if let Some(KnownClass::ParamSpec) = known_class {
|
||||
self.infer_paramspec_default(&default.value);
|
||||
} else {
|
||||
self.infer_type_expression(&default.value);
|
||||
|
|
@ -4920,6 +5020,34 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
}
|
||||
}
|
||||
|
||||
// Infer the deferred base type of a NewType.
|
||||
fn infer_newtype_assignment_deferred(&mut self, arguments: &ast::Arguments) {
|
||||
match self.infer_type_expression(&arguments.args[1]) {
|
||||
Type::NominalInstance(_) | Type::NewTypeInstance(_) => {}
|
||||
// `Unknown` is likely to be the result of an unresolved import or a typo, which will
|
||||
// already get a diagnostic, so don't pile on an extra diagnostic here.
|
||||
Type::Dynamic(DynamicType::Unknown) => {}
|
||||
other_type => {
|
||||
if let Some(builder) = self
|
||||
.context
|
||||
.report_lint(&INVALID_NEWTYPE, &arguments.args[1])
|
||||
{
|
||||
let mut diag = builder.into_diagnostic("invalid base for `typing.NewType`");
|
||||
diag.set_primary_message(format!("type `{}`", other_type.display(self.db())));
|
||||
if matches!(other_type, Type::ProtocolInstance(_)) {
|
||||
diag.info("The base of a `NewType` is not allowed to be a protocol class.");
|
||||
} else if matches!(other_type, Type::TypedDict(_)) {
|
||||
diag.info("The base of a `NewType` is not allowed to be a `TypedDict`.");
|
||||
} else {
|
||||
diag.info(
|
||||
"The base of a `NewType` must be a class type or another `NewType`.",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn infer_annotated_assignment_statement(&mut self, assignment: &ast::StmtAnnAssign) {
|
||||
if assignment.target.is_name_expr() {
|
||||
self.infer_definition(assignment);
|
||||
|
|
@ -7483,11 +7611,11 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
.to_class_type(self.db())
|
||||
.is_none_or(|enum_class| !class.is_subclass_of(self.db(), enum_class))
|
||||
{
|
||||
// Inference of correctly-placed `TypeVar` and `ParamSpec` definitions is done in
|
||||
// `TypeInferenceBuilder::infer_legacy_typevar` and
|
||||
// `TypeInferenceBuilder::infer_paramspec`, and doesn't use the full
|
||||
// call-binding machinery. If we reach here, it means that someone is trying to
|
||||
// instantiate a `typing.TypeVar` and `typing.ParamSpec` in an invalid context.
|
||||
// Inference of correctly-placed `TypeVar`, `ParamSpec`, and `NewType` definitions
|
||||
// is done in `infer_legacy_typevar`, `infer_paramspec`, and
|
||||
// `infer_newtype_expression`, and doesn't use the full call-binding machinery. If
|
||||
// we reach here, it means that someone is trying to instantiate one of these in an
|
||||
// invalid context.
|
||||
match class.known(self.db()) {
|
||||
Some(KnownClass::TypeVar | KnownClass::ExtensionsTypeVar) => {
|
||||
if let Some(builder) = self
|
||||
|
|
@ -7509,6 +7637,15 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
);
|
||||
}
|
||||
}
|
||||
Some(KnownClass::NewType) => {
|
||||
if let Some(builder) =
|
||||
self.context.report_lint(&INVALID_NEWTYPE, call_expression)
|
||||
{
|
||||
builder.into_diagnostic(
|
||||
"A `NewType` definition must be a simple variable assignment",
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
|
@ -8577,7 +8714,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
| Type::BoundSuper(_)
|
||||
| Type::TypeVar(_)
|
||||
| Type::TypeIs(_)
|
||||
| Type::TypedDict(_),
|
||||
| Type::TypedDict(_)
|
||||
| Type::NewTypeInstance(_),
|
||||
) => {
|
||||
let unary_dunder_method = match op {
|
||||
ast::UnaryOp::Invert => "__invert__",
|
||||
|
|
@ -9025,7 +9163,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
| Type::BoundSuper(_)
|
||||
| Type::TypeVar(_)
|
||||
| Type::TypeIs(_)
|
||||
| Type::TypedDict(_),
|
||||
| Type::TypedDict(_)
|
||||
| Type::NewTypeInstance(_),
|
||||
Type::FunctionLiteral(_)
|
||||
| Type::BooleanLiteral(_)
|
||||
| Type::Callable(..)
|
||||
|
|
@ -9054,7 +9193,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
| Type::BoundSuper(_)
|
||||
| Type::TypeVar(_)
|
||||
| Type::TypeIs(_)
|
||||
| Type::TypedDict(_),
|
||||
| Type::TypedDict(_)
|
||||
| Type::NewTypeInstance(_),
|
||||
op,
|
||||
) => Type::try_call_bin_op(self.db(), left_ty, op, right_ty)
|
||||
.map(|outcome| outcome.return_type(self.db()))
|
||||
|
|
|
|||
|
|
@ -828,6 +828,16 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
|||
self.infer_type_expression(slice);
|
||||
todo_type!("Generic specialization of typing.Annotated")
|
||||
}
|
||||
KnownInstanceType::NewType(newtype) => {
|
||||
self.infer_type_expression(&subscript.slice);
|
||||
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) {
|
||||
builder.into_diagnostic(format_args!(
|
||||
"`{}` is a `NewType` and cannot be specialized",
|
||||
newtype.name(self.db())
|
||||
));
|
||||
}
|
||||
Type::unknown()
|
||||
}
|
||||
},
|
||||
Type::Dynamic(DynamicType::Todo(_)) => {
|
||||
self.infer_type_expression(slice);
|
||||
|
|
|
|||
|
|
@ -252,7 +252,8 @@ impl ClassInfoConstraintFunction {
|
|||
| Type::TypeIs(_)
|
||||
| Type::WrapperDescriptor(_)
|
||||
| Type::DataclassTransformer(_)
|
||||
| Type::TypedDict(_) => None,
|
||||
| Type::TypedDict(_)
|
||||
| Type::NewTypeInstance(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
266
crates/ty_python_semantic/src/types/newtype.rs
Normal file
266
crates/ty_python_semantic/src/types/newtype.rs
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
use std::collections::BTreeSet;
|
||||
|
||||
use crate::Db;
|
||||
use crate::semantic_index::definition::{Definition, DefinitionKind};
|
||||
use crate::types::constraints::ConstraintSet;
|
||||
use crate::types::{ClassType, Type, definition_expression_type, visitor};
|
||||
use ruff_db::parsed::parsed_module;
|
||||
use ruff_python_ast as ast;
|
||||
|
||||
/// A `typing.NewType` declaration, either from the perspective of the
|
||||
/// identity-callable-that-acts-like-a-subtype-in-type-expressions returned by the call to
|
||||
/// `typing.NewType(...)`, or from the perspective of instances of that subtype returned by the
|
||||
/// identity callable. For example:
|
||||
///
|
||||
/// ```py
|
||||
/// import typing
|
||||
/// Foo = typing.NewType("Foo", int)
|
||||
/// x = Foo(42)
|
||||
/// ```
|
||||
///
|
||||
/// The revealed types there are:
|
||||
/// - `typing.NewType`: `Type::ClassLiteral(ClassLiteral)` with `KnownClass::NewType`.
|
||||
/// - `Foo`: `Type::KnownInstance(KnownInstanceType::NewType(NewType { .. }))`
|
||||
/// - `x`: `Type::NewTypeInstance(NewType { .. })`
|
||||
///
|
||||
/// # Ordering
|
||||
/// Ordering is based on the newtype's salsa-assigned id and not on its values.
|
||||
/// The id may change between runs, or when the newtype was garbage collected and recreated.
|
||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||
#[derive(PartialOrd, Ord)]
|
||||
pub struct NewType<'db> {
|
||||
/// The name of this NewType (e.g. `"Foo"`)
|
||||
#[returns(ref)]
|
||||
pub name: ast::name::Name,
|
||||
|
||||
/// The binding where this NewType is first created.
|
||||
pub definition: Definition<'db>,
|
||||
|
||||
// The base type of this NewType, if it's eagerly specified. This is typically `None` when a
|
||||
// `NewType` is first encountered, because the base type is lazy/deferred to avoid panics in
|
||||
// the recursive case. This becomes `Some` when a `NewType` is modified by methods like
|
||||
// `.normalize()`. Callers should use the `base` method instead of accessing this field
|
||||
// directly.
|
||||
eager_base: Option<NewTypeBase<'db>>,
|
||||
}
|
||||
|
||||
impl get_size2::GetSize for NewType<'_> {}
|
||||
|
||||
#[salsa::tracked]
|
||||
impl<'db> NewType<'db> {
|
||||
pub fn base(self, db: &'db dyn Db) -> NewTypeBase<'db> {
|
||||
match self.eager_base(db) {
|
||||
Some(base) => base,
|
||||
None => self.lazy_base(db),
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::tracked(
|
||||
cycle_initial=lazy_base_cycle_initial,
|
||||
heap_size=ruff_memory_usage::heap_size
|
||||
)]
|
||||
fn lazy_base(self, db: &'db dyn Db) -> NewTypeBase<'db> {
|
||||
// `TypeInferenceBuilder` emits diagnostics for invalid `NewType` definitions that show up
|
||||
// in assignments, but invalid definitions still get here, and also `NewType` might show up
|
||||
// in places that aren't definitions at all. Fall back to `object` in all error cases.
|
||||
let object_fallback = NewTypeBase::ClassType(ClassType::object(db));
|
||||
let definition = self.definition(db);
|
||||
let module = parsed_module(db, definition.file(db)).load(db);
|
||||
let DefinitionKind::Assignment(assignment) = definition.kind(db) else {
|
||||
return object_fallback;
|
||||
};
|
||||
let Some(call_expr) = assignment.value(&module).as_call_expr() else {
|
||||
return object_fallback;
|
||||
};
|
||||
let Some(second_arg) = call_expr.arguments.args.get(1) else {
|
||||
return object_fallback;
|
||||
};
|
||||
match definition_expression_type(db, definition, second_arg) {
|
||||
Type::NominalInstance(nominal_instance_type) => {
|
||||
NewTypeBase::ClassType(nominal_instance_type.class(db))
|
||||
}
|
||||
Type::NewTypeInstance(newtype) => NewTypeBase::NewType(newtype),
|
||||
// This branch includes bases that are other typing constructs besides classes and
|
||||
// other newtypes, for example unions. `NewType("Foo", int | str)` is not allowed.
|
||||
_ => object_fallback,
|
||||
}
|
||||
}
|
||||
|
||||
fn iter_bases(self, db: &'db dyn Db) -> NewTypeBaseIter<'db> {
|
||||
NewTypeBaseIter {
|
||||
current: Some(self),
|
||||
seen_before: BTreeSet::new(),
|
||||
db,
|
||||
}
|
||||
}
|
||||
|
||||
// Walk the `NewTypeBase` chain to find the underlying `ClassType`. There might not be a
|
||||
// `ClassType` if this `NewType` is cyclical, and we fall back to `object` in that case.
|
||||
pub fn base_class_type(self, db: &'db dyn Db) -> ClassType<'db> {
|
||||
for base in self.iter_bases(db) {
|
||||
if let NewTypeBase::ClassType(class_type) = base {
|
||||
return class_type;
|
||||
}
|
||||
}
|
||||
ClassType::object(db)
|
||||
}
|
||||
|
||||
pub(crate) fn is_equivalent_to_impl(self, db: &'db dyn Db, other: Self) -> bool {
|
||||
// Two instances of the "same" `NewType` won't compare == if one of them has an eagerly
|
||||
// evaluated base (or a normalized base, etc.) and the other doesn't, so we only check for
|
||||
// equality of the `definition`.
|
||||
self.definition(db) == other.definition(db)
|
||||
}
|
||||
|
||||
// Since a regular class can't inherit from a newtype, the only way for one newtype to be a
|
||||
// subtype of another is to have the other in its chain of newtype bases. Once we reach the
|
||||
// base class, we don't have to keep looking.
|
||||
pub(crate) fn has_relation_to_impl(self, db: &'db dyn Db, other: Self) -> ConstraintSet<'db> {
|
||||
if self.is_equivalent_to_impl(db, other) {
|
||||
return ConstraintSet::from(true);
|
||||
}
|
||||
for base in self.iter_bases(db) {
|
||||
if let NewTypeBase::NewType(base_newtype) = base {
|
||||
if base_newtype.is_equivalent_to_impl(db, other) {
|
||||
return ConstraintSet::from(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
ConstraintSet::from(false)
|
||||
}
|
||||
|
||||
pub(crate) fn is_disjoint_from_impl(self, db: &'db dyn Db, other: Self) -> ConstraintSet<'db> {
|
||||
// Two NewTypes are disjoint if they're not equal and neither inherits from the other.
|
||||
// NewTypes have single inheritance, and a regular class can't inherit from a NewType, so
|
||||
// it's not possible for some third type to multiply-inherit from both.
|
||||
let mut self_not_subtype_of_other = self.has_relation_to_impl(db, other).negate(db);
|
||||
let other_not_subtype_of_self = other.has_relation_to_impl(db, self).negate(db);
|
||||
self_not_subtype_of_other.intersect(db, other_not_subtype_of_self)
|
||||
}
|
||||
|
||||
/// Create a new `NewType` by mapping the underlying `ClassType`. This descends through any
|
||||
/// number of nested `NewType` layers and rebuilds the whole chain. In the rare case of cyclic
|
||||
/// `NewType`s with no underlying `ClassType`, this has no effect and does not call `f`.
|
||||
pub(crate) fn map_base_class_type(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
f: impl FnOnce(ClassType<'db>) -> ClassType<'db>,
|
||||
) -> Self {
|
||||
// Modifying the base class type requires unwrapping and re-wrapping however many base
|
||||
// newtypes there are between here and there. Normally recursion would be natural for this,
|
||||
// but the bases iterator does cycle detection, and I think using that with a stack is a
|
||||
// little cleaner than conjuring up yet another `CycleDetector` visitor and yet another
|
||||
// layer of "*_impl" nesting. Also if there is no base class type, returning `self`
|
||||
// unmodified seems more correct than injecting some default type like `object` into the
|
||||
// cycle, which is what `CycleDetector` would do if we used it here.
|
||||
let mut inner_newtype_stack = Vec::new();
|
||||
for base in self.iter_bases(db) {
|
||||
match base {
|
||||
// Build up the stack of intermediate newtypes that we'll need to re-wrap after
|
||||
// we've mapped the `ClassType`.
|
||||
NewTypeBase::NewType(base_newtype) => inner_newtype_stack.push(base_newtype),
|
||||
// We've reached the `ClassType`.
|
||||
NewTypeBase::ClassType(base_class_type) => {
|
||||
// Call `f`.
|
||||
let mut mapped_base = NewTypeBase::ClassType(f(base_class_type));
|
||||
// Re-wrap the mapped base class in however many newtypes we unwrapped.
|
||||
for inner_newtype in inner_newtype_stack.into_iter().rev() {
|
||||
mapped_base = NewTypeBase::NewType(NewType::new(
|
||||
db,
|
||||
inner_newtype.name(db).clone(),
|
||||
inner_newtype.definition(db),
|
||||
Some(mapped_base),
|
||||
));
|
||||
}
|
||||
return NewType::new(
|
||||
db,
|
||||
self.name(db).clone(),
|
||||
self.definition(db),
|
||||
Some(mapped_base),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If we get here, there is no `ClassType` (because this newtype is cyclic), and we don't
|
||||
// call `f` at all.
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn walk_newtype_instance_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
|
||||
db: &'db dyn Db,
|
||||
newtype: NewType<'db>,
|
||||
visitor: &V,
|
||||
) {
|
||||
visitor.visit_type(db, newtype.base(db).instance_type(db));
|
||||
}
|
||||
|
||||
/// `typing.NewType` typically wraps a class type, but it can also wrap another newtype.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, get_size2::GetSize, salsa::Update)]
|
||||
pub enum NewTypeBase<'db> {
|
||||
ClassType(ClassType<'db>),
|
||||
NewType(NewType<'db>),
|
||||
}
|
||||
|
||||
impl<'db> NewTypeBase<'db> {
|
||||
pub fn instance_type(self, db: &'db dyn Db) -> Type<'db> {
|
||||
match self {
|
||||
NewTypeBase::ClassType(class_type) => Type::instance(db, class_type),
|
||||
NewTypeBase::NewType(newtype) => Type::NewTypeInstance(newtype),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over the transitive bases of a `NewType`. In the most common case, e.g.
|
||||
/// `Foo = NewType("Foo", int)`, this yields the one `NewTypeBase::ClassType` (e.g. `int`). For
|
||||
/// newtypes that wrap other newtypes, this iterator yields the `NewTypeBase::NewType`s (not
|
||||
/// including `self`) before finally yielding the `NewTypeBase::ClassType`. In the pathological
|
||||
/// case of cyclic newtypes like `Foo = NewType("Foo", "Foo")`, this iterator yields the unique
|
||||
/// `NewTypeBase::NewType`s (not including `self`), detects the cycle, and then stops.
|
||||
///
|
||||
/// Note that this does *not* detect indirect cycles that go through a proper class, like this:
|
||||
/// ```py
|
||||
/// Foo = NewType("Foo", list["Foo"])
|
||||
/// ```
|
||||
/// As far as this iterator is concerned, that's the "common case", and it yields the one
|
||||
/// `NewTypeBase::ClassType` for `list[Foo]`. Functions like `normalize` that continue recursing
|
||||
/// over the base class need to pass down a cycle-detecting visitor as usual.
|
||||
struct NewTypeBaseIter<'db> {
|
||||
current: Option<NewType<'db>>,
|
||||
seen_before: BTreeSet<NewType<'db>>,
|
||||
db: &'db dyn Db,
|
||||
}
|
||||
|
||||
impl<'db> Iterator for NewTypeBaseIter<'db> {
|
||||
type Item = NewTypeBase<'db>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let current = self.current?;
|
||||
match current.base(self.db) {
|
||||
NewTypeBase::ClassType(base_class_type) => {
|
||||
self.current = None;
|
||||
Some(NewTypeBase::ClassType(base_class_type))
|
||||
}
|
||||
NewTypeBase::NewType(base_newtype) => {
|
||||
// Doing the insertion only in this branch avoids allocating in the common case.
|
||||
self.seen_before.insert(current);
|
||||
if self.seen_before.contains(&base_newtype) {
|
||||
// Cycle detected. Stop iterating.
|
||||
self.current = None;
|
||||
None
|
||||
} else {
|
||||
self.current = Some(base_newtype);
|
||||
Some(NewTypeBase::NewType(base_newtype))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lazy_base_cycle_initial<'db>(
|
||||
db: &'db dyn Db,
|
||||
_id: salsa::Id,
|
||||
_self: NewType<'db>,
|
||||
) -> NewTypeBase<'db> {
|
||||
NewTypeBase::ClassType(ClassType::object(db))
|
||||
}
|
||||
|
|
@ -213,6 +213,10 @@ pub(super) fn union_or_intersection_elements_ordering<'db>(
|
|||
(Type::TypedDict(_), _) => Ordering::Less,
|
||||
(_, Type::TypedDict(_)) => Ordering::Greater,
|
||||
|
||||
(Type::NewTypeInstance(left), Type::NewTypeInstance(right)) => left.cmp(right),
|
||||
(Type::NewTypeInstance(_), _) => Ordering::Less,
|
||||
(_, Type::NewTypeInstance(_)) => Ordering::Greater,
|
||||
|
||||
(Type::Union(_), _) | (_, Type::Union(_)) => {
|
||||
unreachable!("our type representation does not permit nested unions");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use crate::{
|
|||
class::walk_generic_alias,
|
||||
function::{FunctionType, walk_function_type},
|
||||
instance::{walk_nominal_instance_type, walk_protocol_instance_type},
|
||||
newtype::{NewType, walk_newtype_instance_type},
|
||||
subclass_of::walk_subclass_of_type,
|
||||
walk_bound_method_type, walk_bound_type_var_type, walk_callable_type,
|
||||
walk_intersection_type, walk_known_instance_type, walk_method_wrapper_type,
|
||||
|
|
@ -109,6 +110,10 @@ pub(crate) trait TypeVisitor<'db> {
|
|||
fn visit_typed_dict_type(&self, db: &'db dyn Db, typed_dict: TypedDictType<'db>) {
|
||||
walk_typed_dict_type(db, typed_dict, self);
|
||||
}
|
||||
|
||||
fn visit_newtype_instance_type(&self, db: &'db dyn Db, newtype: NewType<'db>) {
|
||||
walk_newtype_instance_type(db, newtype, self);
|
||||
}
|
||||
}
|
||||
|
||||
/// Enumeration of types that may contain other types, such as unions, intersections, and generics.
|
||||
|
|
@ -131,6 +136,7 @@ pub(super) enum NonAtomicType<'db> {
|
|||
ProtocolInstance(ProtocolInstanceType<'db>),
|
||||
TypedDict(TypedDictType<'db>),
|
||||
TypeAlias(TypeAliasType<'db>),
|
||||
NewTypeInstance(NewType<'db>),
|
||||
}
|
||||
|
||||
pub(super) enum TypeKind<'db> {
|
||||
|
|
@ -198,6 +204,9 @@ impl<'db> From<Type<'db>> for TypeKind<'db> {
|
|||
TypeKind::NonAtomic(NonAtomicType::TypedDict(typed_dict))
|
||||
}
|
||||
Type::TypeAlias(alias) => TypeKind::NonAtomic(NonAtomicType::TypeAlias(alias)),
|
||||
Type::NewTypeInstance(newtype) => {
|
||||
TypeKind::NonAtomic(NonAtomicType::NewTypeInstance(newtype))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -239,6 +248,9 @@ pub(super) fn walk_non_atomic_type<'db, V: TypeVisitor<'db> + ?Sized>(
|
|||
NonAtomicType::TypeAlias(alias) => {
|
||||
visitor.visit_type_alias_type(db, alias);
|
||||
}
|
||||
NonAtomicType::NewTypeInstance(newtype) => {
|
||||
visitor.visit_newtype_instance_type(db, newtype);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ Settings: Settings {
|
|||
"invalid-legacy-type-variable": Error (Default),
|
||||
"invalid-metaclass": Error (Default),
|
||||
"invalid-named-tuple": Error (Default),
|
||||
"invalid-newtype": Error (Default),
|
||||
"invalid-overload": Error (Default),
|
||||
"invalid-parameter-default": Error (Default),
|
||||
"invalid-paramspec": Error (Default),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue