[ty] typing.Self is bound by the method, not the class (#19784)
This fixes our logic for binding a legacy typevar with its binding context. (To recap, a legacy typevar starts out "unbound" when it is first created, and each time it's used in a generic class or function, we "bind" it with the corresponding `Definition`.) We treat `typing.Self` the same as a legacy typevar, and so we apply this binding logic to it too. Before, we were using the enclosing class as its binding context. But that's not correct — it's the method where `typing.Self` is used that binds the typevar. (Each invocation of the method will find a new specialization of `Self` based on the specific instance type containing the invoked method.) This required plumbing through some additional state to the `in_type_expression` method. This also revealed that we weren't handling `Self`-typed instance attributes correctly (but were coincidentally not getting the expected false positive diagnostics).
This commit is contained in:
@@ -16,7 +16,7 @@ from typing import Self
|
||||
|
||||
class Shape:
|
||||
def set_scale(self: Self, scale: float) -> Self:
|
||||
reveal_type(self) # revealed: Self@Shape
|
||||
reveal_type(self) # revealed: Self@set_scale
|
||||
return self
|
||||
|
||||
def nested_type(self: Self) -> list[Self]:
|
||||
@@ -24,10 +24,17 @@ class Shape:
|
||||
|
||||
def nested_func(self: Self) -> Self:
|
||||
def inner() -> Self:
|
||||
reveal_type(self) # revealed: Self@Shape
|
||||
reveal_type(self) # revealed: Self@nested_func
|
||||
return self
|
||||
return inner()
|
||||
|
||||
def nested_func_without_enclosing_binding(self):
|
||||
def inner(x: Self):
|
||||
# TODO: revealed: Self@nested_func_without_enclosing_binding
|
||||
# (The outer method binds an implicit `Self`)
|
||||
reveal_type(x) # revealed: Self@inner
|
||||
inner(self)
|
||||
|
||||
def implicit_self(self) -> Self:
|
||||
# TODO: first argument in a method should be considered as "typing.Self"
|
||||
reveal_type(self) # revealed: Unknown
|
||||
@@ -38,13 +45,13 @@ reveal_type(Shape().nested_func()) # revealed: Shape
|
||||
|
||||
class Circle(Shape):
|
||||
def set_scale(self: Self, scale: float) -> Self:
|
||||
reveal_type(self) # revealed: Self@Circle
|
||||
reveal_type(self) # revealed: Self@set_scale
|
||||
return self
|
||||
|
||||
class Outer:
|
||||
class Inner:
|
||||
def foo(self: Self) -> Self:
|
||||
reveal_type(self) # revealed: Self@Inner
|
||||
reveal_type(self) # revealed: Self@foo
|
||||
return self
|
||||
```
|
||||
|
||||
@@ -99,6 +106,9 @@ reveal_type(Shape.bar()) # revealed: Unknown
|
||||
python-version = "3.11"
|
||||
```
|
||||
|
||||
TODO: The use of `Self` to annotate the `next_node` attribute should be
|
||||
[modeled as a property][self attribute], using `Self` in its parameter and return type.
|
||||
|
||||
```py
|
||||
from typing import Self
|
||||
|
||||
@@ -108,6 +118,8 @@ class LinkedList:
|
||||
|
||||
def next(self: Self) -> Self:
|
||||
reveal_type(self.value) # revealed: int
|
||||
# TODO: no error
|
||||
# error: [invalid-return-type]
|
||||
return self.next_node
|
||||
|
||||
reveal_type(LinkedList().next()) # revealed: LinkedList
|
||||
@@ -151,7 +163,7 @@ from typing import Self
|
||||
|
||||
class Shape:
|
||||
def union(self: Self, other: Self | None):
|
||||
reveal_type(other) # revealed: Self@Shape | None
|
||||
reveal_type(other) # revealed: Self@union | None
|
||||
return self
|
||||
```
|
||||
|
||||
@@ -205,3 +217,5 @@ class MyMetaclass(type):
|
||||
def __new__(cls) -> Self:
|
||||
return super().__new__(cls)
|
||||
```
|
||||
|
||||
[self attribute]: https://typing.python.org/en/latest/spec/generics.html#use-in-attribute-annotations
|
||||
|
||||
Reference in New Issue
Block a user