[ty] Support __setitem__ and improve __getitem__ related diagnostics (#19578)
## Summary
Adds validation to subscript assignment expressions.
```py
class Foo: ...
class Bar:
__setattr__ = None
class Baz:
def __setitem__(self, index: str, value: int) -> None:
pass
# We now emit a diagnostic on these statements
Foo()[1] = 2
Bar()[1] = 2
Baz()[1] = 2
```
Also improves error messages on invalid `__getitem__` expressions
## Test Plan
Update mdtests and add more to `subscript/instance.md`
---------
Co-authored-by: David Peter <sharkdp@users.noreply.github.com>
Co-authored-by: David Peter <mail@david-peter.de>
This commit is contained in:
@@ -14,7 +14,7 @@ a = NotSubscriptable()[0] # error: "Cannot subscript object of type `NotSubscri
|
||||
class NotSubscriptable:
|
||||
__getitem__ = None
|
||||
|
||||
# error: "Method `__getitem__` of type `Unknown | None` is not callable on object of type `NotSubscriptable`"
|
||||
# error: "Method `__getitem__` of type `Unknown | None` is possibly not callable on object of type `NotSubscriptable`"
|
||||
a = NotSubscriptable()[0]
|
||||
```
|
||||
|
||||
@@ -43,6 +43,18 @@ def _(flag: bool):
|
||||
reveal_type(Identity()[0]) # revealed: int | str
|
||||
```
|
||||
|
||||
## `__getitem__` with invalid index argument
|
||||
|
||||
```py
|
||||
class Identity:
|
||||
def __getitem__(self, index: int) -> int:
|
||||
return index
|
||||
|
||||
a = Identity()
|
||||
# error: [invalid-argument-type] "Method `__getitem__` of type `bound method Identity.__getitem__(index: int) -> int` cannot be called with key of type `Literal["a"]` on object of type `Identity`"
|
||||
a["a"]
|
||||
```
|
||||
|
||||
## `__setitem__` with no `__getitem__`
|
||||
|
||||
```py
|
||||
@@ -53,3 +65,45 @@ class NoGetitem:
|
||||
a = NoGetitem()
|
||||
a[0] = 0
|
||||
```
|
||||
|
||||
## Subscript store with no `__setitem__`
|
||||
|
||||
```py
|
||||
class NoSetitem: ...
|
||||
|
||||
a = NoSetitem()
|
||||
a[0] = 0 # error: "Cannot assign to object of type `NoSetitem` with no `__setitem__` method"
|
||||
```
|
||||
|
||||
## `__setitem__` not callable
|
||||
|
||||
```py
|
||||
class NoSetitem:
|
||||
__setitem__ = None
|
||||
|
||||
a = NoSetitem()
|
||||
a[0] = 0 # error: "Method `__setitem__` of type `Unknown | None` is possibly not callable on object of type `NoSetitem`"
|
||||
```
|
||||
|
||||
## Valid `__setitem__` method
|
||||
|
||||
```py
|
||||
class Identity:
|
||||
def __setitem__(self, index: int, value: int) -> None:
|
||||
pass
|
||||
|
||||
a = Identity()
|
||||
a[0] = 0
|
||||
```
|
||||
|
||||
## `__setitem__` with invalid index argument
|
||||
|
||||
```py
|
||||
class Identity:
|
||||
def __setitem__(self, index: int, value: int) -> None:
|
||||
pass
|
||||
|
||||
a = Identity()
|
||||
# error: [invalid-assignment] "Method `__setitem__` of type `bound method Identity.__setitem__(index: int, value: int) -> None` cannot be called with a key of type `Literal["a"]` and a value of type `Literal[0]` on object of type `Identity`"
|
||||
a["a"] = 0
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user