mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 13:51:16 +00:00
[ty] Add functions for revealing assignability/subtyping constraints (#20217)
This PR adds two new `ty_extensions` functions, `reveal_when_assignable_to` and `reveal_when_subtype_of`. These are closely related to the existing `is_assignable_to` and `is_subtype_of`, but instead of returning when the property (always) holds, it produces a diagnostic that describes _when_ the property holds. (This will let us construct mdtests that print out constraints that are not always true or always false — though we don't currently have any instances of those.) I did not replace _every_ occurrence of the `is_property` variants in the mdtest suite, instead focusing on the generics-related tests where it will be important to see the full detail of the constraint sets. As part of this, I also updated the mdtest harness to accept the shorter `# revealed:` assertion format for more than just `reveal_type`, and updated the existing uses of `reveal_protocol_interface` to take advantage of this.
This commit is contained in:
parent
200349c6e8
commit
77b2cee223
7 changed files with 309 additions and 199 deletions
|
@ -129,7 +129,7 @@ specialization. Thus, the typevar is a subtype of itself and of `object`, but no
|
||||||
(including other typevars).
|
(including other typevars).
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from ty_extensions import is_assignable_to, is_subtype_of, static_assert
|
from ty_extensions import reveal_when_assignable_to, reveal_when_subtype_of
|
||||||
|
|
||||||
class Super: ...
|
class Super: ...
|
||||||
class Base(Super): ...
|
class Base(Super): ...
|
||||||
|
@ -137,23 +137,23 @@ class Sub(Base): ...
|
||||||
class Unrelated: ...
|
class Unrelated: ...
|
||||||
|
|
||||||
def unbounded_unconstrained[T, U](t: T, u: U) -> None:
|
def unbounded_unconstrained[T, U](t: T, u: U) -> None:
|
||||||
static_assert(is_assignable_to(T, T))
|
reveal_when_assignable_to(T, T) # revealed: always
|
||||||
static_assert(is_assignable_to(T, object))
|
reveal_when_assignable_to(T, object) # revealed: always
|
||||||
static_assert(not is_assignable_to(T, Super))
|
reveal_when_assignable_to(T, Super) # revealed: never
|
||||||
static_assert(is_assignable_to(U, U))
|
reveal_when_assignable_to(U, U) # revealed: always
|
||||||
static_assert(is_assignable_to(U, object))
|
reveal_when_assignable_to(U, object) # revealed: always
|
||||||
static_assert(not is_assignable_to(U, Super))
|
reveal_when_assignable_to(U, Super) # revealed: never
|
||||||
static_assert(not is_assignable_to(T, U))
|
reveal_when_assignable_to(T, U) # revealed: never
|
||||||
static_assert(not is_assignable_to(U, T))
|
reveal_when_assignable_to(U, T) # revealed: never
|
||||||
|
|
||||||
static_assert(is_subtype_of(T, T))
|
reveal_when_subtype_of(T, T) # revealed: always
|
||||||
static_assert(is_subtype_of(T, object))
|
reveal_when_subtype_of(T, object) # revealed: always
|
||||||
static_assert(not is_subtype_of(T, Super))
|
reveal_when_subtype_of(T, Super) # revealed: never
|
||||||
static_assert(is_subtype_of(U, U))
|
reveal_when_subtype_of(U, U) # revealed: always
|
||||||
static_assert(is_subtype_of(U, object))
|
reveal_when_subtype_of(U, object) # revealed: always
|
||||||
static_assert(not is_subtype_of(U, Super))
|
reveal_when_subtype_of(U, Super) # revealed: never
|
||||||
static_assert(not is_subtype_of(T, U))
|
reveal_when_subtype_of(T, U) # revealed: never
|
||||||
static_assert(not is_subtype_of(U, T))
|
reveal_when_subtype_of(U, T) # revealed: never
|
||||||
```
|
```
|
||||||
|
|
||||||
A bounded typevar is assignable to its bound, and a bounded, fully static typevar is a subtype of
|
A bounded typevar is assignable to its bound, and a bounded, fully static typevar is a subtype of
|
||||||
|
@ -167,40 +167,40 @@ from typing import Any
|
||||||
from typing_extensions import final
|
from typing_extensions import final
|
||||||
|
|
||||||
def bounded[T: Super](t: T) -> None:
|
def bounded[T: Super](t: T) -> None:
|
||||||
static_assert(is_assignable_to(T, Super))
|
reveal_when_assignable_to(T, Super) # revealed: always
|
||||||
static_assert(not is_assignable_to(T, Sub))
|
reveal_when_assignable_to(T, Sub) # revealed: never
|
||||||
static_assert(not is_assignable_to(Super, T))
|
reveal_when_assignable_to(Super, T) # revealed: never
|
||||||
static_assert(not is_assignable_to(Sub, T))
|
reveal_when_assignable_to(Sub, T) # revealed: never
|
||||||
|
|
||||||
static_assert(is_subtype_of(T, Super))
|
reveal_when_subtype_of(T, Super) # revealed: always
|
||||||
static_assert(not is_subtype_of(T, Sub))
|
reveal_when_subtype_of(T, Sub) # revealed: never
|
||||||
static_assert(not is_subtype_of(Super, T))
|
reveal_when_subtype_of(Super, T) # revealed: never
|
||||||
static_assert(not is_subtype_of(Sub, T))
|
reveal_when_subtype_of(Sub, T) # revealed: never
|
||||||
|
|
||||||
def bounded_by_gradual[T: Any](t: T) -> None:
|
def bounded_by_gradual[T: Any](t: T) -> None:
|
||||||
static_assert(is_assignable_to(T, Any))
|
reveal_when_assignable_to(T, Any) # revealed: always
|
||||||
static_assert(is_assignable_to(Any, T))
|
reveal_when_assignable_to(Any, T) # revealed: always
|
||||||
static_assert(is_assignable_to(T, Super))
|
reveal_when_assignable_to(T, Super) # revealed: always
|
||||||
static_assert(not is_assignable_to(Super, T))
|
reveal_when_assignable_to(Super, T) # revealed: never
|
||||||
static_assert(is_assignable_to(T, Sub))
|
reveal_when_assignable_to(T, Sub) # revealed: always
|
||||||
static_assert(not is_assignable_to(Sub, T))
|
reveal_when_assignable_to(Sub, T) # revealed: never
|
||||||
|
|
||||||
static_assert(not is_subtype_of(T, Any))
|
reveal_when_subtype_of(T, Any) # revealed: never
|
||||||
static_assert(not is_subtype_of(Any, T))
|
reveal_when_subtype_of(Any, T) # revealed: never
|
||||||
static_assert(not is_subtype_of(T, Super))
|
reveal_when_subtype_of(T, Super) # revealed: never
|
||||||
static_assert(not is_subtype_of(Super, T))
|
reveal_when_subtype_of(Super, T) # revealed: never
|
||||||
static_assert(not is_subtype_of(T, Sub))
|
reveal_when_subtype_of(T, Sub) # revealed: never
|
||||||
static_assert(not is_subtype_of(Sub, T))
|
reveal_when_subtype_of(Sub, T) # revealed: never
|
||||||
|
|
||||||
@final
|
@final
|
||||||
class FinalClass: ...
|
class FinalClass: ...
|
||||||
|
|
||||||
def bounded_final[T: FinalClass](t: T) -> None:
|
def bounded_final[T: FinalClass](t: T) -> None:
|
||||||
static_assert(is_assignable_to(T, FinalClass))
|
reveal_when_assignable_to(T, FinalClass) # revealed: always
|
||||||
static_assert(not is_assignable_to(FinalClass, T))
|
reveal_when_assignable_to(FinalClass, T) # revealed: never
|
||||||
|
|
||||||
static_assert(is_subtype_of(T, FinalClass))
|
reveal_when_subtype_of(T, FinalClass) # revealed: always
|
||||||
static_assert(not is_subtype_of(FinalClass, T))
|
reveal_when_subtype_of(FinalClass, T) # revealed: never
|
||||||
```
|
```
|
||||||
|
|
||||||
Two distinct fully static typevars are not subtypes of each other, even if they have the same
|
Two distinct fully static typevars are not subtypes of each other, even if they have the same
|
||||||
|
@ -210,18 +210,18 @@ typevars to `Never` in addition to that final class.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
def two_bounded[T: Super, U: Super](t: T, u: U) -> None:
|
def two_bounded[T: Super, U: Super](t: T, u: U) -> None:
|
||||||
static_assert(not is_assignable_to(T, U))
|
reveal_when_assignable_to(T, U) # revealed: never
|
||||||
static_assert(not is_assignable_to(U, T))
|
reveal_when_assignable_to(U, T) # revealed: never
|
||||||
|
|
||||||
static_assert(not is_subtype_of(T, U))
|
reveal_when_subtype_of(T, U) # revealed: never
|
||||||
static_assert(not is_subtype_of(U, T))
|
reveal_when_subtype_of(U, T) # revealed: never
|
||||||
|
|
||||||
def two_final_bounded[T: FinalClass, U: FinalClass](t: T, u: U) -> None:
|
def two_final_bounded[T: FinalClass, U: FinalClass](t: T, u: U) -> None:
|
||||||
static_assert(not is_assignable_to(T, U))
|
reveal_when_assignable_to(T, U) # revealed: never
|
||||||
static_assert(not is_assignable_to(U, T))
|
reveal_when_assignable_to(U, T) # revealed: never
|
||||||
|
|
||||||
static_assert(not is_subtype_of(T, U))
|
reveal_when_subtype_of(T, U) # revealed: never
|
||||||
static_assert(not is_subtype_of(U, T))
|
reveal_when_subtype_of(U, T) # revealed: never
|
||||||
```
|
```
|
||||||
|
|
||||||
A constrained fully static typevar is assignable to the union of its constraints, but not to any of
|
A constrained fully static typevar is assignable to the union of its constraints, but not to any of
|
||||||
|
@ -232,64 +232,64 @@ intersection of all of its constraints is a subtype of the typevar.
|
||||||
from ty_extensions import Intersection
|
from ty_extensions import Intersection
|
||||||
|
|
||||||
def constrained[T: (Base, Unrelated)](t: T) -> None:
|
def constrained[T: (Base, Unrelated)](t: T) -> None:
|
||||||
static_assert(not is_assignable_to(T, Super))
|
reveal_when_assignable_to(T, Super) # revealed: never
|
||||||
static_assert(not is_assignable_to(T, Base))
|
reveal_when_assignable_to(T, Base) # revealed: never
|
||||||
static_assert(not is_assignable_to(T, Sub))
|
reveal_when_assignable_to(T, Sub) # revealed: never
|
||||||
static_assert(not is_assignable_to(T, Unrelated))
|
reveal_when_assignable_to(T, Unrelated) # revealed: never
|
||||||
static_assert(is_assignable_to(T, Super | Unrelated))
|
reveal_when_assignable_to(T, Super | Unrelated) # revealed: always
|
||||||
static_assert(is_assignable_to(T, Base | Unrelated))
|
reveal_when_assignable_to(T, Base | Unrelated) # revealed: always
|
||||||
static_assert(not is_assignable_to(T, Sub | Unrelated))
|
reveal_when_assignable_to(T, Sub | Unrelated) # revealed: never
|
||||||
static_assert(not is_assignable_to(Super, T))
|
reveal_when_assignable_to(Super, T) # revealed: never
|
||||||
static_assert(not is_assignable_to(Unrelated, T))
|
reveal_when_assignable_to(Unrelated, T) # revealed: never
|
||||||
static_assert(not is_assignable_to(Super | Unrelated, T))
|
reveal_when_assignable_to(Super | Unrelated, T) # revealed: never
|
||||||
static_assert(is_assignable_to(Intersection[Base, Unrelated], T))
|
reveal_when_assignable_to(Intersection[Base, Unrelated], T) # revealed: always
|
||||||
|
|
||||||
static_assert(not is_subtype_of(T, Super))
|
reveal_when_subtype_of(T, Super) # revealed: never
|
||||||
static_assert(not is_subtype_of(T, Base))
|
reveal_when_subtype_of(T, Base) # revealed: never
|
||||||
static_assert(not is_subtype_of(T, Sub))
|
reveal_when_subtype_of(T, Sub) # revealed: never
|
||||||
static_assert(not is_subtype_of(T, Unrelated))
|
reveal_when_subtype_of(T, Unrelated) # revealed: never
|
||||||
static_assert(is_subtype_of(T, Super | Unrelated))
|
reveal_when_subtype_of(T, Super | Unrelated) # revealed: always
|
||||||
static_assert(is_subtype_of(T, Base | Unrelated))
|
reveal_when_subtype_of(T, Base | Unrelated) # revealed: always
|
||||||
static_assert(not is_subtype_of(T, Sub | Unrelated))
|
reveal_when_subtype_of(T, Sub | Unrelated) # revealed: never
|
||||||
static_assert(not is_subtype_of(Super, T))
|
reveal_when_subtype_of(Super, T) # revealed: never
|
||||||
static_assert(not is_subtype_of(Unrelated, T))
|
reveal_when_subtype_of(Unrelated, T) # revealed: never
|
||||||
static_assert(not is_subtype_of(Super | Unrelated, T))
|
reveal_when_subtype_of(Super | Unrelated, T) # revealed: never
|
||||||
static_assert(is_subtype_of(Intersection[Base, Unrelated], T))
|
reveal_when_subtype_of(Intersection[Base, Unrelated], T) # revealed: always
|
||||||
|
|
||||||
def constrained_by_gradual[T: (Base, Any)](t: T) -> None:
|
def constrained_by_gradual[T: (Base, Any)](t: T) -> None:
|
||||||
static_assert(is_assignable_to(T, Super))
|
reveal_when_assignable_to(T, Super) # revealed: always
|
||||||
static_assert(is_assignable_to(T, Base))
|
reveal_when_assignable_to(T, Base) # revealed: always
|
||||||
static_assert(not is_assignable_to(T, Sub))
|
reveal_when_assignable_to(T, Sub) # revealed: never
|
||||||
static_assert(not is_assignable_to(T, Unrelated))
|
reveal_when_assignable_to(T, Unrelated) # revealed: never
|
||||||
static_assert(is_assignable_to(T, Any))
|
reveal_when_assignable_to(T, Any) # revealed: always
|
||||||
static_assert(is_assignable_to(T, Super | Any))
|
reveal_when_assignable_to(T, Super | Any) # revealed: always
|
||||||
static_assert(is_assignable_to(T, Super | Unrelated))
|
reveal_when_assignable_to(T, Super | Unrelated) # revealed: always
|
||||||
static_assert(not is_assignable_to(Super, T))
|
reveal_when_assignable_to(Super, T) # revealed: never
|
||||||
static_assert(is_assignable_to(Base, T))
|
reveal_when_assignable_to(Base, T) # revealed: always
|
||||||
static_assert(not is_assignable_to(Unrelated, T))
|
reveal_when_assignable_to(Unrelated, T) # revealed: never
|
||||||
static_assert(is_assignable_to(Any, T))
|
reveal_when_assignable_to(Any, T) # revealed: always
|
||||||
static_assert(not is_assignable_to(Super | Any, T))
|
reveal_when_assignable_to(Super | Any, T) # revealed: never
|
||||||
static_assert(is_assignable_to(Base | Any, T))
|
reveal_when_assignable_to(Base | Any, T) # revealed: always
|
||||||
static_assert(not is_assignable_to(Super | Unrelated, T))
|
reveal_when_assignable_to(Super | Unrelated, T) # revealed: never
|
||||||
static_assert(is_assignable_to(Intersection[Base, Unrelated], T))
|
reveal_when_assignable_to(Intersection[Base, Unrelated], T) # revealed: always
|
||||||
static_assert(is_assignable_to(Intersection[Base, Any], T))
|
reveal_when_assignable_to(Intersection[Base, Any], T) # revealed: always
|
||||||
|
|
||||||
static_assert(not is_subtype_of(T, Super))
|
reveal_when_subtype_of(T, Super) # revealed: never
|
||||||
static_assert(not is_subtype_of(T, Base))
|
reveal_when_subtype_of(T, Base) # revealed: never
|
||||||
static_assert(not is_subtype_of(T, Sub))
|
reveal_when_subtype_of(T, Sub) # revealed: never
|
||||||
static_assert(not is_subtype_of(T, Unrelated))
|
reveal_when_subtype_of(T, Unrelated) # revealed: never
|
||||||
static_assert(not is_subtype_of(T, Any))
|
reveal_when_subtype_of(T, Any) # revealed: never
|
||||||
static_assert(not is_subtype_of(T, Super | Any))
|
reveal_when_subtype_of(T, Super | Any) # revealed: never
|
||||||
static_assert(not is_subtype_of(T, Super | Unrelated))
|
reveal_when_subtype_of(T, Super | Unrelated) # revealed: never
|
||||||
static_assert(not is_subtype_of(Super, T))
|
reveal_when_subtype_of(Super, T) # revealed: never
|
||||||
static_assert(not is_subtype_of(Base, T))
|
reveal_when_subtype_of(Base, T) # revealed: never
|
||||||
static_assert(not is_subtype_of(Unrelated, T))
|
reveal_when_subtype_of(Unrelated, T) # revealed: never
|
||||||
static_assert(not is_subtype_of(Any, T))
|
reveal_when_subtype_of(Any, T) # revealed: never
|
||||||
static_assert(not is_subtype_of(Super | Any, T))
|
reveal_when_subtype_of(Super | Any, T) # revealed: never
|
||||||
static_assert(not is_subtype_of(Base | Any, T))
|
reveal_when_subtype_of(Base | Any, T) # revealed: never
|
||||||
static_assert(not is_subtype_of(Super | Unrelated, T))
|
reveal_when_subtype_of(Super | Unrelated, T) # revealed: never
|
||||||
static_assert(not is_subtype_of(Intersection[Base, Unrelated], T))
|
reveal_when_subtype_of(Intersection[Base, Unrelated], T) # revealed: never
|
||||||
static_assert(not is_subtype_of(Intersection[Base, Any], T))
|
reveal_when_subtype_of(Intersection[Base, Any], T) # revealed: never
|
||||||
```
|
```
|
||||||
|
|
||||||
Two distinct fully static typevars are not subtypes of each other, even if they have the same
|
Two distinct fully static typevars are not subtypes of each other, even if they have the same
|
||||||
|
@ -299,58 +299,58 @@ the same type.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
def two_constrained[T: (int, str), U: (int, str)](t: T, u: U) -> None:
|
def two_constrained[T: (int, str), U: (int, str)](t: T, u: U) -> None:
|
||||||
static_assert(not is_assignable_to(T, U))
|
reveal_when_assignable_to(T, U) # revealed: never
|
||||||
static_assert(not is_assignable_to(U, T))
|
reveal_when_assignable_to(U, T) # revealed: never
|
||||||
|
|
||||||
static_assert(not is_subtype_of(T, U))
|
reveal_when_subtype_of(T, U) # revealed: never
|
||||||
static_assert(not is_subtype_of(U, T))
|
reveal_when_subtype_of(U, T) # revealed: never
|
||||||
|
|
||||||
@final
|
@final
|
||||||
class AnotherFinalClass: ...
|
class AnotherFinalClass: ...
|
||||||
|
|
||||||
def two_final_constrained[T: (FinalClass, AnotherFinalClass), U: (FinalClass, AnotherFinalClass)](t: T, u: U) -> None:
|
def two_final_constrained[T: (FinalClass, AnotherFinalClass), U: (FinalClass, AnotherFinalClass)](t: T, u: U) -> None:
|
||||||
static_assert(not is_assignable_to(T, U))
|
reveal_when_assignable_to(T, U) # revealed: never
|
||||||
static_assert(not is_assignable_to(U, T))
|
reveal_when_assignable_to(U, T) # revealed: never
|
||||||
|
|
||||||
static_assert(not is_subtype_of(T, U))
|
reveal_when_subtype_of(T, U) # revealed: never
|
||||||
static_assert(not is_subtype_of(U, T))
|
reveal_when_subtype_of(U, T) # revealed: never
|
||||||
```
|
```
|
||||||
|
|
||||||
A bound or constrained typevar is a subtype of itself in a union:
|
A bound or constrained typevar is a subtype of itself in a union:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
def union[T: Base, U: (Base, Unrelated)](t: T, u: U) -> None:
|
def union[T: Base, U: (Base, Unrelated)](t: T, u: U) -> None:
|
||||||
static_assert(is_assignable_to(T, T | None))
|
reveal_when_assignable_to(T, T | None) # revealed: always
|
||||||
static_assert(is_assignable_to(U, U | None))
|
reveal_when_assignable_to(U, U | None) # revealed: always
|
||||||
|
|
||||||
static_assert(is_subtype_of(T, T | None))
|
reveal_when_subtype_of(T, T | None) # revealed: always
|
||||||
static_assert(is_subtype_of(U, U | None))
|
reveal_when_subtype_of(U, U | None) # revealed: always
|
||||||
```
|
```
|
||||||
|
|
||||||
A bound or constrained typevar in a union with a dynamic type is assignable to the typevar:
|
A bound or constrained typevar in a union with a dynamic type is assignable to the typevar:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
def union_with_dynamic[T: Base, U: (Base, Unrelated)](t: T, u: U) -> None:
|
def union_with_dynamic[T: Base, U: (Base, Unrelated)](t: T, u: U) -> None:
|
||||||
static_assert(is_assignable_to(T | Any, T))
|
reveal_when_assignable_to(T | Any, T) # revealed: always
|
||||||
static_assert(is_assignable_to(U | Any, U))
|
reveal_when_assignable_to(U | Any, U) # revealed: always
|
||||||
|
|
||||||
static_assert(not is_subtype_of(T | Any, T))
|
reveal_when_subtype_of(T | Any, T) # revealed: never
|
||||||
static_assert(not is_subtype_of(U | Any, U))
|
reveal_when_subtype_of(U | Any, U) # revealed: never
|
||||||
```
|
```
|
||||||
|
|
||||||
And an intersection of a typevar with another type is always a subtype of the TypeVar:
|
And an intersection of a typevar with another type is always a subtype of the TypeVar:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from ty_extensions import Intersection, Not, is_disjoint_from
|
from ty_extensions import Intersection, Not, is_disjoint_from, static_assert
|
||||||
|
|
||||||
class A: ...
|
class A: ...
|
||||||
|
|
||||||
def inter[T: Base, U: (Base, Unrelated)](t: T, u: U) -> None:
|
def inter[T: Base, U: (Base, Unrelated)](t: T, u: U) -> None:
|
||||||
static_assert(is_assignable_to(Intersection[T, Unrelated], T))
|
reveal_when_assignable_to(Intersection[T, Unrelated], T) # revealed: always
|
||||||
static_assert(is_subtype_of(Intersection[T, Unrelated], T))
|
reveal_when_subtype_of(Intersection[T, Unrelated], T) # revealed: always
|
||||||
|
|
||||||
static_assert(is_assignable_to(Intersection[U, A], U))
|
reveal_when_assignable_to(Intersection[U, A], U) # revealed: always
|
||||||
static_assert(is_subtype_of(Intersection[U, A], U))
|
reveal_when_subtype_of(Intersection[U, A], U) # revealed: always
|
||||||
|
|
||||||
static_assert(is_disjoint_from(Not[T], T))
|
static_assert(is_disjoint_from(Not[T], T))
|
||||||
static_assert(is_disjoint_from(T, Not[T]))
|
static_assert(is_disjoint_from(T, Not[T]))
|
||||||
|
@ -647,14 +647,14 @@ The intersection of a typevar with any other type is assignable to (and if fully
|
||||||
of) itself.
|
of) itself.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from ty_extensions import is_assignable_to, is_subtype_of, static_assert, Not
|
from ty_extensions import reveal_when_assignable_to, reveal_when_subtype_of, Not
|
||||||
|
|
||||||
def intersection_is_assignable[T](t: T) -> None:
|
def intersection_is_assignable[T](t: T) -> None:
|
||||||
static_assert(is_assignable_to(Intersection[T, None], T))
|
reveal_when_assignable_to(Intersection[T, None], T) # revealed: always
|
||||||
static_assert(is_assignable_to(Intersection[T, Not[None]], T))
|
reveal_when_assignable_to(Intersection[T, Not[None]], T) # revealed: always
|
||||||
|
|
||||||
static_assert(is_subtype_of(Intersection[T, None], T))
|
reveal_when_subtype_of(Intersection[T, None], T) # revealed: always
|
||||||
static_assert(is_subtype_of(Intersection[T, Not[None]], T))
|
reveal_when_subtype_of(Intersection[T, Not[None]], T) # revealed: always
|
||||||
```
|
```
|
||||||
|
|
||||||
## Narrowing
|
## Narrowing
|
||||||
|
|
|
@ -413,13 +413,13 @@ To see the kinds and types of the protocol members, you can use the debugging ai
|
||||||
from ty_extensions import reveal_protocol_interface
|
from ty_extensions import reveal_protocol_interface
|
||||||
from typing import SupportsIndex, SupportsAbs, ClassVar, Iterator
|
from typing import SupportsIndex, SupportsAbs, ClassVar, Iterator
|
||||||
|
|
||||||
# error: [revealed-type] "Revealed protocol interface: `{"method_member": MethodMember(`(self) -> bytes`), "x": AttributeMember(`int`), "y": PropertyMember { getter: `def y(self) -> str` }, "z": PropertyMember { getter: `def z(self) -> int`, setter: `def z(self, z: int) -> None` }}`"
|
# revealed: {"method_member": MethodMember(`(self) -> bytes`), "x": AttributeMember(`int`), "y": PropertyMember { getter: `def y(self) -> str` }, "z": PropertyMember { getter: `def z(self) -> int`, setter: `def z(self, z: int) -> None` }}
|
||||||
reveal_protocol_interface(Foo)
|
reveal_protocol_interface(Foo)
|
||||||
# error: [revealed-type] "Revealed protocol interface: `{"__index__": MethodMember(`(self) -> int`)}`"
|
# revealed: {"__index__": MethodMember(`(self) -> int`)}
|
||||||
reveal_protocol_interface(SupportsIndex)
|
reveal_protocol_interface(SupportsIndex)
|
||||||
# error: [revealed-type] "Revealed protocol interface: `{"__abs__": MethodMember(`(self) -> Unknown`)}`"
|
# revealed: {"__abs__": MethodMember(`(self) -> Unknown`)}
|
||||||
reveal_protocol_interface(SupportsAbs)
|
reveal_protocol_interface(SupportsAbs)
|
||||||
# error: [revealed-type] "Revealed protocol interface: `{"__iter__": MethodMember(`(self) -> Iterator[Unknown]`), "__next__": MethodMember(`(self) -> Unknown`)}`"
|
# revealed: {"__iter__": MethodMember(`(self) -> Iterator[Unknown]`), "__next__": MethodMember(`(self) -> Unknown`)}
|
||||||
reveal_protocol_interface(Iterator)
|
reveal_protocol_interface(Iterator)
|
||||||
|
|
||||||
# error: [invalid-argument-type] "Invalid argument to `reveal_protocol_interface`: Only protocol classes can be passed to `reveal_protocol_interface`"
|
# error: [invalid-argument-type] "Invalid argument to `reveal_protocol_interface`: Only protocol classes can be passed to `reveal_protocol_interface`"
|
||||||
|
@ -439,9 +439,9 @@ do not implement any special handling for generic aliases passed to the function
|
||||||
reveal_type(get_protocol_members(SupportsAbs[int])) # revealed: frozenset[str]
|
reveal_type(get_protocol_members(SupportsAbs[int])) # revealed: frozenset[str]
|
||||||
reveal_type(get_protocol_members(Iterator[int])) # revealed: frozenset[str]
|
reveal_type(get_protocol_members(Iterator[int])) # revealed: frozenset[str]
|
||||||
|
|
||||||
# error: [revealed-type] "Revealed protocol interface: `{"__abs__": MethodMember(`(self) -> int`)}`"
|
# revealed: {"__abs__": MethodMember(`(self) -> int`)}
|
||||||
reveal_protocol_interface(SupportsAbs[int])
|
reveal_protocol_interface(SupportsAbs[int])
|
||||||
# error: [revealed-type] "Revealed protocol interface: `{"__iter__": MethodMember(`(self) -> Iterator[int]`), "__next__": MethodMember(`(self) -> int`)}`"
|
# revealed: {"__iter__": MethodMember(`(self) -> Iterator[int]`), "__next__": MethodMember(`(self) -> int`)}
|
||||||
reveal_protocol_interface(Iterator[int])
|
reveal_protocol_interface(Iterator[int])
|
||||||
|
|
||||||
class BaseProto(Protocol):
|
class BaseProto(Protocol):
|
||||||
|
@ -450,16 +450,16 @@ class BaseProto(Protocol):
|
||||||
class SubProto(BaseProto, Protocol):
|
class SubProto(BaseProto, Protocol):
|
||||||
def member(self) -> bool: ...
|
def member(self) -> bool: ...
|
||||||
|
|
||||||
# error: [revealed-type] "Revealed protocol interface: `{"member": MethodMember(`(self) -> int`)}`"
|
# revealed: {"member": MethodMember(`(self) -> int`)}
|
||||||
reveal_protocol_interface(BaseProto)
|
reveal_protocol_interface(BaseProto)
|
||||||
|
|
||||||
# error: [revealed-type] "Revealed protocol interface: `{"member": MethodMember(`(self) -> bool`)}`"
|
# revealed: {"member": MethodMember(`(self) -> bool`)}
|
||||||
reveal_protocol_interface(SubProto)
|
reveal_protocol_interface(SubProto)
|
||||||
|
|
||||||
class ProtoWithClassVar(Protocol):
|
class ProtoWithClassVar(Protocol):
|
||||||
x: ClassVar[int]
|
x: ClassVar[int]
|
||||||
|
|
||||||
# error: [revealed-type] "Revealed protocol interface: `{"x": AttributeMember(`int`; ClassVar)}`"
|
# revealed: {"x": AttributeMember(`int`; ClassVar)}
|
||||||
reveal_protocol_interface(ProtoWithClassVar)
|
reveal_protocol_interface(ProtoWithClassVar)
|
||||||
|
|
||||||
class ProtocolWithDefault(Protocol):
|
class ProtocolWithDefault(Protocol):
|
||||||
|
@ -468,7 +468,7 @@ class ProtocolWithDefault(Protocol):
|
||||||
# We used to incorrectly report this as having an `x: Literal[0]` member;
|
# We used to incorrectly report this as having an `x: Literal[0]` member;
|
||||||
# declared types should take priority over inferred types for protocol interfaces!
|
# declared types should take priority over inferred types for protocol interfaces!
|
||||||
#
|
#
|
||||||
# error: [revealed-type] "Revealed protocol interface: `{"x": AttributeMember(`int`)}`"
|
# revealed: {"x": AttributeMember(`int`)}
|
||||||
reveal_protocol_interface(ProtocolWithDefault)
|
reveal_protocol_interface(ProtocolWithDefault)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -2474,7 +2474,7 @@ class Foo(Protocol):
|
||||||
from stub import Foo
|
from stub import Foo
|
||||||
from ty_extensions import reveal_protocol_interface
|
from ty_extensions import reveal_protocol_interface
|
||||||
|
|
||||||
# error: [revealed-type] "Revealed protocol interface: `{"x": AttributeMember(`int`; ClassVar)}`"
|
# revealed: {"x": AttributeMember(`int`; ClassVar)}
|
||||||
reveal_protocol_interface(Foo)
|
reveal_protocol_interface(Foo)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -4168,6 +4168,24 @@ impl<'db> Type<'db> {
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
|
|
||||||
|
Some(
|
||||||
|
KnownFunction::RevealWhenAssignableTo | KnownFunction::RevealWhenSubtypeOf,
|
||||||
|
) => Binding::single(
|
||||||
|
self,
|
||||||
|
Signature::new(
|
||||||
|
Parameters::new([
|
||||||
|
Parameter::positional_only(Some(Name::new_static("a")))
|
||||||
|
.type_form()
|
||||||
|
.with_annotated_type(Type::any()),
|
||||||
|
Parameter::positional_only(Some(Name::new_static("b")))
|
||||||
|
.type_form()
|
||||||
|
.with_annotated_type(Type::any()),
|
||||||
|
]),
|
||||||
|
Some(KnownClass::NoneType.to_instance(db)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
|
||||||
Some(KnownFunction::IsSingleton | KnownFunction::IsSingleValued) => {
|
Some(KnownFunction::IsSingleton | KnownFunction::IsSingleValued) => {
|
||||||
Binding::single(
|
Binding::single(
|
||||||
self,
|
self,
|
||||||
|
|
|
@ -133,9 +133,6 @@ pub(crate) trait Constraints<'db>: Clone + Sized {
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is here so that we can easily print constraint sets when debugging.
|
// This is here so that we can easily print constraint sets when debugging.
|
||||||
// TODO: Add a ty_extensions function to reveal constraint sets so that this is no longer dead
|
|
||||||
// code, and so that we verify the contents of our rendering.
|
|
||||||
#[expect(dead_code)]
|
|
||||||
fn display(&self, db: &'db dyn Db) -> impl Display;
|
fn display(&self, db: &'db dyn Db) -> impl Display;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,34 +342,6 @@ impl<'db> ConstraintSet<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is here so that we can easily print constraint sets when debugging.
|
|
||||||
// TODO: Add a ty_extensions function to reveal constraint sets so that this is no longer dead
|
|
||||||
// code, and so that we verify the contents of our rendering.
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub(crate) fn display(&self, db: &'db dyn Db) -> impl Display {
|
|
||||||
struct DisplayConstraintSet<'a, 'db> {
|
|
||||||
set: &'a ConstraintSet<'db>,
|
|
||||||
db: &'db dyn Db,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for DisplayConstraintSet<'_, '_> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
if self.set.clauses.is_empty() {
|
|
||||||
return f.write_str("0");
|
|
||||||
}
|
|
||||||
for (i, clause) in self.set.clauses.iter().enumerate() {
|
|
||||||
if i > 0 {
|
|
||||||
f.write_str(" ∨ ")?;
|
|
||||||
}
|
|
||||||
clause.display(self.db).fmt(f)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DisplayConstraintSet { set: self, db }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> Constraints<'db> for ConstraintSet<'db> {
|
impl<'db> Constraints<'db> for ConstraintSet<'db> {
|
||||||
|
@ -411,7 +380,27 @@ impl<'db> Constraints<'db> for ConstraintSet<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display(&self, db: &'db dyn Db) -> impl Display {
|
fn display(&self, db: &'db dyn Db) -> impl Display {
|
||||||
self.display(db)
|
struct DisplayConstraintSet<'a, 'db> {
|
||||||
|
set: &'a ConstraintSet<'db>,
|
||||||
|
db: &'db dyn Db,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for DisplayConstraintSet<'_, '_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
if self.set.clauses.is_empty() {
|
||||||
|
return f.write_str("0");
|
||||||
|
}
|
||||||
|
for (i, clause) in self.set.clauses.iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
f.write_str(" ∨ ")?;
|
||||||
|
}
|
||||||
|
clause.display(self.db).fmt(f)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DisplayConstraintSet { set: self, db }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ use crate::semantic_index::definition::Definition;
|
||||||
use crate::semantic_index::scope::ScopeId;
|
use crate::semantic_index::scope::ScopeId;
|
||||||
use crate::semantic_index::semantic_index;
|
use crate::semantic_index::semantic_index;
|
||||||
use crate::types::call::{Binding, CallArguments};
|
use crate::types::call::{Binding, CallArguments};
|
||||||
use crate::types::constraints::Constraints;
|
use crate::types::constraints::{ConstraintSet, Constraints};
|
||||||
use crate::types::context::InferContext;
|
use crate::types::context::InferContext;
|
||||||
use crate::types::diagnostic::{
|
use crate::types::diagnostic::{
|
||||||
INVALID_ARGUMENT_TYPE, REDUNDANT_CAST, STATIC_ASSERT_ERROR, TYPE_ASSERTION_FAILURE,
|
INVALID_ARGUMENT_TYPE, REDUNDANT_CAST, STATIC_ASSERT_ERROR, TYPE_ASSERTION_FAILURE,
|
||||||
|
@ -1188,6 +1188,10 @@ pub enum KnownFunction {
|
||||||
HasMember,
|
HasMember,
|
||||||
/// `ty_extensions.reveal_protocol_interface`
|
/// `ty_extensions.reveal_protocol_interface`
|
||||||
RevealProtocolInterface,
|
RevealProtocolInterface,
|
||||||
|
/// `ty_extensions.reveal_when_assignable_to`
|
||||||
|
RevealWhenAssignableTo,
|
||||||
|
/// `ty_extensions.reveal_when_subtype_of`
|
||||||
|
RevealWhenSubtypeOf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KnownFunction {
|
impl KnownFunction {
|
||||||
|
@ -1253,6 +1257,8 @@ impl KnownFunction {
|
||||||
| Self::StaticAssert
|
| Self::StaticAssert
|
||||||
| Self::HasMember
|
| Self::HasMember
|
||||||
| Self::RevealProtocolInterface
|
| Self::RevealProtocolInterface
|
||||||
|
| Self::RevealWhenAssignableTo
|
||||||
|
| Self::RevealWhenSubtypeOf
|
||||||
| Self::AllMembers => module.is_ty_extensions(),
|
| Self::AllMembers => module.is_ty_extensions(),
|
||||||
Self::ImportModule => module.is_importlib(),
|
Self::ImportModule => module.is_importlib(),
|
||||||
}
|
}
|
||||||
|
@ -1548,6 +1554,54 @@ impl KnownFunction {
|
||||||
overload.set_return_type(Type::module_literal(db, file, module));
|
overload.set_return_type(Type::module_literal(db, file, module));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KnownFunction::RevealWhenAssignableTo => {
|
||||||
|
let [Some(ty_a), Some(ty_b)] = overload.parameter_types() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let constraints = ty_a.when_assignable_to::<ConstraintSet>(db, *ty_b);
|
||||||
|
let Some(builder) =
|
||||||
|
context.report_diagnostic(DiagnosticId::RevealedType, Severity::Info)
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let mut diag = builder.into_diagnostic("Assignability holds");
|
||||||
|
let span = context.span(call_expression);
|
||||||
|
if constraints.is_always_satisfied(db) {
|
||||||
|
diag.annotate(Annotation::primary(span).message("always"));
|
||||||
|
} else if constraints.is_never_satisfied(db) {
|
||||||
|
diag.annotate(Annotation::primary(span).message("never"));
|
||||||
|
} else {
|
||||||
|
diag.annotate(
|
||||||
|
Annotation::primary(span)
|
||||||
|
.message(format_args!("when {}", constraints.display(db))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
KnownFunction::RevealWhenSubtypeOf => {
|
||||||
|
let [Some(ty_a), Some(ty_b)] = overload.parameter_types() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let constraints = ty_a.when_subtype_of::<ConstraintSet>(db, *ty_b);
|
||||||
|
let Some(builder) =
|
||||||
|
context.report_diagnostic(DiagnosticId::RevealedType, Severity::Info)
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let mut diag = builder.into_diagnostic("Subtyping holds");
|
||||||
|
let span = context.span(call_expression);
|
||||||
|
if constraints.is_always_satisfied(db) {
|
||||||
|
diag.annotate(Annotation::primary(span).message("always"));
|
||||||
|
} else if constraints.is_never_satisfied(db) {
|
||||||
|
diag.annotate(Annotation::primary(span).message("never"));
|
||||||
|
} else {
|
||||||
|
diag.annotate(
|
||||||
|
Annotation::primary(span)
|
||||||
|
.message(format_args!("when {}", constraints.display(db))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1608,6 +1662,8 @@ pub(crate) mod tests {
|
||||||
| KnownFunction::IsEquivalentTo
|
| KnownFunction::IsEquivalentTo
|
||||||
| KnownFunction::HasMember
|
| KnownFunction::HasMember
|
||||||
| KnownFunction::RevealProtocolInterface
|
| KnownFunction::RevealProtocolInterface
|
||||||
|
| KnownFunction::RevealWhenAssignableTo
|
||||||
|
| KnownFunction::RevealWhenSubtypeOf
|
||||||
| KnownFunction::AllMembers => KnownModule::TyExtensions,
|
| KnownFunction::AllMembers => KnownModule::TyExtensions,
|
||||||
|
|
||||||
KnownFunction::ImportModule => KnownModule::ImportLib,
|
KnownFunction::ImportModule => KnownModule::ImportLib,
|
||||||
|
|
|
@ -1,15 +1,19 @@
|
||||||
//! Match [`Diagnostic`]s against assertions and produce test failure
|
//! Match [`Diagnostic`]s against assertions and produce test failure
|
||||||
//! messages for any mismatches.
|
//! messages for any mismatches.
|
||||||
use crate::assertion::{InlineFileAssertions, ParsedAssertion, UnparsedAssertion};
|
|
||||||
use crate::db::Db;
|
use std::borrow::Cow;
|
||||||
use crate::diagnostic::SortedDiagnostics;
|
use std::cmp::Ordering;
|
||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use ruff_db::diagnostic::{Diagnostic, DiagnosticId};
|
use ruff_db::diagnostic::{Diagnostic, DiagnosticId};
|
||||||
use ruff_db::files::File;
|
use ruff_db::files::File;
|
||||||
use ruff_db::source::{SourceText, line_index, source_text};
|
use ruff_db::source::{SourceText, line_index, source_text};
|
||||||
use ruff_source_file::{LineIndex, OneIndexed};
|
use ruff_source_file::{LineIndex, OneIndexed};
|
||||||
use std::cmp::Ordering;
|
|
||||||
use std::ops::Range;
|
use crate::assertion::{InlineFileAssertions, ParsedAssertion, UnparsedAssertion};
|
||||||
|
use crate::db::Db;
|
||||||
|
use crate::diagnostic::SortedDiagnostics;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub(super) struct FailuresByLine {
|
pub(super) struct FailuresByLine {
|
||||||
|
@ -194,14 +198,19 @@ impl UnmatchedWithColumn for &Diagnostic {
|
||||||
|
|
||||||
/// Discard `@Todo`-type metadata from expected types, which is not available
|
/// Discard `@Todo`-type metadata from expected types, which is not available
|
||||||
/// when running in release mode.
|
/// when running in release mode.
|
||||||
|
fn discard_todo_metadata(ty: &str) -> Cow<'_, str> {
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
fn discard_todo_metadata(ty: &str) -> std::borrow::Cow<'_, str> {
|
{
|
||||||
static TODO_METADATA_REGEX: std::sync::LazyLock<regex::Regex> =
|
static TODO_METADATA_REGEX: std::sync::LazyLock<regex::Regex> =
|
||||||
std::sync::LazyLock::new(|| regex::Regex::new(r"@Todo\([^)]*\)").unwrap());
|
std::sync::LazyLock::new(|| regex::Regex::new(r"@Todo\([^)]*\)").unwrap());
|
||||||
|
|
||||||
TODO_METADATA_REGEX.replace_all(ty, "@Todo")
|
TODO_METADATA_REGEX.replace_all(ty, "@Todo")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
Cow::Borrowed(ty)
|
||||||
|
}
|
||||||
|
|
||||||
struct Matcher {
|
struct Matcher {
|
||||||
line_index: LineIndex,
|
line_index: LineIndex,
|
||||||
source: SourceText,
|
source: SourceText,
|
||||||
|
@ -297,21 +306,53 @@ impl Matcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ParsedAssertion::Revealed(expected_type) => {
|
ParsedAssertion::Revealed(expected_type) => {
|
||||||
#[cfg(not(debug_assertions))]
|
let expected_type = discard_todo_metadata(expected_type);
|
||||||
let expected_type = discard_todo_metadata(&expected_type);
|
let expected_reveal_type_message = format!("`{expected_type}`");
|
||||||
|
|
||||||
|
let diagnostic_matches_reveal = |diagnostic: &Diagnostic| {
|
||||||
|
if diagnostic.id() != DiagnosticId::RevealedType {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let primary_message = diagnostic.primary_message();
|
||||||
|
let Some(primary_annotation) =
|
||||||
|
(diagnostic.primary_annotation()).and_then(|a| a.get_message())
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// reveal_type
|
||||||
|
if primary_message == "Revealed type"
|
||||||
|
&& primary_annotation == expected_reveal_type_message
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reveal_protocol_interface
|
||||||
|
if primary_message == "Revealed protocol interface"
|
||||||
|
&& primary_annotation == expected_reveal_type_message
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reveal_when_assignable_to
|
||||||
|
if primary_message == "Assignability holds"
|
||||||
|
&& primary_annotation == expected_type
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reveal_when_subtype_of
|
||||||
|
if primary_message == "Subtyping holds" && primary_annotation == expected_type {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
let mut matched_revealed_type = None;
|
let mut matched_revealed_type = None;
|
||||||
let mut matched_undefined_reveal = None;
|
let mut matched_undefined_reveal = None;
|
||||||
let expected_reveal_type_message = format!("`{expected_type}`");
|
|
||||||
for (index, diagnostic) in unmatched.iter().enumerate() {
|
for (index, diagnostic) in unmatched.iter().enumerate() {
|
||||||
if matched_revealed_type.is_none()
|
if matched_revealed_type.is_none() && diagnostic_matches_reveal(diagnostic) {
|
||||||
&& diagnostic.id() == DiagnosticId::RevealedType
|
|
||||||
&& diagnostic
|
|
||||||
.primary_annotation()
|
|
||||||
.and_then(|a| a.get_message())
|
|
||||||
.unwrap_or_default()
|
|
||||||
== expected_reveal_type_message
|
|
||||||
{
|
|
||||||
matched_revealed_type = Some(index);
|
matched_revealed_type = Some(index);
|
||||||
} else if matched_undefined_reveal.is_none()
|
} else if matched_undefined_reveal.is_none()
|
||||||
&& diagnostic.id().is_lint_named("undefined-reveal")
|
&& diagnostic.id().is_lint_named("undefined-reveal")
|
||||||
|
|
|
@ -45,15 +45,21 @@ type JustComplex = TypeOf[1.0j]
|
||||||
# Ideally, these would be annotated using `TypeForm`, but that has not been
|
# Ideally, these would be annotated using `TypeForm`, but that has not been
|
||||||
# standardized yet (https://peps.python.org/pep-0747).
|
# standardized yet (https://peps.python.org/pep-0747).
|
||||||
def is_equivalent_to(type_a: Any, type_b: Any) -> bool: ...
|
def is_equivalent_to(type_a: Any, type_b: Any) -> bool: ...
|
||||||
def is_subtype_of(type_derived: Any, type_base: Any) -> bool: ...
|
def is_subtype_of(type_a: Any, type_b: Any) -> bool: ...
|
||||||
def is_assignable_to(type_target: Any, type_source: Any) -> bool: ...
|
def is_assignable_to(type_a: Any, type_b: Any) -> bool: ...
|
||||||
def is_disjoint_from(type_a: Any, type_b: Any) -> bool: ...
|
def is_disjoint_from(type_a: Any, type_b: Any) -> bool: ...
|
||||||
def is_singleton(type: Any) -> bool: ...
|
def is_singleton(ty: Any) -> bool: ...
|
||||||
def is_single_valued(type: Any) -> bool: ...
|
def is_single_valued(ty: Any) -> bool: ...
|
||||||
|
|
||||||
|
# These are the same as above, but instead of returning _whether_ the property
|
||||||
|
# holds, it shows a diagnostic that describes under what constraints the
|
||||||
|
# property holds.
|
||||||
|
def reveal_when_assignable_to(type_a: Any, type_b: Any) -> None: ...
|
||||||
|
def reveal_when_subtype_of(type_a: Any, type_b: Any) -> None: ...
|
||||||
|
|
||||||
# Returns the generic context of a type as a tuple of typevars, or `None` if the
|
# Returns the generic context of a type as a tuple of typevars, or `None` if the
|
||||||
# type is not generic.
|
# type is not generic.
|
||||||
def generic_context(type: Any) -> Any: ...
|
def generic_context(ty: Any) -> Any: ...
|
||||||
|
|
||||||
# Returns the `__all__` names of a module as a tuple of sorted strings, or `None` if
|
# Returns the `__all__` names of a module as a tuple of sorted strings, or `None` if
|
||||||
# either the module does not have `__all__` or it has invalid elements.
|
# either the module does not have `__all__` or it has invalid elements.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue