mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 02:38:25 +00:00
[ty] Propagate specializations to ancestor base classes (#17892)
@AlexWaygood discovered that even though we've been propagating specializations to _parent_ base classes correctly, we haven't been passing them on to _grandparent_ base classes: https://github.com/astral-sh/ruff/pull/17832#issuecomment-2854360969 ```py class Bar[T]: x: T class Baz[T](Bar[T]): ... class Spam[T](Baz[T]): ... reveal_type(Spam[int]().x) # revealed: `T`, but should be `int` ``` This PR updates the MRO machinery to apply the current specialization when starting to iterate the MRO of each base class.
This commit is contained in:
parent
8152ba7cb7
commit
9085f18353
8 changed files with 156 additions and 52 deletions
|
@ -91,7 +91,7 @@ reveal_type(ListSubclass.__mro__)
|
|||
class DictSubclass(typing.Dict): ...
|
||||
|
||||
# TODO: generic protocols
|
||||
# revealed: tuple[Literal[DictSubclass], Literal[dict[Unknown, Unknown]], Literal[MutableMapping[_KT, _VT]], Literal[Mapping[_KT, _VT]], Literal[Collection], Literal[Iterable], Literal[Container], @Todo(`Protocol[]` subscript), typing.Generic, typing.Generic[_KT, _VT_co], Literal[object]]
|
||||
# revealed: tuple[Literal[DictSubclass], Literal[dict[Unknown, Unknown]], Literal[MutableMapping[Unknown, Unknown]], Literal[Mapping[Unknown, Unknown]], Literal[Collection], Literal[Iterable], Literal[Container], @Todo(`Protocol[]` subscript), typing.Generic, typing.Generic[_KT, _VT_co], Literal[object]]
|
||||
reveal_type(DictSubclass.__mro__)
|
||||
|
||||
class SetSubclass(typing.Set): ...
|
||||
|
@ -113,19 +113,19 @@ reveal_type(FrozenSetSubclass.__mro__)
|
|||
class ChainMapSubclass(typing.ChainMap): ...
|
||||
|
||||
# TODO: generic protocols
|
||||
# revealed: tuple[Literal[ChainMapSubclass], Literal[ChainMap[Unknown, Unknown]], Literal[MutableMapping[_KT, _VT]], Literal[Mapping[_KT, _VT]], Literal[Collection], Literal[Iterable], Literal[Container], @Todo(`Protocol[]` subscript), typing.Generic, typing.Generic[_KT, _VT_co], Literal[object]]
|
||||
# revealed: tuple[Literal[ChainMapSubclass], Literal[ChainMap[Unknown, Unknown]], Literal[MutableMapping[Unknown, Unknown]], Literal[Mapping[Unknown, Unknown]], Literal[Collection], Literal[Iterable], Literal[Container], @Todo(`Protocol[]` subscript), typing.Generic, typing.Generic[_KT, _VT_co], Literal[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[Literal[CounterSubclass], Literal[Counter[Unknown]], Literal[dict[_T, int]], Literal[MutableMapping[_KT, _VT]], Literal[Mapping[_KT, _VT]], Literal[Collection], Literal[Iterable], Literal[Container], @Todo(`Protocol[]` subscript), typing.Generic, typing.Generic[_KT, _VT_co], typing.Generic[_T], Literal[object]]
|
||||
# revealed: tuple[Literal[CounterSubclass], Literal[Counter[Unknown]], Literal[dict[Unknown, int]], Literal[MutableMapping[Unknown, int]], Literal[Mapping[Unknown, int]], Literal[Collection], Literal[Iterable], Literal[Container], @Todo(`Protocol[]` subscript), typing.Generic, typing.Generic[_KT, _VT_co], typing.Generic[_T], Literal[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[Literal[DefaultDictSubclass], Literal[defaultdict[Unknown, Unknown]], Literal[dict[_KT, _VT]], Literal[MutableMapping[_KT, _VT]], Literal[Mapping[_KT, _VT]], Literal[Collection], Literal[Iterable], Literal[Container], @Todo(`Protocol[]` subscript), typing.Generic, typing.Generic[_KT, _VT_co], Literal[object]]
|
||||
# revealed: tuple[Literal[DefaultDictSubclass], Literal[defaultdict[Unknown, Unknown]], Literal[dict[Unknown, Unknown]], Literal[MutableMapping[Unknown, Unknown]], Literal[Mapping[Unknown, Unknown]], Literal[Collection], Literal[Iterable], Literal[Container], @Todo(`Protocol[]` subscript), typing.Generic, typing.Generic[_KT, _VT_co], Literal[object]]
|
||||
reveal_type(DefaultDictSubclass.__mro__)
|
||||
|
||||
class DequeSubclass(typing.Deque): ...
|
||||
|
@ -137,6 +137,6 @@ reveal_type(DequeSubclass.__mro__)
|
|||
class OrderedDictSubclass(typing.OrderedDict): ...
|
||||
|
||||
# TODO: Should be (OrderedDictSubclass, OrderedDict, dict, MutableMapping, Mapping, Collection, Sized, Iterable, Container, Generic, object)
|
||||
# revealed: tuple[Literal[OrderedDictSubclass], Literal[OrderedDict[Unknown, Unknown]], Literal[dict[_KT, _VT]], Literal[MutableMapping[_KT, _VT]], Literal[Mapping[_KT, _VT]], Literal[Collection], Literal[Iterable], Literal[Container], @Todo(`Protocol[]` subscript), typing.Generic, typing.Generic[_KT, _VT_co], Literal[object]]
|
||||
# revealed: tuple[Literal[OrderedDictSubclass], Literal[OrderedDict[Unknown, Unknown]], Literal[dict[Unknown, Unknown]], Literal[MutableMapping[Unknown, Unknown]], Literal[Mapping[Unknown, Unknown]], Literal[Collection], Literal[Iterable], Literal[Container], @Todo(`Protocol[]` subscript), typing.Generic, typing.Generic[_KT, _VT_co], Literal[object]]
|
||||
reveal_type(OrderedDictSubclass.__mro__)
|
||||
```
|
||||
|
|
|
@ -340,16 +340,27 @@ propagate through:
|
|||
from typing import Generic, TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
U = TypeVar("U")
|
||||
V = TypeVar("V")
|
||||
W = TypeVar("W")
|
||||
|
||||
class Base(Generic[T]):
|
||||
x: T | None = None
|
||||
class Parent(Generic[T]):
|
||||
x: T
|
||||
|
||||
class ExplicitlyGenericSub(Base[T], Generic[T]): ...
|
||||
class ImplicitlyGenericSub(Base[T]): ...
|
||||
class ExplicitlyGenericChild(Parent[U], Generic[U]): ...
|
||||
class ExplicitlyGenericGrandchild(ExplicitlyGenericChild[V], Generic[V]): ...
|
||||
class ExplicitlyGenericGreatgrandchild(ExplicitlyGenericGrandchild[W], Generic[W]): ...
|
||||
class ImplicitlyGenericChild(Parent[U]): ...
|
||||
class ImplicitlyGenericGrandchild(ImplicitlyGenericChild[V]): ...
|
||||
class ImplicitlyGenericGreatgrandchild(ImplicitlyGenericGrandchild[W]): ...
|
||||
|
||||
reveal_type(Base[int].x) # revealed: int | None
|
||||
reveal_type(ExplicitlyGenericSub[int].x) # revealed: int | None
|
||||
reveal_type(ImplicitlyGenericSub[int].x) # revealed: int | None
|
||||
reveal_type(Parent[int]().x) # revealed: int
|
||||
reveal_type(ExplicitlyGenericChild[int]().x) # revealed: int
|
||||
reveal_type(ImplicitlyGenericChild[int]().x) # revealed: int
|
||||
reveal_type(ExplicitlyGenericGrandchild[int]().x) # revealed: int
|
||||
reveal_type(ImplicitlyGenericGrandchild[int]().x) # revealed: int
|
||||
reveal_type(ExplicitlyGenericGreatgrandchild[int]().x) # revealed: int
|
||||
reveal_type(ImplicitlyGenericGreatgrandchild[int]().x) # revealed: int
|
||||
```
|
||||
|
||||
## Generic methods
|
||||
|
|
|
@ -285,13 +285,17 @@ When a generic subclass fills its superclass's type parameter with one of its ow
|
|||
propagate through:
|
||||
|
||||
```py
|
||||
class Base[T]:
|
||||
x: T | None = None
|
||||
class Parent[T]:
|
||||
x: T
|
||||
|
||||
class Sub[U](Base[U]): ...
|
||||
class Child[U](Parent[U]): ...
|
||||
class Grandchild[V](Child[V]): ...
|
||||
class Greatgrandchild[W](Child[W]): ...
|
||||
|
||||
reveal_type(Base[int].x) # revealed: int | None
|
||||
reveal_type(Sub[int].x) # revealed: int | None
|
||||
reveal_type(Parent[int]().x) # revealed: int
|
||||
reveal_type(Child[int]().x) # revealed: int
|
||||
reveal_type(Grandchild[int]().x) # revealed: int
|
||||
reveal_type(Greatgrandchild[int]().x) # revealed: int
|
||||
```
|
||||
|
||||
## Generic methods
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue