mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 10:48:32 +00:00
[ty] Understand classes that inherit from subscripted Protocol[]
as generic (#17832)
This commit is contained in:
parent
2370297cde
commit
d1bb10a66b
35 changed files with 451 additions and 183 deletions
|
@ -59,7 +59,29 @@ type KeyDiagnosticFields = (
|
|||
Severity,
|
||||
);
|
||||
|
||||
static EXPECTED_TOMLLIB_DIAGNOSTICS: &[KeyDiagnosticFields] = &[];
|
||||
static EXPECTED_TOMLLIB_DIAGNOSTICS: &[KeyDiagnosticFields] = &[
|
||||
(
|
||||
DiagnosticId::lint("invalid-argument-type"),
|
||||
Some("/src/tomllib/_parser.py"),
|
||||
Some(8224..8254),
|
||||
"Argument to this function is incorrect",
|
||||
Severity::Error,
|
||||
),
|
||||
(
|
||||
DiagnosticId::lint("invalid-argument-type"),
|
||||
Some("/src/tomllib/_parser.py"),
|
||||
Some(16914..16948),
|
||||
"Argument to this function is incorrect",
|
||||
Severity::Error,
|
||||
),
|
||||
(
|
||||
DiagnosticId::lint("invalid-argument-type"),
|
||||
Some("/src/tomllib/_parser.py"),
|
||||
Some(17319..17363),
|
||||
"Argument to this function is incorrect",
|
||||
Severity::Error,
|
||||
),
|
||||
];
|
||||
|
||||
fn tomllib_path(file: &TestFile) -> SystemPathBuf {
|
||||
SystemPathBuf::from("src").join(file.name())
|
||||
|
|
|
@ -33,7 +33,9 @@ class Shape:
|
|||
reveal_type(self) # revealed: Unknown
|
||||
return self
|
||||
|
||||
reveal_type(Shape().nested_type()) # revealed: @Todo(specialized non-generic class)
|
||||
# TODO: should be `list[Shape]`
|
||||
reveal_type(Shape().nested_type()) # revealed: list[Self]
|
||||
|
||||
reveal_type(Shape().nested_func()) # revealed: Shape
|
||||
|
||||
class Circle(Shape):
|
||||
|
|
|
@ -14,7 +14,7 @@ Ts = TypeVarTuple("Ts")
|
|||
|
||||
def append_int(*args: *Ts) -> tuple[*Ts, int]:
|
||||
# TODO: tuple[*Ts]
|
||||
reveal_type(args) # revealed: tuple
|
||||
reveal_type(args) # revealed: tuple[Unknown, ...]
|
||||
|
||||
return (*args, 1)
|
||||
|
||||
|
|
|
@ -30,24 +30,22 @@ def f(
|
|||
ordered_dict_bare: typing.OrderedDict,
|
||||
ordered_dict_parametrized: typing.OrderedDict[int, str],
|
||||
):
|
||||
# TODO: revealed: list[Unknown]
|
||||
reveal_type(list_bare) # revealed: list
|
||||
reveal_type(list_bare) # revealed: list[Unknown]
|
||||
# TODO: revealed: list[int]
|
||||
reveal_type(list_parametrized) # revealed: list
|
||||
reveal_type(list_parametrized) # revealed: list[Unknown]
|
||||
|
||||
reveal_type(dict_bare) # revealed: dict[Unknown, Unknown]
|
||||
# TODO: revealed: dict[int, str]
|
||||
reveal_type(dict_parametrized) # revealed: dict[Unknown, Unknown]
|
||||
|
||||
# TODO: revealed: set[Unknown]
|
||||
reveal_type(set_bare) # revealed: set
|
||||
reveal_type(set_bare) # revealed: set[Unknown]
|
||||
# TODO: revealed: set[int]
|
||||
reveal_type(set_parametrized) # revealed: set
|
||||
reveal_type(set_parametrized) # revealed: set[Unknown]
|
||||
|
||||
# TODO: revealed: frozenset[Unknown]
|
||||
reveal_type(frozen_set_bare) # revealed: frozenset
|
||||
reveal_type(frozen_set_bare) # revealed: frozenset[Unknown]
|
||||
# TODO: revealed: frozenset[str]
|
||||
reveal_type(frozen_set_parametrized) # revealed: frozenset
|
||||
reveal_type(frozen_set_parametrized) # revealed: frozenset[Unknown]
|
||||
|
||||
reveal_type(chain_map_bare) # revealed: ChainMap[Unknown, Unknown]
|
||||
# TODO: revealed: ChainMap[str, int]
|
||||
|
@ -61,10 +59,9 @@ def f(
|
|||
# TODO: revealed: defaultdict[str, int]
|
||||
reveal_type(default_dict_parametrized) # revealed: defaultdict[Unknown, Unknown]
|
||||
|
||||
# TODO: revealed: deque[Unknown]
|
||||
reveal_type(deque_bare) # revealed: deque
|
||||
reveal_type(deque_bare) # revealed: deque[Unknown]
|
||||
# TODO: revealed: deque[str]
|
||||
reveal_type(deque_parametrized) # revealed: deque
|
||||
reveal_type(deque_parametrized) # revealed: deque[Unknown]
|
||||
|
||||
reveal_type(ordered_dict_bare) # revealed: OrderedDict[Unknown, Unknown]
|
||||
# TODO: revealed: OrderedDict[int, str]
|
||||
|
@ -84,26 +81,23 @@ import typing
|
|||
|
||||
class ListSubclass(typing.List): ...
|
||||
|
||||
# TODO: generic protocols
|
||||
# revealed: tuple[<class 'ListSubclass'>, <class 'list'>, <class 'MutableSequence'>, <class 'Sequence'>, <class 'Reversible'>, <class 'Collection'>, <class 'Iterable'>, <class 'Container'>, @Todo(`Protocol[]` subscript), typing.Generic, <class 'object'>]
|
||||
# revealed: tuple[<class 'ListSubclass'>, <class 'list[Unknown]'>, <class 'MutableSequence[Unknown]'>, <class 'Sequence[Unknown]'>, <class 'Reversible[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol[_T_co], typing.Generic[_T_co], <class 'object'>]
|
||||
reveal_type(ListSubclass.__mro__)
|
||||
|
||||
class DictSubclass(typing.Dict): ...
|
||||
|
||||
# TODO: generic protocols
|
||||
# revealed: tuple[<class 'DictSubclass'>, <class 'dict[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection'>, <class 'Iterable'>, <class 'Container'>, @Todo(`Protocol[]` subscript), typing.Generic, typing.Generic[_KT, _VT_co], <class 'object'>]
|
||||
# TODO: should not have multiple `Generic[]` elements
|
||||
# revealed: tuple[<class 'DictSubclass'>, <class 'dict[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol[_T_co], typing.Generic[_T_co], typing.Generic[_KT, _VT_co], <class 'object'>]
|
||||
reveal_type(DictSubclass.__mro__)
|
||||
|
||||
class SetSubclass(typing.Set): ...
|
||||
|
||||
# TODO: generic protocols
|
||||
# revealed: tuple[<class 'SetSubclass'>, <class 'set'>, <class 'MutableSet'>, <class 'AbstractSet'>, <class 'Collection'>, <class 'Iterable'>, <class 'Container'>, @Todo(`Protocol[]` subscript), typing.Generic, <class 'object'>]
|
||||
# revealed: tuple[<class 'SetSubclass'>, <class 'set[Unknown]'>, <class 'MutableSet[Unknown]'>, <class 'AbstractSet[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol[_T_co], typing.Generic[_T_co], <class 'object'>]
|
||||
reveal_type(SetSubclass.__mro__)
|
||||
|
||||
class FrozenSetSubclass(typing.FrozenSet): ...
|
||||
|
||||
# TODO: generic protocols
|
||||
# revealed: tuple[<class 'FrozenSetSubclass'>, <class 'frozenset'>, <class 'AbstractSet'>, <class 'Collection'>, <class 'Iterable'>, <class 'Container'>, @Todo(`Protocol[]` subscript), typing.Generic, <class 'object'>]
|
||||
# revealed: tuple[<class 'FrozenSetSubclass'>, <class 'frozenset[Unknown]'>, <class 'AbstractSet[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol[_T_co], typing.Generic[_T_co], <class 'object'>]
|
||||
reveal_type(FrozenSetSubclass.__mro__)
|
||||
|
||||
####################
|
||||
|
@ -112,31 +106,30 @@ reveal_type(FrozenSetSubclass.__mro__)
|
|||
|
||||
class ChainMapSubclass(typing.ChainMap): ...
|
||||
|
||||
# TODO: generic protocols
|
||||
# revealed: tuple[<class 'ChainMapSubclass'>, <class 'ChainMap[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection'>, <class 'Iterable'>, <class 'Container'>, @Todo(`Protocol[]` subscript), typing.Generic, typing.Generic[_KT, _VT_co], <class 'object'>]
|
||||
# TODO: should not have multiple `Generic[]` elements
|
||||
# revealed: tuple[<class 'ChainMapSubclass'>, <class 'ChainMap[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol[_T_co], typing.Generic[_T_co], typing.Generic[_KT, _VT_co], <class 'object'>]
|
||||
reveal_type(ChainMapSubclass.__mro__)
|
||||
|
||||
class CounterSubclass(typing.Counter): ...
|
||||
|
||||
# TODO: Should be (CounterSubclass, Counter, dict, MutableMapping, Mapping, Collection, Sized, Iterable, Container, Generic, object)
|
||||
# revealed: tuple[<class 'CounterSubclass'>, <class 'Counter[Unknown]'>, <class 'dict[Unknown, int]'>, <class 'MutableMapping[Unknown, int]'>, <class 'Mapping[Unknown, int]'>, <class 'Collection'>, <class 'Iterable'>, <class 'Container'>, @Todo(`Protocol[]` subscript), typing.Generic, typing.Generic[_KT, _VT_co], typing.Generic[_T], <class 'object'>]
|
||||
# TODO: Should have one `Generic[]` element, not three(!)
|
||||
# revealed: tuple[<class 'CounterSubclass'>, <class 'Counter[Unknown]'>, <class 'dict[Unknown, int]'>, <class 'MutableMapping[Unknown, int]'>, <class 'Mapping[Unknown, int]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol[_T_co], typing.Generic[_T_co], typing.Generic[_KT, _VT_co], typing.Generic[_T], <class 'object'>]
|
||||
reveal_type(CounterSubclass.__mro__)
|
||||
|
||||
class DefaultDictSubclass(typing.DefaultDict): ...
|
||||
|
||||
# TODO: Should be (DefaultDictSubclass, defaultdict, dict, MutableMapping, Mapping, Collection, Sized, Iterable, Container, Generic, object)
|
||||
# revealed: tuple[<class 'DefaultDictSubclass'>, <class 'defaultdict[Unknown, Unknown]'>, <class 'dict[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection'>, <class 'Iterable'>, <class 'Container'>, @Todo(`Protocol[]` subscript), typing.Generic, typing.Generic[_KT, _VT_co], <class 'object'>]
|
||||
# TODO: Should not have multiple `Generic[]` elements
|
||||
# revealed: tuple[<class 'DefaultDictSubclass'>, <class 'defaultdict[Unknown, Unknown]'>, <class 'dict[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol[_T_co], typing.Generic[_T_co], typing.Generic[_KT, _VT_co], <class 'object'>]
|
||||
reveal_type(DefaultDictSubclass.__mro__)
|
||||
|
||||
class DequeSubclass(typing.Deque): ...
|
||||
|
||||
# TODO: generic protocols
|
||||
# revealed: tuple[<class 'DequeSubclass'>, <class 'deque'>, <class 'MutableSequence'>, <class 'Sequence'>, <class 'Reversible'>, <class 'Collection'>, <class 'Iterable'>, <class 'Container'>, @Todo(`Protocol[]` subscript), typing.Generic, <class 'object'>]
|
||||
# revealed: tuple[<class 'DequeSubclass'>, <class 'deque[Unknown]'>, <class 'MutableSequence[Unknown]'>, <class 'Sequence[Unknown]'>, <class 'Reversible[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol[_T_co], typing.Generic[_T_co], <class 'object'>]
|
||||
reveal_type(DequeSubclass.__mro__)
|
||||
|
||||
class OrderedDictSubclass(typing.OrderedDict): ...
|
||||
|
||||
# TODO: Should be (OrderedDictSubclass, OrderedDict, dict, MutableMapping, Mapping, Collection, Sized, Iterable, Container, Generic, object)
|
||||
# revealed: tuple[<class 'OrderedDictSubclass'>, <class 'OrderedDict[Unknown, Unknown]'>, <class 'dict[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection'>, <class 'Iterable'>, <class 'Container'>, @Todo(`Protocol[]` subscript), typing.Generic, typing.Generic[_KT, _VT_co], <class 'object'>]
|
||||
# TODO: Should not have multiple `Generic[]` elements
|
||||
# revealed: tuple[<class 'OrderedDictSubclass'>, <class 'OrderedDict[Unknown, Unknown]'>, <class 'dict[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol[_T_co], typing.Generic[_T_co], typing.Generic[_KT, _VT_co], <class 'object'>]
|
||||
reveal_type(OrderedDictSubclass.__mro__)
|
||||
```
|
||||
|
|
|
@ -16,7 +16,7 @@ Alias: TypeAlias = int
|
|||
|
||||
def f(*args: Unpack[Ts]) -> tuple[Unpack[Ts]]:
|
||||
# TODO: should understand the annotation
|
||||
reveal_type(args) # revealed: tuple
|
||||
reveal_type(args) # revealed: tuple[Unknown, ...]
|
||||
|
||||
reveal_type(Alias) # revealed: @Todo(Support for `typing.TypeAlias`)
|
||||
|
||||
|
@ -24,7 +24,7 @@ def g() -> TypeGuard[int]: ...
|
|||
def h() -> TypeIs[int]: ...
|
||||
def i(callback: Callable[Concatenate[int, P], R_co], *args: P.args, **kwargs: P.kwargs) -> R_co:
|
||||
# TODO: should understand the annotation
|
||||
reveal_type(args) # revealed: tuple
|
||||
reveal_type(args) # revealed: tuple[Unknown, ...]
|
||||
reveal_type(kwargs) # revealed: dict[str, @Todo(Support for `typing.ParamSpec`)]
|
||||
return callback(42, *args, **kwargs)
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ reveal_type(d) # revealed: tuple[tuple[str, str], tuple[int, int]]
|
|||
reveal_type(e) # revealed: @Todo(full tuple[...] support)
|
||||
reveal_type(f) # revealed: @Todo(full tuple[...] support)
|
||||
reveal_type(g) # revealed: @Todo(full tuple[...] support)
|
||||
reveal_type(h) # revealed: tuple[@Todo(specialized non-generic class), @Todo(specialized non-generic class)]
|
||||
reveal_type(h) # revealed: tuple[list[int], list[int]]
|
||||
|
||||
reveal_type(i) # revealed: tuple[str | int, str | int]
|
||||
reveal_type(j) # revealed: tuple[str | int]
|
||||
|
@ -76,7 +76,7 @@ a: tuple[()] = (1, 2)
|
|||
# error: [invalid-assignment] "Object of type `tuple[Literal["foo"]]` is not assignable to `tuple[int]`"
|
||||
b: tuple[int] = ("foo",)
|
||||
|
||||
# error: [invalid-assignment] "Object of type `tuple[list, Literal["foo"]]` is not assignable to `tuple[str | int, str]`"
|
||||
# error: [invalid-assignment] "Object of type `tuple[list[Unknown], Literal["foo"]]` is not assignable to `tuple[str | int, str]`"
|
||||
c: tuple[str | int, str] = ([], "foo")
|
||||
```
|
||||
|
||||
|
|
|
@ -1728,7 +1728,7 @@ reveal_type(False.real) # revealed: Literal[0]
|
|||
All attribute access on literal `bytes` types is currently delegated to `builtins.bytes`:
|
||||
|
||||
```py
|
||||
# revealed: bound method Literal[b"foo"].join(iterable_of_bytes: @Todo(specialized non-generic class), /) -> bytes
|
||||
# revealed: bound method Literal[b"foo"].join(iterable_of_bytes: Iterable[@Todo(Support for `typing.TypeAlias`)], /) -> bytes
|
||||
reveal_type(b"foo".join)
|
||||
# revealed: bound method Literal[b"foo"].endswith(suffix: @Todo(Support for `typing.TypeAlias`), start: SupportsIndex | None = ellipsis, end: SupportsIndex | None = ellipsis, /) -> bool
|
||||
reveal_type(b"foo".endswith)
|
||||
|
|
|
@ -79,8 +79,7 @@ lambda x=1: reveal_type(x) # revealed: Unknown | Literal[1]
|
|||
Using a variadic parameter:
|
||||
|
||||
```py
|
||||
# TODO: should be `tuple[Unknown, ...]` (needs generics)
|
||||
lambda *args: reveal_type(args) # revealed: tuple
|
||||
lambda *args: reveal_type(args) # revealed: tuple[Unknown, ...]
|
||||
```
|
||||
|
||||
Using a keyword-variadic parameter:
|
||||
|
|
|
@ -25,8 +25,8 @@ def f(a, b: int, c=1, d: int = 2, /, e=3, f: Literal[4] = 4, *args: object, g=5,
|
|||
reveal_type(f) # revealed: Literal[4]
|
||||
reveal_type(g) # revealed: Unknown | Literal[5]
|
||||
reveal_type(h) # revealed: Literal[6]
|
||||
# TODO: should be `tuple[object, ...]` (needs generics)
|
||||
reveal_type(args) # revealed: tuple
|
||||
# TODO: should be `tuple[object, ...]`
|
||||
reveal_type(args) # revealed: tuple[Unknown, ...]
|
||||
reveal_type(kwargs) # revealed: dict[str, str]
|
||||
```
|
||||
|
||||
|
@ -36,8 +36,7 @@ def f(a, b: int, c=1, d: int = 2, /, e=3, f: Literal[4] = 4, *args: object, g=5,
|
|||
|
||||
```py
|
||||
def g(*args, **kwargs):
|
||||
# TODO: should be `tuple[Unknown, ...]` (needs generics)
|
||||
reveal_type(args) # revealed: tuple
|
||||
reveal_type(args) # revealed: tuple[Unknown, ...]
|
||||
reveal_type(kwargs) # revealed: dict[str, Unknown]
|
||||
```
|
||||
|
||||
|
|
|
@ -399,7 +399,7 @@ In a specialized generic alias, the specialization is applied to the attributes
|
|||
class.
|
||||
|
||||
```py
|
||||
from typing import Generic, TypeVar
|
||||
from typing import Generic, TypeVar, Protocol
|
||||
|
||||
T = TypeVar("T")
|
||||
U = TypeVar("U")
|
||||
|
@ -425,6 +425,33 @@ reveal_type(c.y) # revealed: str
|
|||
reveal_type(c.method1()) # revealed: int
|
||||
reveal_type(c.method2()) # revealed: str
|
||||
reveal_type(c.method3()) # revealed: LinkedList[int]
|
||||
|
||||
class SomeProtocol(Protocol[T]):
|
||||
x: T
|
||||
|
||||
class Foo:
|
||||
x: int
|
||||
|
||||
class D(Generic[T, U]):
|
||||
x: T
|
||||
y: U
|
||||
|
||||
def method1(self) -> T:
|
||||
return self.x
|
||||
|
||||
def method2(self) -> U:
|
||||
return self.y
|
||||
|
||||
def method3(self) -> SomeProtocol[T]:
|
||||
return Foo()
|
||||
|
||||
d = D[int, str]()
|
||||
reveal_type(d.x) # revealed: int
|
||||
reveal_type(d.y) # revealed: str
|
||||
reveal_type(d.method1()) # revealed: int
|
||||
reveal_type(d.method2()) # revealed: str
|
||||
reveal_type(d.method3()) # revealed: SomeProtocol[int]
|
||||
reveal_type(d.method3().x) # revealed: int
|
||||
```
|
||||
|
||||
## Cyclic class definitions
|
||||
|
|
|
@ -38,6 +38,7 @@ T = TypeVar("T")
|
|||
U: TypeVar = TypeVar("U")
|
||||
|
||||
# error: [invalid-legacy-type-variable] "A legacy `typing.TypeVar` must be immediately assigned to a variable"
|
||||
# error: [invalid-type-form] "Function calls are not allowed in type expressions"
|
||||
TestList = list[TypeVar("W")]
|
||||
```
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ from typing import Generic, TypeVar
|
|||
|
||||
T = TypeVar("T")
|
||||
|
||||
# error: [invalid-generic-class] "Cannot both inherit from `Generic` and use PEP 695 type variables"
|
||||
# error: [invalid-generic-class] "Cannot both inherit from `typing.Generic` and use PEP 695 type variables"
|
||||
class BothGenericSyntaxes[U](Generic[T]): ...
|
||||
```
|
||||
|
||||
|
|
|
@ -785,7 +785,7 @@ from subexporter import *
|
|||
|
||||
# TODO: Should be `list[str]`
|
||||
# TODO: Should we avoid including `Unknown` for this case?
|
||||
reveal_type(__all__) # revealed: Unknown | list
|
||||
reveal_type(__all__) # revealed: Unknown | list[Unknown]
|
||||
|
||||
__all__.append("B")
|
||||
|
||||
|
|
|
@ -3,5 +3,5 @@
|
|||
## Empty list
|
||||
|
||||
```py
|
||||
reveal_type([]) # revealed: list
|
||||
reveal_type([]) # revealed: list[Unknown]
|
||||
```
|
||||
|
|
|
@ -3,5 +3,5 @@
|
|||
## Basic set
|
||||
|
||||
```py
|
||||
reveal_type({1, 2}) # revealed: set
|
||||
reveal_type({1, 2}) # revealed: set[Unknown]
|
||||
```
|
||||
|
|
|
@ -48,8 +48,8 @@ alice2 = Person2(1, "Alice")
|
|||
# TODO: should be an error
|
||||
Person2(1)
|
||||
|
||||
reveal_type(alice2.id) # revealed: @Todo(GenericAlias instance)
|
||||
reveal_type(alice2.name) # revealed: @Todo(GenericAlias instance)
|
||||
reveal_type(alice2.id) # revealed: @Todo(functional `NamedTuple` syntax)
|
||||
reveal_type(alice2.name) # revealed: @Todo(functional `NamedTuple` syntax)
|
||||
```
|
||||
|
||||
### Multiple Inheritance
|
||||
|
|
|
@ -133,11 +133,11 @@ match x:
|
|||
case "foo" | 42 | None:
|
||||
reveal_type(x) # revealed: Literal["foo", 42] | None
|
||||
case "foo" | tuple():
|
||||
reveal_type(x) # revealed: tuple
|
||||
reveal_type(x) # revealed: tuple[Unknown, ...]
|
||||
case True | False:
|
||||
reveal_type(x) # revealed: bool
|
||||
case 3.14 | 2.718 | 1.414:
|
||||
reveal_type(x) # revealed: float & ~tuple
|
||||
reveal_type(x) # revealed: float & ~tuple[Unknown, ...]
|
||||
|
||||
reveal_type(x) # revealed: object
|
||||
```
|
||||
|
@ -155,7 +155,7 @@ reveal_type(x) # revealed: object
|
|||
match x:
|
||||
case "foo" | 42 | None if reveal_type(x): # revealed: Literal["foo", 42] | None
|
||||
pass
|
||||
case "foo" | tuple() if reveal_type(x): # revealed: Literal["foo"] | tuple
|
||||
case "foo" | tuple() if reveal_type(x): # revealed: Literal["foo"] | tuple[Unknown, ...]
|
||||
pass
|
||||
case True | False if reveal_type(x): # revealed: bool
|
||||
pass
|
||||
|
|
|
@ -58,6 +58,7 @@ class Bar1(Protocol[T], Generic[T]):
|
|||
class Bar2[T](Protocol):
|
||||
x: T
|
||||
|
||||
# error: [invalid-generic-class] "Cannot both inherit from subscripted `typing.Protocol` and use PEP 695 type variables"
|
||||
class Bar3[T](Protocol[T]):
|
||||
x: T
|
||||
```
|
||||
|
@ -70,8 +71,8 @@ simultaneously:
|
|||
class DuplicateBases(Protocol, Protocol[T]):
|
||||
x: T
|
||||
|
||||
# TODO: should not have `Protocol` multiple times
|
||||
# revealed: tuple[<class 'DuplicateBases'>, typing.Protocol, @Todo(`Protocol[]` subscript), typing.Generic, <class 'object'>]
|
||||
# TODO: should not have `Protocol` or `Generic` multiple times
|
||||
# revealed: tuple[<class 'DuplicateBases[Unknown]'>, typing.Protocol, typing.Generic, typing.Protocol[T], typing.Generic[T], <class 'object'>]
|
||||
reveal_type(DuplicateBases.__mro__)
|
||||
```
|
||||
|
||||
|
|
|
@ -13,8 +13,7 @@ reveal_type(__loader__) # revealed: LoaderProtocol | None
|
|||
reveal_type(__package__) # revealed: str | None
|
||||
reveal_type(__doc__) # revealed: str | None
|
||||
reveal_type(__spec__) # revealed: ModuleSpec | None
|
||||
|
||||
reveal_type(__path__) # revealed: @Todo(specialized non-generic class)
|
||||
reveal_type(__path__) # revealed: MutableSequence[str]
|
||||
|
||||
class X:
|
||||
reveal_type(__name__) # revealed: str
|
||||
|
|
|
@ -9,13 +9,13 @@ A list can be indexed into with:
|
|||
|
||||
```py
|
||||
x = [1, 2, 3]
|
||||
reveal_type(x) # revealed: list
|
||||
reveal_type(x) # revealed: list[Unknown]
|
||||
|
||||
# TODO reveal int
|
||||
reveal_type(x[0]) # revealed: Unknown
|
||||
|
||||
# TODO reveal list
|
||||
reveal_type(x[0:1]) # revealed: @Todo(specialized non-generic class)
|
||||
# TODO reveal list[int]
|
||||
reveal_type(x[0:1]) # revealed: list[Unknown]
|
||||
|
||||
# error: [call-non-callable]
|
||||
reveal_type(x["a"]) # revealed: Unknown
|
||||
|
|
|
@ -83,9 +83,8 @@ python-version = "3.9"
|
|||
```py
|
||||
class A(tuple[int, str]): ...
|
||||
|
||||
# Runtime value: `(A, tuple, object)`
|
||||
# TODO: Generics
|
||||
reveal_type(A.__mro__) # revealed: tuple[<class 'A'>, @Todo(GenericAlias instance), <class 'object'>]
|
||||
# revealed: tuple[<class 'A'>, <class 'tuple[@Todo(Generic tuple specializations), ...]'>, <class 'Sequence[@Todo(Generic tuple specializations)]'>, <class 'Reversible[@Todo(Generic tuple specializations)]'>, <class 'Collection[@Todo(Generic tuple specializations)]'>, <class 'Iterable[@Todo(Generic tuple specializations)]'>, <class 'Container[@Todo(Generic tuple specializations)]'>, typing.Protocol[_T_co], typing.Generic[_T_co], <class 'object'>]
|
||||
reveal_type(A.__mro__)
|
||||
```
|
||||
|
||||
## `typing.Tuple`
|
||||
|
@ -100,7 +99,7 @@ from typing import Any, Tuple
|
|||
class A: ...
|
||||
|
||||
def _(c: Tuple, d: Tuple[int, A], e: Tuple[Any, ...]):
|
||||
reveal_type(c) # revealed: tuple
|
||||
reveal_type(c) # revealed: tuple[Unknown, ...]
|
||||
reveal_type(d) # revealed: tuple[int, A]
|
||||
reveal_type(e) # revealed: @Todo(full tuple[...] support)
|
||||
```
|
||||
|
@ -115,7 +114,6 @@ from typing import Tuple
|
|||
|
||||
class C(Tuple): ...
|
||||
|
||||
# TODO: generic protocols
|
||||
# revealed: tuple[<class 'C'>, <class 'tuple'>, <class 'Sequence'>, <class 'Reversible'>, <class 'Collection'>, <class 'Iterable'>, <class 'Container'>, @Todo(`Protocol[]` subscript), typing.Generic, <class 'object'>]
|
||||
# revealed: tuple[<class 'C'>, <class 'tuple[Unknown, ...]'>, <class 'Sequence[Unknown]'>, <class 'Reversible[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol[_T_co], typing.Generic[_T_co], <class 'object'>]
|
||||
reveal_type(C.__mro__)
|
||||
```
|
||||
|
|
|
@ -151,7 +151,8 @@ static_assert(not is_subtype_of(tuple[B1, B2], tuple[()]))
|
|||
static_assert(not is_subtype_of(tuple[B1, B2], tuple[A1]))
|
||||
static_assert(not is_subtype_of(tuple[B1, B2], tuple[A1, A2, Unrelated]))
|
||||
|
||||
static_assert(is_subtype_of(tuple[int], tuple))
|
||||
# TODO: should pass
|
||||
static_assert(is_subtype_of(tuple[int], tuple[object, ...])) # error: [static-assert-error]
|
||||
```
|
||||
|
||||
## Union types
|
||||
|
|
|
@ -6,14 +6,20 @@ cpython # access to field whilst being initialized, too many cycle iterations
|
|||
discord.py # some kind of hang, only when multi-threaded?
|
||||
freqtrade # hangs
|
||||
hydpy # too many iterations
|
||||
ibis # too many iterations
|
||||
jax # too many iterations
|
||||
packaging # too many iterations
|
||||
pandas # slow
|
||||
pandas-stubs # hangs/slow, or else https://github.com/salsa-rs/salsa/issues/831
|
||||
pandera # stack overflow
|
||||
pip # vendors packaging, see above
|
||||
prefect # slow
|
||||
pylint # cycle panics (self-recursive type alias)
|
||||
pyodide # too many cycle iterations
|
||||
pywin32 # bad use-def map (binding with definitely-visible unbound)
|
||||
schemathesis # https://github.com/salsa-rs/salsa/issues/831
|
||||
scikit-learn # success, but mypy-primer hangs processing the output
|
||||
setuptools # vendors packaging, see above
|
||||
spack # success, but mypy-primer hangs processing the output
|
||||
spark # too many iterations
|
||||
steam.py # hangs
|
||||
|
|
|
@ -42,13 +42,11 @@ git-revise
|
|||
graphql-core
|
||||
httpx-caching
|
||||
hydra-zen
|
||||
ibis
|
||||
ignite
|
||||
imagehash
|
||||
isort
|
||||
itsdangerous
|
||||
janus
|
||||
jax
|
||||
jinja
|
||||
koda-validate
|
||||
kopf
|
||||
|
@ -70,11 +68,9 @@ openlibrary
|
|||
operator
|
||||
optuna
|
||||
paasta
|
||||
packaging
|
||||
paroxython
|
||||
parso
|
||||
pegen
|
||||
pip
|
||||
poetry
|
||||
porcupine
|
||||
ppb-vector
|
||||
|
@ -86,7 +82,6 @@ pydantic
|
|||
pyinstrument
|
||||
pyjwt
|
||||
pylox
|
||||
pyodide
|
||||
pyp
|
||||
pyppeteer
|
||||
pytest
|
||||
|
@ -100,7 +95,6 @@ rotki
|
|||
schema_salad
|
||||
scipy
|
||||
scrapy
|
||||
setuptools
|
||||
sockeye
|
||||
speedrun.com_global_scoreboard_webapp
|
||||
sphinx
|
||||
|
|
|
@ -662,7 +662,7 @@ impl<'db> Type<'db> {
|
|||
|
||||
pub fn contains_todo(&self, db: &'db dyn Db) -> bool {
|
||||
match self {
|
||||
Self::Dynamic(DynamicType::Todo(_) | DynamicType::SubscriptedProtocol) => true,
|
||||
Self::Dynamic(DynamicType::Todo(_)) => true,
|
||||
|
||||
Self::AlwaysFalsy
|
||||
| Self::AlwaysTruthy
|
||||
|
@ -703,9 +703,7 @@ impl<'db> Type<'db> {
|
|||
}
|
||||
|
||||
Self::SubclassOf(subclass_of) => match subclass_of.subclass_of() {
|
||||
SubclassOfInner::Dynamic(
|
||||
DynamicType::Todo(_) | DynamicType::SubscriptedProtocol,
|
||||
) => true,
|
||||
SubclassOfInner::Dynamic(DynamicType::Todo(_)) => true,
|
||||
SubclassOfInner::Dynamic(DynamicType::Unknown | DynamicType::Any) => false,
|
||||
SubclassOfInner::Class(_) => false,
|
||||
},
|
||||
|
@ -722,12 +720,10 @@ impl<'db> Type<'db> {
|
|||
Self::BoundSuper(bound_super) => {
|
||||
matches!(
|
||||
bound_super.pivot_class(db),
|
||||
ClassBase::Dynamic(DynamicType::Todo(_) | DynamicType::SubscriptedProtocol)
|
||||
ClassBase::Dynamic(DynamicType::Todo(_))
|
||||
) || matches!(
|
||||
bound_super.owner(db),
|
||||
SuperOwnerKind::Dynamic(
|
||||
DynamicType::Todo(_) | DynamicType::SubscriptedProtocol
|
||||
)
|
||||
SuperOwnerKind::Dynamic(DynamicType::Todo(_))
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -3939,6 +3935,9 @@ impl<'db> Type<'db> {
|
|||
);
|
||||
Signatures::single(signature)
|
||||
}
|
||||
Some(KnownClass::NamedTuple) => {
|
||||
Signatures::single(CallableSignature::todo("functional `NamedTuple` syntax"))
|
||||
}
|
||||
Some(KnownClass::Object) => {
|
||||
// ```py
|
||||
// class object:
|
||||
|
@ -4328,6 +4327,12 @@ impl<'db> Type<'db> {
|
|||
return Ok(UnionType::from_elements(db, tuple_type.elements(db)));
|
||||
}
|
||||
|
||||
if let Type::GenericAlias(alias) = self {
|
||||
if alias.origin(db).is_known(db, KnownClass::Tuple) {
|
||||
return Ok(todo_type!("*tuple[] annotations"));
|
||||
}
|
||||
}
|
||||
|
||||
let try_call_dunder_getitem = || {
|
||||
self.try_call_dunder(
|
||||
db,
|
||||
|
@ -4826,7 +4831,7 @@ impl<'db> Type<'db> {
|
|||
KnownInstanceType::TypeAlias => Ok(todo_type!("Support for `typing.TypeAlias`")),
|
||||
KnownInstanceType::TypedDict => Ok(todo_type!("Support for `typing.TypedDict`")),
|
||||
|
||||
KnownInstanceType::Protocol => Err(InvalidTypeExpressionError {
|
||||
KnownInstanceType::Protocol(_) => Err(InvalidTypeExpressionError {
|
||||
invalid_expressions: smallvec::smallvec![InvalidTypeExpression::Protocol],
|
||||
fallback_type: Type::unknown(),
|
||||
}),
|
||||
|
@ -4931,9 +4936,6 @@ impl<'db> Type<'db> {
|
|||
Some(KnownClass::UnionType) => Ok(todo_type!(
|
||||
"Support for `types.UnionType` instances in type expressions"
|
||||
)),
|
||||
Some(KnownClass::NamedTuple) => Ok(todo_type!(
|
||||
"Support for functional `typing.NamedTuple` syntax"
|
||||
)),
|
||||
_ => Err(InvalidTypeExpressionError {
|
||||
invalid_expressions: smallvec::smallvec![InvalidTypeExpression::InvalidType(
|
||||
*self
|
||||
|
@ -5093,6 +5095,10 @@ impl<'db> Type<'db> {
|
|||
instance.apply_type_mapping(db, type_mapping),
|
||||
),
|
||||
|
||||
Type::ProtocolInstance(instance) => {
|
||||
Type::ProtocolInstance(instance.apply_specialization(db, type_mapping))
|
||||
}
|
||||
|
||||
Type::MethodWrapper(MethodWrapperKind::FunctionTypeDunderGet(function)) => {
|
||||
Type::MethodWrapper(MethodWrapperKind::FunctionTypeDunderGet(
|
||||
function.apply_type_mapping(db, type_mapping),
|
||||
|
@ -5176,8 +5182,6 @@ impl<'db> Type<'db> {
|
|||
| Type::StringLiteral(_)
|
||||
| Type::BytesLiteral(_)
|
||||
| Type::BoundSuper(_)
|
||||
// Same for `ProtocolInstance`
|
||||
| Type::ProtocolInstance(_)
|
||||
| Type::KnownInstance(_) => self,
|
||||
}
|
||||
}
|
||||
|
@ -5498,9 +5502,6 @@ pub enum DynamicType {
|
|||
///
|
||||
/// This variant should be created with the `todo_type!` macro.
|
||||
Todo(TodoType),
|
||||
/// Temporary type until we support generic protocols.
|
||||
/// We use a separate variant (instead of `Todo(…)`) in order to be able to match on them explicitly.
|
||||
SubscriptedProtocol,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DynamicType {
|
||||
|
@ -5511,11 +5512,6 @@ impl std::fmt::Display for DynamicType {
|
|||
// `DynamicType::Todo`'s display should be explicit that is not a valid display of
|
||||
// any other type
|
||||
DynamicType::Todo(todo) => write!(f, "@Todo{todo}"),
|
||||
DynamicType::SubscriptedProtocol => f.write_str(if cfg!(debug_assertions) {
|
||||
"@Todo(`Protocol[]` subscript)"
|
||||
} else {
|
||||
"@Todo"
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -312,7 +312,7 @@ impl<'db> ClassType<'db> {
|
|||
ClassBase::Dynamic(_) => false,
|
||||
|
||||
// Protocol and Generic are not represented by a ClassType.
|
||||
ClassBase::Protocol | ClassBase::Generic(_) => false,
|
||||
ClassBase::Protocol(_) | ClassBase::Generic(_) => false,
|
||||
|
||||
ClassBase::Class(base) => match (base, other) {
|
||||
(ClassType::NonGeneric(base), ClassType::NonGeneric(other)) => base == other,
|
||||
|
@ -350,7 +350,7 @@ impl<'db> ClassType<'db> {
|
|||
ClassBase::Dynamic(_) => false,
|
||||
|
||||
// Protocol and Generic are not represented by a ClassType.
|
||||
ClassBase::Protocol | ClassBase::Generic(_) => false,
|
||||
ClassBase::Protocol(_) | ClassBase::Generic(_) => false,
|
||||
|
||||
ClassBase::Class(base) => match (base, other) {
|
||||
(ClassType::NonGeneric(base), ClassType::NonGeneric(other)) => base == other,
|
||||
|
@ -536,7 +536,10 @@ impl<'db> ClassLiteral<'db> {
|
|||
|
||||
pub(crate) fn legacy_generic_context(self, db: &'db dyn Db) -> Option<GenericContext<'db>> {
|
||||
self.explicit_bases(db).iter().find_map(|base| match base {
|
||||
Type::KnownInstance(KnownInstanceType::Generic(generic_context)) => *generic_context,
|
||||
Type::KnownInstance(
|
||||
KnownInstanceType::Generic(generic_context)
|
||||
| KnownInstanceType::Protocol(generic_context),
|
||||
) => *generic_context,
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
@ -608,6 +611,17 @@ impl<'db> ClassLiteral<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns a specialization of this class with a `@Todo`-type
|
||||
pub(crate) fn todo_specialization(self, db: &'db dyn Db, todo: &'static str) -> ClassType<'db> {
|
||||
match self.generic_context(db) {
|
||||
None => ClassType::NonGeneric(self),
|
||||
Some(generic_context) => {
|
||||
let specialization = generic_context.todo_specialization(db, todo);
|
||||
ClassType::Generic(GenericAlias::new(db, self, specialization))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the unknown specialization of this class. For non-generic classes, the class is
|
||||
/// returned unchanged. For a non-specialized generic class, we return a generic alias that
|
||||
/// maps each of the class's typevars to `Unknown`.
|
||||
|
@ -678,13 +692,11 @@ impl<'db> ClassLiteral<'db> {
|
|||
// - OR be the last-but-one base (with the final base being `Generic[]` or `object`)
|
||||
// - OR be the last-but-two base (with the penultimate base being `Generic[]`
|
||||
// and the final base being `object`)
|
||||
self.explicit_bases(db).iter().rev().take(3).any(|base| {
|
||||
matches!(
|
||||
base,
|
||||
Type::KnownInstance(KnownInstanceType::Protocol)
|
||||
| Type::Dynamic(DynamicType::SubscriptedProtocol)
|
||||
)
|
||||
})
|
||||
self.explicit_bases(db)
|
||||
.iter()
|
||||
.rev()
|
||||
.take(3)
|
||||
.any(|base| matches!(base, Type::KnownInstance(KnownInstanceType::Protocol(_))))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1011,12 +1023,8 @@ impl<'db> ClassLiteral<'db> {
|
|||
|
||||
for superclass in mro_iter {
|
||||
match superclass {
|
||||
ClassBase::Dynamic(DynamicType::SubscriptedProtocol)
|
||||
| ClassBase::Generic(_)
|
||||
| ClassBase::Protocol => {
|
||||
// TODO: We currently skip `Protocol` when looking up class members, in order to
|
||||
// avoid creating many dynamic types in our test suite that would otherwise
|
||||
// result from looking up attributes on builtin types like `str`, `list`, `tuple`
|
||||
ClassBase::Generic(_) | ClassBase::Protocol(_) => {
|
||||
// Skip over these very special class bases that aren't really classes.
|
||||
}
|
||||
ClassBase::Dynamic(_) => {
|
||||
// Note: calling `Type::from(superclass).member()` would be incorrect here.
|
||||
|
@ -1354,12 +1362,8 @@ impl<'db> ClassLiteral<'db> {
|
|||
|
||||
for superclass in self.iter_mro(db, specialization) {
|
||||
match superclass {
|
||||
ClassBase::Dynamic(DynamicType::SubscriptedProtocol)
|
||||
| ClassBase::Generic(_)
|
||||
| ClassBase::Protocol => {
|
||||
// TODO: We currently skip these when looking up instance members, in order to
|
||||
// avoid creating many dynamic types in our test suite that would otherwise
|
||||
// result from looking up attributes on builtin types like `str`, `list`, `tuple`
|
||||
ClassBase::Generic(_) | ClassBase::Protocol(_) => {
|
||||
// Skip over these very special class bases that aren't really classes.
|
||||
}
|
||||
ClassBase::Dynamic(_) => {
|
||||
return SymbolAndQualifiers::todo(
|
||||
|
|
|
@ -18,7 +18,7 @@ pub enum ClassBase<'db> {
|
|||
Class(ClassType<'db>),
|
||||
/// Although `Protocol` is not a class in typeshed's stubs, it is at runtime,
|
||||
/// and can appear in the MRO of a class.
|
||||
Protocol,
|
||||
Protocol(Option<GenericContext<'db>>),
|
||||
/// Bare `Generic` cannot be subclassed directly in user code,
|
||||
/// but nonetheless appears in the MRO of classes that inherit from `Generic[T]`,
|
||||
/// `Protocol[T]`, or bare `Protocol`.
|
||||
|
@ -50,11 +50,17 @@ impl<'db> ClassBase<'db> {
|
|||
ClassBase::Class(ClassType::Generic(alias)) => {
|
||||
write!(f, "<class '{}'>", alias.display(self.db))
|
||||
}
|
||||
ClassBase::Protocol => f.write_str("typing.Protocol"),
|
||||
ClassBase::Protocol(generic_context) => {
|
||||
f.write_str("typing.Protocol")?;
|
||||
if let Some(generic_context) = generic_context {
|
||||
generic_context.display(self.db).fmt(f)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
ClassBase::Generic(generic_context) => {
|
||||
f.write_str("typing.Generic")?;
|
||||
if let Some(generic_context) = generic_context {
|
||||
write!(f, "{}", generic_context.display(self.db))?;
|
||||
generic_context.display(self.db).fmt(f)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -71,9 +77,7 @@ impl<'db> ClassBase<'db> {
|
|||
ClassBase::Dynamic(DynamicType::Any) => "Any",
|
||||
ClassBase::Dynamic(DynamicType::Unknown) => "Unknown",
|
||||
ClassBase::Dynamic(DynamicType::Todo(_)) => "@Todo",
|
||||
ClassBase::Protocol | ClassBase::Dynamic(DynamicType::SubscriptedProtocol) => {
|
||||
"Protocol"
|
||||
}
|
||||
ClassBase::Protocol(_) => "Protocol",
|
||||
ClassBase::Generic(_) => "Generic",
|
||||
}
|
||||
}
|
||||
|
@ -199,7 +203,9 @@ impl<'db> ClassBase<'db> {
|
|||
KnownInstanceType::Callable => {
|
||||
Self::try_from_type(db, todo_type!("Support for Callable as a base class"))
|
||||
}
|
||||
KnownInstanceType::Protocol => Some(ClassBase::Protocol),
|
||||
KnownInstanceType::Protocol(generic_context) => {
|
||||
Some(ClassBase::Protocol(generic_context))
|
||||
}
|
||||
KnownInstanceType::Generic(generic_context) => {
|
||||
Some(ClassBase::Generic(generic_context))
|
||||
}
|
||||
|
@ -210,14 +216,14 @@ impl<'db> ClassBase<'db> {
|
|||
pub(super) fn into_class(self) -> Option<ClassType<'db>> {
|
||||
match self {
|
||||
Self::Class(class) => Some(class),
|
||||
Self::Dynamic(_) | Self::Generic(_) | Self::Protocol => None,
|
||||
Self::Dynamic(_) | Self::Generic(_) | Self::Protocol(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_type_mapping<'a>(self, db: &'db dyn Db, type_mapping: TypeMapping<'a, 'db>) -> Self {
|
||||
match self {
|
||||
Self::Class(class) => Self::Class(class.apply_type_mapping(db, type_mapping)),
|
||||
Self::Dynamic(_) | Self::Generic(_) | Self::Protocol => self,
|
||||
Self::Dynamic(_) | Self::Generic(_) | Self::Protocol(_) => self,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,7 +247,7 @@ impl<'db> ClassBase<'db> {
|
|||
.try_mro(db, specialization)
|
||||
.is_err_and(MroError::is_cycle)
|
||||
}
|
||||
ClassBase::Dynamic(_) | ClassBase::Generic(_) | ClassBase::Protocol => false,
|
||||
ClassBase::Dynamic(_) | ClassBase::Generic(_) | ClassBase::Protocol(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -252,11 +258,8 @@ impl<'db> ClassBase<'db> {
|
|||
additional_specialization: Option<Specialization<'db>>,
|
||||
) -> impl Iterator<Item = ClassBase<'db>> {
|
||||
match self {
|
||||
ClassBase::Protocol => {
|
||||
ClassBaseMroIterator::length_3(db, self, ClassBase::Generic(None))
|
||||
}
|
||||
ClassBase::Dynamic(DynamicType::SubscriptedProtocol) => {
|
||||
ClassBaseMroIterator::length_3(db, self, ClassBase::Generic(None))
|
||||
ClassBase::Protocol(context) => {
|
||||
ClassBaseMroIterator::length_3(db, self, ClassBase::Generic(context))
|
||||
}
|
||||
ClassBase::Dynamic(_) | ClassBase::Generic(_) => {
|
||||
ClassBaseMroIterator::length_2(db, self)
|
||||
|
@ -279,7 +282,9 @@ impl<'db> From<ClassBase<'db>> for Type<'db> {
|
|||
match value {
|
||||
ClassBase::Dynamic(dynamic) => Type::Dynamic(dynamic),
|
||||
ClassBase::Class(class) => class.into(),
|
||||
ClassBase::Protocol => Type::KnownInstance(KnownInstanceType::Protocol),
|
||||
ClassBase::Protocol(generic_context) => {
|
||||
Type::KnownInstance(KnownInstanceType::Protocol(generic_context))
|
||||
}
|
||||
ClassBase::Generic(generic_context) => {
|
||||
Type::KnownInstance(KnownInstanceType::Generic(generic_context))
|
||||
}
|
||||
|
|
|
@ -174,7 +174,9 @@ impl Display for DisplayRepresentation<'_> {
|
|||
function = function.name(self.db),
|
||||
specialization = if let Some(specialization) = function.specialization(self.db)
|
||||
{
|
||||
specialization.display_short(self.db).to_string()
|
||||
specialization
|
||||
.display_short(self.db, TupleSpecialization::No)
|
||||
.to_string()
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
|
@ -187,7 +189,9 @@ impl Display for DisplayRepresentation<'_> {
|
|||
function = function.name(self.db),
|
||||
specialization = if let Some(specialization) = function.specialization(self.db)
|
||||
{
|
||||
specialization.display_short(self.db).to_string()
|
||||
specialization
|
||||
.display_short(self.db, TupleSpecialization::No)
|
||||
.to_string()
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
|
@ -274,7 +278,10 @@ impl Display for DisplayGenericAlias<'_> {
|
|||
f,
|
||||
"{origin}{specialization}",
|
||||
origin = self.origin.name(self.db),
|
||||
specialization = self.specialization.display_short(self.db),
|
||||
specialization = self.specialization.display_short(
|
||||
self.db,
|
||||
TupleSpecialization::from_class(self.db, self.origin)
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -327,22 +334,32 @@ impl Display for DisplayGenericContext<'_> {
|
|||
|
||||
impl<'db> Specialization<'db> {
|
||||
/// Renders the specialization in full, e.g. `{T = int, U = str}`.
|
||||
pub fn display(&'db self, db: &'db dyn Db) -> DisplaySpecialization<'db> {
|
||||
pub fn display(
|
||||
&'db self,
|
||||
db: &'db dyn Db,
|
||||
tuple_specialization: TupleSpecialization,
|
||||
) -> DisplaySpecialization<'db> {
|
||||
DisplaySpecialization {
|
||||
typevars: self.generic_context(db).variables(db),
|
||||
types: self.types(db),
|
||||
db,
|
||||
full: true,
|
||||
tuple_specialization,
|
||||
}
|
||||
}
|
||||
|
||||
/// Renders the specialization as it would appear in a subscript expression, e.g. `[int, str]`.
|
||||
pub fn display_short(&'db self, db: &'db dyn Db) -> DisplaySpecialization<'db> {
|
||||
pub fn display_short(
|
||||
&'db self,
|
||||
db: &'db dyn Db,
|
||||
tuple_specialization: TupleSpecialization,
|
||||
) -> DisplaySpecialization<'db> {
|
||||
DisplaySpecialization {
|
||||
typevars: self.generic_context(db).variables(db),
|
||||
types: self.types(db),
|
||||
db,
|
||||
full: false,
|
||||
tuple_specialization,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -352,6 +369,7 @@ pub struct DisplaySpecialization<'db> {
|
|||
types: &'db [Type<'db>],
|
||||
db: &'db dyn Db,
|
||||
full: bool,
|
||||
tuple_specialization: TupleSpecialization,
|
||||
}
|
||||
|
||||
impl Display for DisplaySpecialization<'_> {
|
||||
|
@ -373,11 +391,34 @@ impl Display for DisplaySpecialization<'_> {
|
|||
}
|
||||
ty.display(self.db).fmt(f)?;
|
||||
}
|
||||
if self.tuple_specialization.is_yes() {
|
||||
f.write_str(", ...")?;
|
||||
}
|
||||
f.write_char(']')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum TupleSpecialization {
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
|
||||
impl TupleSpecialization {
|
||||
const fn is_yes(self) -> bool {
|
||||
matches!(self, Self::Yes)
|
||||
}
|
||||
|
||||
fn from_class(db: &dyn Db, class: ClassLiteral) -> Self {
|
||||
if class.is_known(db, KnownClass::Tuple) {
|
||||
Self::Yes
|
||||
} else {
|
||||
Self::No
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> CallableType<'db> {
|
||||
pub(crate) fn display(&'db self, db: &'db dyn Db) -> DisplayCallableType<'db> {
|
||||
DisplayCallableType {
|
||||
|
|
|
@ -4,8 +4,8 @@ use rustc_hash::FxHashMap;
|
|||
use crate::semantic_index::SemanticIndex;
|
||||
use crate::types::signatures::{Parameter, Parameters, Signature};
|
||||
use crate::types::{
|
||||
declaration_type, KnownInstanceType, Type, TypeVarBoundOrConstraints, TypeVarInstance,
|
||||
TypeVarVariance, UnionType,
|
||||
declaration_type, todo_type, KnownInstanceType, Type, TypeVarBoundOrConstraints,
|
||||
TypeVarInstance, TypeVarVariance, UnionType,
|
||||
};
|
||||
use crate::{Db, FxOrderSet};
|
||||
|
||||
|
@ -17,6 +17,7 @@ use crate::{Db, FxOrderSet};
|
|||
pub struct GenericContext<'db> {
|
||||
#[returns(ref)]
|
||||
pub(crate) variables: FxOrderSet<TypeVarInstance<'db>>,
|
||||
pub(crate) origin: GenericContextOrigin,
|
||||
}
|
||||
|
||||
impl<'db> GenericContext<'db> {
|
||||
|
@ -30,7 +31,7 @@ impl<'db> GenericContext<'db> {
|
|||
.iter()
|
||||
.filter_map(|type_param| Self::variable_from_type_param(db, index, type_param))
|
||||
.collect();
|
||||
Self::new(db, variables)
|
||||
Self::new(db, variables, GenericContextOrigin::TypeParameterList)
|
||||
}
|
||||
|
||||
fn variable_from_type_param(
|
||||
|
@ -76,7 +77,11 @@ impl<'db> GenericContext<'db> {
|
|||
if variables.is_empty() {
|
||||
return None;
|
||||
}
|
||||
Some(Self::new(db, variables))
|
||||
Some(Self::new(
|
||||
db,
|
||||
variables,
|
||||
GenericContextOrigin::LegacyGenericFunction,
|
||||
))
|
||||
}
|
||||
|
||||
/// Creates a generic context from the legacy `TypeVar`s that appear in class's base class
|
||||
|
@ -92,7 +97,7 @@ impl<'db> GenericContext<'db> {
|
|||
if variables.is_empty() {
|
||||
return None;
|
||||
}
|
||||
Some(Self::new(db, variables))
|
||||
Some(Self::new(db, variables, GenericContextOrigin::Inherited))
|
||||
}
|
||||
|
||||
pub(crate) fn len(self, db: &'db dyn Db) -> usize {
|
||||
|
@ -133,6 +138,20 @@ impl<'db> GenericContext<'db> {
|
|||
self.specialize_partial(db, &vec![None; self.variables(db).len()])
|
||||
}
|
||||
|
||||
#[allow(unused_variables)] // Only unused in release builds
|
||||
pub(crate) fn todo_specialization(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
todo: &'static str,
|
||||
) -> Specialization<'db> {
|
||||
let types = self
|
||||
.variables(db)
|
||||
.iter()
|
||||
.map(|typevar| typevar.default_ty(db).unwrap_or(todo_type!(todo)))
|
||||
.collect();
|
||||
self.specialize(db, types)
|
||||
}
|
||||
|
||||
pub(crate) fn identity_specialization(self, db: &'db dyn Db) -> Specialization<'db> {
|
||||
let types = self
|
||||
.variables(db)
|
||||
|
@ -209,6 +228,58 @@ impl<'db> GenericContext<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum GenericContextOrigin {
|
||||
LegacyBase(LegacyGenericBase),
|
||||
Inherited,
|
||||
LegacyGenericFunction,
|
||||
TypeParameterList,
|
||||
}
|
||||
|
||||
impl GenericContextOrigin {
|
||||
pub(crate) const fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::LegacyBase(base) => base.as_str(),
|
||||
Self::Inherited => "inherited",
|
||||
Self::LegacyGenericFunction => "legacy generic function",
|
||||
Self::TypeParameterList => "type parameter list",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for GenericContextOrigin {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum LegacyGenericBase {
|
||||
Generic,
|
||||
Protocol,
|
||||
}
|
||||
|
||||
impl LegacyGenericBase {
|
||||
pub(crate) const fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Generic => "`typing.Generic`",
|
||||
Self::Protocol => "subscripted `typing.Protocol`",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for LegacyGenericBase {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LegacyGenericBase> for GenericContextOrigin {
|
||||
fn from(base: LegacyGenericBase) -> Self {
|
||||
Self::LegacyBase(base)
|
||||
}
|
||||
}
|
||||
|
||||
/// An assignment of a specific type to each type variable in a generic scope.
|
||||
///
|
||||
/// TODO: Handle nested specializations better, with actual parent links to the specialization of
|
||||
|
|
|
@ -107,6 +107,7 @@ use super::diagnostic::{
|
|||
report_unresolved_reference, INVALID_METACLASS, INVALID_OVERLOAD, INVALID_PROTOCOL,
|
||||
REDUNDANT_CAST, STATIC_ASSERT_ERROR, SUBCLASS_OF_FINAL_CLASS, TYPE_ASSERTION_FAILURE,
|
||||
};
|
||||
use super::generics::{GenericContextOrigin, LegacyGenericBase};
|
||||
use super::slots::check_class_slots;
|
||||
use super::string_annotation::{
|
||||
parse_string_annotation, BYTE_STRING_TYPE_ANNOTATION, FSTRING_TYPE_ANNOTATION,
|
||||
|
@ -1020,14 +1021,16 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
|
||||
// (5) Check that a generic class does not have invalid or conflicting generic
|
||||
// contexts.
|
||||
if class.pep695_generic_context(self.db()).is_some()
|
||||
&& class.legacy_generic_context(self.db()).is_some()
|
||||
{
|
||||
if let Some(builder) = self.context.report_lint(&INVALID_GENERIC_CLASS, class_node)
|
||||
{
|
||||
builder.into_diagnostic(
|
||||
"Cannot both inherit from `Generic` and use PEP 695 type variables",
|
||||
);
|
||||
if class.pep695_generic_context(self.db()).is_some() {
|
||||
if let Some(legacy_context) = class.legacy_generic_context(self.db()) {
|
||||
if let Some(builder) =
|
||||
self.context.report_lint(&INVALID_GENERIC_CLASS, class_node)
|
||||
{
|
||||
builder.into_diagnostic(format_args!(
|
||||
"Cannot both inherit from {} and use PEP 695 type variables",
|
||||
legacy_context.origin(self.db())
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4734,6 +4737,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
| KnownClass::Property
|
||||
| KnownClass::Super
|
||||
| KnownClass::TypeVar
|
||||
| KnownClass::NamedTuple
|
||||
)
|
||||
)
|
||||
// temporary special-casing for all subclasses of `enum.Enum`
|
||||
|
@ -5795,8 +5799,6 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
| (_, unknown @ Type::Dynamic(DynamicType::Unknown), _) => Some(unknown),
|
||||
(todo @ Type::Dynamic(DynamicType::Todo(_)), _, _)
|
||||
| (_, todo @ Type::Dynamic(DynamicType::Todo(_)), _) => Some(todo),
|
||||
(todo @ Type::Dynamic(DynamicType::SubscriptedProtocol), _, _)
|
||||
| (_, todo @ Type::Dynamic(DynamicType::SubscriptedProtocol), _) => Some(todo),
|
||||
(Type::Never, _, _) | (_, Type::Never, _) => Some(Type::Never),
|
||||
|
||||
(Type::IntLiteral(n), Type::IntLiteral(m), ast::Operator::Add) => Some(
|
||||
|
@ -6884,6 +6886,13 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
// special cases, too.
|
||||
let value_ty = self.infer_expression(value);
|
||||
if let Type::ClassLiteral(class) = value_ty {
|
||||
if class.is_known(self.db(), KnownClass::Tuple) {
|
||||
self.infer_expression(slice);
|
||||
// TODO heterogeneous and homogeneous tuples in value expressions
|
||||
return Type::from(
|
||||
class.todo_specialization(self.db(), "Generic tuple specializations"),
|
||||
);
|
||||
}
|
||||
if let Some(generic_context) = class.generic_context(self.db()) {
|
||||
return self.infer_explicit_class_specialization(
|
||||
subscript,
|
||||
|
@ -7072,14 +7081,44 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
value_ty,
|
||||
Type::IntLiteral(i64::from(bool)),
|
||||
),
|
||||
(Type::KnownInstance(KnownInstanceType::Protocol), _, _) => {
|
||||
Type::Dynamic(DynamicType::SubscriptedProtocol)
|
||||
(Type::KnownInstance(KnownInstanceType::Protocol(None)), Type::Tuple(typevars), _) => {
|
||||
self.legacy_generic_class_context(
|
||||
value_node,
|
||||
typevars.elements(self.db()),
|
||||
LegacyGenericBase::Protocol,
|
||||
)
|
||||
.map(|context| Type::KnownInstance(KnownInstanceType::Protocol(Some(context))))
|
||||
.unwrap_or_else(Type::unknown)
|
||||
}
|
||||
(Type::KnownInstance(KnownInstanceType::Protocol(None)), typevar, _) => self
|
||||
.legacy_generic_class_context(
|
||||
value_node,
|
||||
std::slice::from_ref(&typevar),
|
||||
LegacyGenericBase::Protocol,
|
||||
)
|
||||
.map(|context| Type::KnownInstance(KnownInstanceType::Protocol(Some(context))))
|
||||
.unwrap_or_else(Type::unknown),
|
||||
(Type::KnownInstance(KnownInstanceType::Protocol(Some(_))), _, _) => {
|
||||
// TODO: emit a diagnostic
|
||||
todo_type!("doubly-specialized typing.Protocol")
|
||||
}
|
||||
(Type::KnownInstance(KnownInstanceType::Generic(None)), Type::Tuple(typevars), _) => {
|
||||
self.infer_subscript_legacy_generic_class(value_node, typevars.elements(self.db()))
|
||||
self.legacy_generic_class_context(
|
||||
value_node,
|
||||
typevars.elements(self.db()),
|
||||
LegacyGenericBase::Generic,
|
||||
)
|
||||
.map(|context| Type::KnownInstance(KnownInstanceType::Generic(Some(context))))
|
||||
.unwrap_or_else(Type::unknown)
|
||||
}
|
||||
(Type::KnownInstance(KnownInstanceType::Generic(None)), typevar, _) => self
|
||||
.infer_subscript_legacy_generic_class(value_node, std::slice::from_ref(&typevar)),
|
||||
.legacy_generic_class_context(
|
||||
value_node,
|
||||
std::slice::from_ref(&typevar),
|
||||
LegacyGenericBase::Generic,
|
||||
)
|
||||
.map(|context| Type::KnownInstance(KnownInstanceType::Generic(Some(context))))
|
||||
.unwrap_or_else(Type::unknown),
|
||||
(Type::KnownInstance(KnownInstanceType::Generic(Some(_))), _, _) => {
|
||||
// TODO: emit a diagnostic
|
||||
todo_type!("doubly-specialized typing.Generic")
|
||||
|
@ -7238,11 +7277,12 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
fn infer_subscript_legacy_generic_class(
|
||||
fn legacy_generic_class_context(
|
||||
&mut self,
|
||||
value_node: &ast::Expr,
|
||||
typevars: &[Type<'db>],
|
||||
) -> Type<'db> {
|
||||
origin: LegacyGenericBase,
|
||||
) -> Option<GenericContext<'db>> {
|
||||
let typevars: Option<FxOrderSet<_>> = typevars
|
||||
.iter()
|
||||
.map(|typevar| match typevar {
|
||||
|
@ -7252,7 +7292,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
self.context.report_lint(&INVALID_ARGUMENT_TYPE, value_node)
|
||||
{
|
||||
builder.into_diagnostic(format_args!(
|
||||
"`{}` is not a valid argument to `typing.Generic`",
|
||||
"`{}` is not a valid argument to {origin}",
|
||||
typevar.display(self.db()),
|
||||
));
|
||||
}
|
||||
|
@ -7260,11 +7300,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
}
|
||||
})
|
||||
.collect();
|
||||
let Some(typevars) = typevars else {
|
||||
return Type::unknown();
|
||||
};
|
||||
let generic_context = GenericContext::new(self.db(), typevars);
|
||||
Type::KnownInstance(KnownInstanceType::Generic(Some(generic_context)))
|
||||
typevars.map(|typevars| {
|
||||
GenericContext::new(self.db(), typevars, GenericContextOrigin::from(origin))
|
||||
})
|
||||
}
|
||||
|
||||
fn infer_slice_expression(&mut self, slice: &ast::ExprSlice) -> Type<'db> {
|
||||
|
@ -8420,9 +8458,14 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
self.infer_type_expression(arguments_slice);
|
||||
todo_type!("`Unpack[]` special form")
|
||||
}
|
||||
KnownInstanceType::Protocol => {
|
||||
KnownInstanceType::Protocol(_) => {
|
||||
self.infer_type_expression(arguments_slice);
|
||||
Type::Dynamic(DynamicType::SubscriptedProtocol)
|
||||
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) {
|
||||
builder.into_diagnostic(format_args!(
|
||||
"`typing.Protocol` is not allowed in type expressions",
|
||||
));
|
||||
}
|
||||
Type::unknown()
|
||||
}
|
||||
KnownInstanceType::Generic(_) => {
|
||||
self.infer_type_expression(arguments_slice);
|
||||
|
|
|
@ -260,6 +260,21 @@ impl<'db> ProtocolInstanceType<'db> {
|
|||
.unwrap_or_else(|| KnownClass::Object.to_instance(db).instance_member(db, name)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn apply_specialization<'a>(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
type_mapping: TypeMapping<'a, 'db>,
|
||||
) -> Self {
|
||||
match self.0 {
|
||||
Protocol::FromClass(class) => Self(Protocol::FromClass(
|
||||
class.apply_type_mapping(db, type_mapping),
|
||||
)),
|
||||
Protocol::Synthesized(synthesized) => Self(Protocol::Synthesized(
|
||||
synthesized.apply_type_mapping(db, type_mapping),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An enumeration of the two kinds of protocol types: those that originate from a class
|
||||
|
@ -287,6 +302,7 @@ impl<'db> Protocol<'db> {
|
|||
|
||||
mod synthesized_protocol {
|
||||
use crate::db::Db;
|
||||
use crate::types::generics::TypeMapping;
|
||||
use crate::types::protocol_class::ProtocolInterface;
|
||||
|
||||
/// A "synthesized" protocol type that is dissociated from a class definition in source code.
|
||||
|
@ -306,6 +322,14 @@ mod synthesized_protocol {
|
|||
Self(interface.normalized(db))
|
||||
}
|
||||
|
||||
pub(super) fn apply_type_mapping<'a>(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
type_mapping: TypeMapping<'a, 'db>,
|
||||
) -> Self {
|
||||
Self(self.0.specialized_and_normalized(db, type_mapping))
|
||||
}
|
||||
|
||||
pub(in crate::types) fn interface(self) -> ProtocolInterface<'db> {
|
||||
self.0
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ pub enum KnownInstanceType<'db> {
|
|||
/// The symbol `typing.OrderedDict` (which can also be found as `typing_extensions.OrderedDict`)
|
||||
OrderedDict,
|
||||
/// The symbol `typing.Protocol` (which can also be found as `typing_extensions.Protocol`)
|
||||
Protocol,
|
||||
Protocol(Option<GenericContext<'db>>),
|
||||
/// The symbol `typing.Generic` (which can also be found as `typing_extensions.Generic`)
|
||||
Generic(Option<GenericContext<'db>>),
|
||||
/// The symbol `typing.Type` (which can also be found as `typing_extensions.Type`)
|
||||
|
@ -146,7 +146,7 @@ impl<'db> KnownInstanceType<'db> {
|
|||
| Self::Deque
|
||||
| Self::ChainMap
|
||||
| Self::OrderedDict
|
||||
| Self::Protocol
|
||||
| Self::Protocol(_)
|
||||
| Self::Generic(_)
|
||||
| Self::ReadOnly
|
||||
| Self::TypeAliasType(_)
|
||||
|
@ -203,7 +203,7 @@ impl<'db> KnownInstanceType<'db> {
|
|||
Self::Deque => KnownClass::StdlibAlias,
|
||||
Self::ChainMap => KnownClass::StdlibAlias,
|
||||
Self::OrderedDict => KnownClass::StdlibAlias,
|
||||
Self::Protocol => KnownClass::SpecialForm, // actually `_ProtocolMeta` at runtime but this is what typeshed says
|
||||
Self::Protocol(_) => KnownClass::SpecialForm, // actually `_ProtocolMeta` at runtime but this is what typeshed says
|
||||
Self::Generic(_) => KnownClass::SpecialForm, // actually `type` at runtime but this is what typeshed says
|
||||
Self::TypeVar(_) => KnownClass::TypeVar,
|
||||
Self::TypeAliasType(_) => KnownClass::TypeAliasType,
|
||||
|
@ -249,7 +249,7 @@ impl<'db> KnownInstanceType<'db> {
|
|||
"ChainMap" => Self::ChainMap,
|
||||
"OrderedDict" => Self::OrderedDict,
|
||||
"Generic" => Self::Generic(None),
|
||||
"Protocol" => Self::Protocol,
|
||||
"Protocol" => Self::Protocol(None),
|
||||
"Optional" => Self::Optional,
|
||||
"Union" => Self::Union,
|
||||
"NoReturn" => Self::NoReturn,
|
||||
|
@ -311,7 +311,7 @@ impl<'db> KnownInstanceType<'db> {
|
|||
| Self::Generic(_)
|
||||
| Self::Callable => module.is_typing(),
|
||||
Self::Annotated
|
||||
| Self::Protocol
|
||||
| Self::Protocol(_)
|
||||
| Self::Literal
|
||||
| Self::LiteralString
|
||||
| Self::Never
|
||||
|
@ -384,11 +384,17 @@ impl Display for KnownInstanceRepr<'_> {
|
|||
KnownInstanceType::Deque => f.write_str("typing.Deque"),
|
||||
KnownInstanceType::ChainMap => f.write_str("typing.ChainMap"),
|
||||
KnownInstanceType::OrderedDict => f.write_str("typing.OrderedDict"),
|
||||
KnownInstanceType::Protocol => f.write_str("typing.Protocol"),
|
||||
KnownInstanceType::Protocol(generic_context) => {
|
||||
f.write_str("typing.Protocol")?;
|
||||
if let Some(generic_context) = generic_context {
|
||||
generic_context.display(self.db).fmt(f)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
KnownInstanceType::Generic(generic_context) => {
|
||||
f.write_str("typing.Generic")?;
|
||||
if let Some(generic_context) = generic_context {
|
||||
write!(f, "{}", generic_context.display(self.db))?;
|
||||
generic_context.display(self.db).fmt(f)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -174,7 +174,9 @@ impl<'db> Mro<'db> {
|
|||
continue;
|
||||
}
|
||||
match base {
|
||||
ClassBase::Class(_) | ClassBase::Generic(_) | ClassBase::Protocol => {
|
||||
ClassBase::Class(_)
|
||||
| ClassBase::Generic(_)
|
||||
| ClassBase::Protocol(_) => {
|
||||
errors.push(DuplicateBaseError {
|
||||
duplicate_base: base,
|
||||
first_index: *first_index,
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
|||
db::Db,
|
||||
semantic_index::{symbol_table, use_def_map},
|
||||
symbol::{symbol_from_bindings, symbol_from_declarations},
|
||||
types::{ClassBase, ClassLiteral, KnownFunction, Type, TypeQualifiers},
|
||||
types::{ClassBase, ClassLiteral, KnownFunction, Type, TypeMapping, TypeQualifiers},
|
||||
};
|
||||
|
||||
impl<'db> ClassLiteral<'db> {
|
||||
|
@ -146,6 +146,29 @@ impl<'db> ProtocolInterface<'db> {
|
|||
Self::SelfReference => Self::SelfReference,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn specialized_and_normalized<'a>(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
type_mapping: TypeMapping<'a, 'db>,
|
||||
) -> Self {
|
||||
match self {
|
||||
Self::Members(members) => Self::Members(ProtocolInterfaceMembers::new(
|
||||
db,
|
||||
members
|
||||
.inner(db)
|
||||
.iter()
|
||||
.map(|(name, data)| {
|
||||
(
|
||||
name.clone(),
|
||||
data.apply_type_mapping(db, type_mapping).normalized(db),
|
||||
)
|
||||
})
|
||||
.collect::<BTreeMap<_, _>>(),
|
||||
)),
|
||||
Self::SelfReference => Self::SelfReference,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash, salsa::Update)]
|
||||
|
@ -161,6 +184,13 @@ impl<'db> ProtocolMemberData<'db> {
|
|||
qualifiers: self.qualifiers,
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_type_mapping<'a>(&self, db: &'db dyn Db, type_mapping: TypeMapping<'a, 'db>) -> Self {
|
||||
Self {
|
||||
ty: self.ty.apply_type_mapping(db, type_mapping),
|
||||
qualifiers: self.qualifiers,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A single member of a protocol interface.
|
||||
|
|
|
@ -153,11 +153,15 @@ pub(super) fn union_or_intersection_elements_ordering<'db>(
|
|||
(ClassBase::Class(left), ClassBase::Class(right)) => left.cmp(&right),
|
||||
(ClassBase::Class(_), _) => Ordering::Less,
|
||||
(_, ClassBase::Class(_)) => Ordering::Greater,
|
||||
(ClassBase::Protocol, _) => Ordering::Less,
|
||||
(_, ClassBase::Protocol) => Ordering::Greater,
|
||||
|
||||
(ClassBase::Protocol(left), ClassBase::Protocol(right)) => left.cmp(&right),
|
||||
(ClassBase::Protocol(_), _) => Ordering::Less,
|
||||
(_, ClassBase::Protocol(_)) => Ordering::Greater,
|
||||
|
||||
(ClassBase::Generic(left), ClassBase::Generic(right)) => left.cmp(&right),
|
||||
(ClassBase::Generic(_), _) => Ordering::Less,
|
||||
(_, ClassBase::Generic(_)) => Ordering::Greater,
|
||||
|
||||
(ClassBase::Dynamic(left), ClassBase::Dynamic(right)) => {
|
||||
dynamic_elements_ordering(left, right)
|
||||
}
|
||||
|
@ -253,8 +257,11 @@ pub(super) fn union_or_intersection_elements_ordering<'db>(
|
|||
(KnownInstanceType::Generic(_), _) => Ordering::Less,
|
||||
(_, KnownInstanceType::Generic(_)) => Ordering::Greater,
|
||||
|
||||
(KnownInstanceType::Protocol, _) => Ordering::Less,
|
||||
(_, KnownInstanceType::Protocol) => Ordering::Greater,
|
||||
(KnownInstanceType::Protocol(left), KnownInstanceType::Protocol(right)) => {
|
||||
left.cmp(right)
|
||||
}
|
||||
(KnownInstanceType::Protocol(_), _) => Ordering::Less,
|
||||
(_, KnownInstanceType::Protocol(_)) => Ordering::Greater,
|
||||
|
||||
(KnownInstanceType::NoReturn, _) => Ordering::Less,
|
||||
(_, KnownInstanceType::NoReturn) => Ordering::Greater,
|
||||
|
@ -379,8 +386,5 @@ fn dynamic_elements_ordering(left: DynamicType, right: DynamicType) -> Ordering
|
|||
|
||||
#[cfg(not(debug_assertions))]
|
||||
(DynamicType::Todo(TodoType), DynamicType::Todo(TodoType)) => Ordering::Equal,
|
||||
|
||||
(DynamicType::SubscriptedProtocol, _) => Ordering::Less,
|
||||
(_, DynamicType::SubscriptedProtocol) => Ordering::Greater,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue