mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 10:48:32 +00:00
red_knot_python_semantic: improve not-iterable
diagnostic
This cleans up one particular TODO by splitting the "because" part of the `not-iterable` diagnostic out into an info sub-diagnostic.
This commit is contained in:
parent
07718f4788
commit
9a8f3cf247
20 changed files with 310 additions and 211 deletions
|
@ -286,7 +286,7 @@ class Test:
|
|||
def __iter__(self) -> TestIter | int:
|
||||
return TestIter()
|
||||
|
||||
# error: [not-iterable] "Object of type `Test` may not be iterable because its `__iter__` method returns an object of type `TestIter | int`, which may not have a `__next__` method"
|
||||
# error: [not-iterable] "Object of type `Test` may not be iterable"
|
||||
for x in Test():
|
||||
reveal_type(x) # revealed: int
|
||||
```
|
||||
|
@ -316,12 +316,12 @@ def _(flag: bool):
|
|||
else:
|
||||
__iter__: None = None
|
||||
|
||||
# error: [not-iterable] "Object of type `Iterable1` may not be iterable because its `__iter__` attribute (with type `CustomCallable`) may not be callable"
|
||||
# error: [not-iterable] "Object of type `Iterable1` may not be iterable"
|
||||
for x in Iterable1():
|
||||
# TODO... `int` might be ideal here?
|
||||
reveal_type(x) # revealed: int | Unknown
|
||||
|
||||
# error: [not-iterable] "Object of type `Iterable2` may not be iterable because its `__iter__` attribute (with type `(bound method Iterable2.__iter__() -> Iterator) | None`) may not be callable"
|
||||
# error: [not-iterable] "Object of type `Iterable2` may not be iterable"
|
||||
for y in Iterable2():
|
||||
# TODO... `int` might be ideal here?
|
||||
reveal_type(y) # revealed: int | Unknown
|
||||
|
@ -376,7 +376,7 @@ def _(flag: bool):
|
|||
def __iter__(self) -> Iterator:
|
||||
return Iterator()
|
||||
|
||||
# error: [not-iterable] "Object of type `Iterable` may not be iterable because its `__iter__` method returns an object of type `Iterator`, which may not have a `__next__` method"
|
||||
# error: [not-iterable] "Object of type `Iterable` may not be iterable"
|
||||
for x in Iterable():
|
||||
reveal_type(x) # revealed: int
|
||||
```
|
||||
|
@ -461,7 +461,7 @@ def _(flag: bool):
|
|||
return Iterator()
|
||||
__getitem__: None = None
|
||||
|
||||
# error: [not-iterable] "Object of type `Iterable` may not be iterable because it may not have an `__iter__` method and its `__getitem__` attribute has type `None`, which is not callable"
|
||||
# error: [not-iterable] "Object of type `Iterable` may not be iterable"
|
||||
for x in Iterable():
|
||||
reveal_type(x) # revealed: int
|
||||
```
|
||||
|
|
|
@ -28,7 +28,7 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/loops/for.md
|
|||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:not-iterable: Object of type `Iterable` is not iterable because it has no `__iter__` method and its `__getitem__` method has an incorrect signature for the old-style iteration protocol (expected a signature at least as permissive as `def __getitem__(self, key: int): ...`)
|
||||
error: lint:not-iterable: Object of type `Iterable` is not iterable
|
||||
--> /src/mdtest_snippet.py:10:10
|
||||
|
|
||||
9 | # error: [not-iterable]
|
||||
|
@ -36,6 +36,8 @@ error: lint:not-iterable: Object of type `Iterable` is not iterable because it h
|
|||
| ^^^^^^^^^^
|
||||
11 | reveal_type(x) # revealed: int
|
||||
|
|
||||
info: It has no `__iter__` method and its `__getitem__` method has an incorrect signature for the old-style iteration protocol
|
||||
info: `__getitem__` must be at least as permissive as `def __getitem__(self, key: int): ...` to satisfy the old-style iteration protocol
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/loops/for.md
|
|||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:not-iterable: Object of type `Literal[123]` is not iterable because it doesn't have an `__iter__` method or a `__getitem__` method
|
||||
error: lint:not-iterable: Object of type `Literal[123]` is not iterable
|
||||
--> /src/mdtest_snippet.py:2:10
|
||||
|
|
||||
1 | nonsense = 123
|
||||
|
@ -28,5 +28,6 @@ error: lint:not-iterable: Object of type `Literal[123]` is not iterable because
|
|||
| ^^^^^^^^
|
||||
3 | pass
|
||||
|
|
||||
info: It doesn't have an `__iter__` method or a `__getitem__` method
|
||||
|
||||
```
|
||||
|
|
|
@ -24,7 +24,7 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/loops/for.md
|
|||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:not-iterable: Object of type `NotIterable` is not iterable because its `__iter__` attribute has type `None`, which is not callable
|
||||
error: lint:not-iterable: Object of type `NotIterable` is not iterable
|
||||
--> /src/mdtest_snippet.py:6:10
|
||||
|
|
||||
4 | __iter__: None = None
|
||||
|
@ -33,5 +33,6 @@ error: lint:not-iterable: Object of type `NotIterable` is not iterable because i
|
|||
| ^^^^^^^^^^^^^
|
||||
7 | pass
|
||||
|
|
||||
info: Its `__iter__` attribute has type `None`, which is not callable
|
||||
|
||||
```
|
||||
|
|
|
@ -25,7 +25,7 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/loops/for.md
|
|||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:not-iterable: Object of type `Bad` is not iterable because it has no `__iter__` method and its `__getitem__` attribute has type `None`, which is not callable
|
||||
error: lint:not-iterable: Object of type `Bad` is not iterable
|
||||
--> /src/mdtest_snippet.py:7:10
|
||||
|
|
||||
6 | # error: [not-iterable]
|
||||
|
@ -33,6 +33,7 @@ error: lint:not-iterable: Object of type `Bad` is not iterable because it has no
|
|||
| ^^^^^
|
||||
8 | reveal_type(x) # revealed: Unknown
|
||||
|
|
||||
info: It has no `__iter__` method and its `__getitem__` attribute has type `None`, which is not callable
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/loops/for.md
|
|||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:not-iterable: Object of type `Iterable1` may not be iterable because it has no `__iter__` method and its `__getitem__` attribute (with type `CustomCallable`) may not be callable
|
||||
error: lint:not-iterable: Object of type `Iterable1` may not be iterable
|
||||
--> /src/mdtest_snippet.py:22:14
|
||||
|
|
||||
21 | # error: [not-iterable]
|
||||
|
@ -55,6 +55,8 @@ error: lint:not-iterable: Object of type `Iterable1` may not be iterable because
|
|||
23 | # TODO... `int` might be ideal here?
|
||||
24 | reveal_type(x) # revealed: int | Unknown
|
||||
|
|
||||
info: It has no `__iter__` method and its `__getitem__` attribute is invalid
|
||||
info: `__getitem__` has type `CustomCallable`, which is not callable
|
||||
|
||||
```
|
||||
|
||||
|
@ -73,7 +75,7 @@ info: revealed-type: Revealed type
|
|||
```
|
||||
|
||||
```
|
||||
error: lint:not-iterable: Object of type `Iterable2` may not be iterable because it has no `__iter__` method and its `__getitem__` attribute (with type `(bound method Iterable2.__getitem__(key: int) -> int) | None`) may not be callable
|
||||
error: lint:not-iterable: Object of type `Iterable2` may not be iterable
|
||||
--> /src/mdtest_snippet.py:27:14
|
||||
|
|
||||
26 | # error: [not-iterable]
|
||||
|
@ -82,6 +84,8 @@ error: lint:not-iterable: Object of type `Iterable2` may not be iterable because
|
|||
28 | # TODO... `int` might be ideal here?
|
||||
29 | reveal_type(y) # revealed: int | Unknown
|
||||
|
|
||||
info: It has no `__iter__` method and its `__getitem__` attribute is invalid
|
||||
info: `__getitem__` has type `(bound method Iterable2.__getitem__(key: int) -> int) | None`, which is not callable
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/loops/for.md
|
|||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:not-iterable: Object of type `Iterable1` may not be iterable because it has no `__iter__` method and its `__getitem__` attribute (with type `(bound method Iterable1.__getitem__(item: int) -> str) | None`) may not be callable
|
||||
error: lint:not-iterable: Object of type `Iterable1` may not be iterable
|
||||
--> /src/mdtest_snippet.py:20:14
|
||||
|
|
||||
19 | # error: [not-iterable]
|
||||
|
@ -52,6 +52,8 @@ error: lint:not-iterable: Object of type `Iterable1` may not be iterable because
|
|||
21 | # TODO: `str` might be better
|
||||
22 | reveal_type(x) # revealed: str | Unknown
|
||||
|
|
||||
info: It has no `__iter__` method and its `__getitem__` attribute is invalid
|
||||
info: `__getitem__` has type `(bound method Iterable1.__getitem__(item: int) -> str) | None`, which is not callable
|
||||
|
||||
```
|
||||
|
||||
|
@ -70,7 +72,7 @@ info: revealed-type: Revealed type
|
|||
```
|
||||
|
||||
```
|
||||
error: lint:not-iterable: Object of type `Iterable2` may not be iterable because it has no `__iter__` method and its `__getitem__` method (with type `(bound method Iterable2.__getitem__(item: int) -> str) | (bound method Iterable2.__getitem__(item: str) -> int)`) may have an incorrect signature for the old-style iteration protocol (expected a signature at least as permissive as `def __getitem__(self, key: int): ...`)
|
||||
error: lint:not-iterable: Object of type `Iterable2` may not be iterable
|
||||
--> /src/mdtest_snippet.py:25:14
|
||||
|
|
||||
24 | # error: [not-iterable]
|
||||
|
@ -78,6 +80,8 @@ error: lint:not-iterable: Object of type `Iterable2` may not be iterable because
|
|||
| ^^^^^^^^^^^
|
||||
26 | reveal_type(y) # revealed: str | int
|
||||
|
|
||||
info: It has no `__iter__` method and its `__getitem__` method (with type `(bound method Iterable2.__getitem__(item: int) -> str) | (bound method Iterable2.__getitem__(item: str) -> int)`) may have an incorrect signature for the old-style iteration protocol
|
||||
info: `__getitem__` must be at least as permissive as `def __getitem__(self, key: int): ...` to satisfy the old-style iteration protocol
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/loops/for.md
|
|||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:not-iterable: Object of type `Iterable1` may not be iterable because its `__iter__` method (with type `(bound method Iterable1.__iter__() -> Iterator) | (bound method Iterable1.__iter__(invalid_extra_arg) -> Iterator)`) may have an invalid signature (expected `def __iter__(self): ...`)
|
||||
error: lint:not-iterable: Object of type `Iterable1` may not be iterable
|
||||
--> /src/mdtest_snippet.py:17:14
|
||||
|
|
||||
16 | # error: [not-iterable]
|
||||
|
@ -55,6 +55,9 @@ error: lint:not-iterable: Object of type `Iterable1` may not be iterable because
|
|||
| ^^^^^^^^^^^
|
||||
18 | reveal_type(x) # revealed: int
|
||||
|
|
||||
info: Its `__iter__` method may have an invalid signature
|
||||
info: Type of `__iter__` is `(bound method Iterable1.__iter__() -> Iterator) | (bound method Iterable1.__iter__(invalid_extra_arg) -> Iterator)`
|
||||
info: Expected signature for `__iter__` is `def __iter__(self): ...`
|
||||
|
||||
```
|
||||
|
||||
|
@ -73,7 +76,7 @@ info: revealed-type: Revealed type
|
|||
```
|
||||
|
||||
```
|
||||
error: lint:not-iterable: Object of type `Iterable2` may not be iterable because its `__iter__` attribute (with type `(bound method Iterable2.__iter__() -> Iterator) | None`) may not be callable
|
||||
error: lint:not-iterable: Object of type `Iterable2` may not be iterable
|
||||
--> /src/mdtest_snippet.py:28:14
|
||||
|
|
||||
27 | # error: [not-iterable]
|
||||
|
@ -82,6 +85,7 @@ error: lint:not-iterable: Object of type `Iterable2` may not be iterable because
|
|||
29 | # TODO: `int` would probably be better here:
|
||||
30 | reveal_type(x) # revealed: int | Unknown
|
||||
|
|
||||
info: Its `__iter__` attribute (with type `(bound method Iterable2.__iter__() -> Iterator) | None`) may not be callable
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/loops/for.md
|
|||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:not-iterable: Object of type `Iterable1` may not be iterable because its `__iter__` method returns an object of type `Iterator1`, which may have an invalid `__next__` method (expected `def __next__(self): ...`)
|
||||
error: lint:not-iterable: Object of type `Iterable1` may not be iterable
|
||||
--> /src/mdtest_snippet.py:28:14
|
||||
|
|
||||
27 | # error: [not-iterable]
|
||||
|
@ -59,6 +59,8 @@ error: lint:not-iterable: Object of type `Iterable1` may not be iterable because
|
|||
| ^^^^^^^^^^^
|
||||
29 | reveal_type(x) # revealed: int | str
|
||||
|
|
||||
info: Its `__iter__` method returns an object of type `Iterator1`, which may have an invalid `__next__` method
|
||||
info: Expected signature for `__next__` is `def __next__(self): ...`)
|
||||
|
||||
```
|
||||
|
||||
|
@ -77,7 +79,7 @@ info: revealed-type: Revealed type
|
|||
```
|
||||
|
||||
```
|
||||
error: lint:not-iterable: Object of type `Iterable2` may not be iterable because its `__iter__` method returns an object of type `Iterator2`, which has a `__next__` attribute that may not be callable
|
||||
error: lint:not-iterable: Object of type `Iterable2` may not be iterable
|
||||
--> /src/mdtest_snippet.py:32:14
|
||||
|
|
||||
31 | # error: [not-iterable]
|
||||
|
@ -86,6 +88,7 @@ error: lint:not-iterable: Object of type `Iterable2` may not be iterable because
|
|||
33 | # TODO: `int` would probably be better here:
|
||||
34 | reveal_type(y) # revealed: int | Unknown
|
||||
|
|
||||
info: Its `__iter__` method returns an object of type `Iterator2`, which has a `__next__` attribute that may not be callable
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/loops/for.md
|
|||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:not-iterable: Object of type `Iterable` may not be iterable because it may not have an `__iter__` method and its `__getitem__` method has an incorrect signature for the old-style iteration protocol (expected a signature at least as permissive as `def __getitem__(self, key: int): ...`)
|
||||
error: lint:not-iterable: Object of type `Iterable` may not be iterable
|
||||
--> /src/mdtest_snippet.py:18:14
|
||||
|
|
||||
17 | # error: [not-iterable]
|
||||
|
@ -44,6 +44,8 @@ error: lint:not-iterable: Object of type `Iterable` may not be iterable because
|
|||
| ^^^^^^^^^^
|
||||
19 | reveal_type(x) # revealed: int | bytes
|
||||
|
|
||||
info: It may not have an `__iter__` method and its `__getitem__` method has an incorrect signature for the old-style iteration protocol
|
||||
info: `__getitem__` must be at least as permissive as `def __getitem__(self, key: int): ...` to satisfy the old-style iteration protocol
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/loops/for.md
|
|||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:not-iterable: Object of type `Iterable1` may not be iterable because it may not have an `__iter__` method and its `__getitem__` attribute (with type `(bound method Iterable1.__getitem__(item: int) -> str) | None`) may not be callable
|
||||
error: lint:not-iterable: Object of type `Iterable1` may not be iterable
|
||||
--> /src/mdtest_snippet.py:31:14
|
||||
|
|
||||
30 | # error: [not-iterable]
|
||||
|
@ -63,6 +63,7 @@ error: lint:not-iterable: Object of type `Iterable1` may not be iterable because
|
|||
32 | # TODO: `bytes | str` might be better
|
||||
33 | reveal_type(x) # revealed: bytes | str | Unknown
|
||||
|
|
||||
info: It may not have an `__iter__` method and its `__getitem__` attribute (with type `(bound method Iterable1.__getitem__(item: int) -> str) | None`) may not be callable
|
||||
|
||||
```
|
||||
|
||||
|
@ -81,7 +82,7 @@ info: revealed-type: Revealed type
|
|||
```
|
||||
|
||||
```
|
||||
error: lint:not-iterable: Object of type `Iterable2` may not be iterable because it may not have an `__iter__` method and its `__getitem__` method (with type `(bound method Iterable2.__getitem__(item: int) -> str) | (bound method Iterable2.__getitem__(item: str) -> int)`) may have an incorrect signature for the old-style iteration protocol (expected a signature at least as permissive as `def __getitem__(self, key: int): ...`)
|
||||
error: lint:not-iterable: Object of type `Iterable2` may not be iterable
|
||||
--> /src/mdtest_snippet.py:36:14
|
||||
|
|
||||
35 | # error: [not-iterable]
|
||||
|
@ -89,6 +90,8 @@ error: lint:not-iterable: Object of type `Iterable2` may not be iterable because
|
|||
| ^^^^^^^^^^^
|
||||
37 | reveal_type(y) # revealed: bytes | str | int
|
||||
|
|
||||
info: It may not have an `__iter__` method and its `__getitem__` method (with type `(bound method Iterable2.__getitem__(item: int) -> str) | (bound method Iterable2.__getitem__(item: str) -> int)`) may have an incorrect signature for the old-style iteration protocol
|
||||
info: `__getitem__` must be at least as permissive as `def __getitem__(self, key: int): ...` to satisfy the old-style iteration protocol
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/loops/for.md
|
|||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:not-iterable: Object of type `Iterable` may not be iterable because it may not have an `__iter__` method or a `__getitem__` method
|
||||
error: lint:not-iterable: Object of type `Iterable` may not be iterable
|
||||
--> /src/mdtest_snippet.py:17:14
|
||||
|
|
||||
16 | # error: [not-iterable]
|
||||
|
@ -43,6 +43,7 @@ error: lint:not-iterable: Object of type `Iterable` may not be iterable because
|
|||
| ^^^^^^^^^^
|
||||
18 | reveal_type(x) # revealed: int | bytes
|
||||
|
|
||||
info: It may not have an `__iter__` method or a `__getitem__` method
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/loops/for.md
|
|||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:not-iterable: Object of type `Test | Test2` may not be iterable because its `__iter__` method returns an object of type `TestIter | int`, which may not have a `__next__` method
|
||||
error: lint:not-iterable: Object of type `Test | Test2` may not be iterable
|
||||
--> /src/mdtest_snippet.py:18:14
|
||||
|
|
||||
16 | # TODO: Improve error message to state which union variant isn't iterable (https://github.com/astral-sh/ruff/issues/13989)
|
||||
|
@ -45,6 +45,7 @@ error: lint:not-iterable: Object of type `Test | Test2` may not be iterable beca
|
|||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
19 | reveal_type(x) # revealed: int
|
||||
|
|
||||
info: Its `__iter__` method returns an object of type `TestIter | int`, which may not have a `__next__` method
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/loops/for.md
|
|||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:not-iterable: Object of type `Test | Literal[42]` may not be iterable because it may not have an `__iter__` method and it doesn't have a `__getitem__` method
|
||||
error: lint:not-iterable: Object of type `Test | Literal[42]` may not be iterable
|
||||
--> /src/mdtest_snippet.py:13:14
|
||||
|
|
||||
11 | def _(flag: bool):
|
||||
|
@ -40,6 +40,7 @@ error: lint:not-iterable: Object of type `Test | Literal[42]` may not be iterabl
|
|||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
14 | reveal_type(x) # revealed: int
|
||||
|
|
||||
info: It may not have an `__iter__` method and it doesn't have a `__getitem__` method
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/loops/for.md
|
|||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:not-iterable: Object of type `NotIterable` is not iterable because its `__iter__` attribute has type `int | None`, which is not callable
|
||||
error: lint:not-iterable: Object of type `NotIterable` is not iterable
|
||||
--> /src/mdtest_snippet.py:11:14
|
||||
|
|
||||
10 | # error: [not-iterable]
|
||||
|
@ -41,6 +41,7 @@ error: lint:not-iterable: Object of type `NotIterable` is not iterable because i
|
|||
| ^^^^^^^^^^^^^
|
||||
12 | pass
|
||||
|
|
||||
info: Its `__iter__` attribute has type `int | None`, which is not callable
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/loops/for.md
|
|||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:not-iterable: Object of type `Bad` is not iterable because its `__iter__` method returns an object of type `int`, which has no `__next__` method
|
||||
error: lint:not-iterable: Object of type `Bad` is not iterable
|
||||
--> /src/mdtest_snippet.py:8:10
|
||||
|
|
||||
7 | # error: [not-iterable]
|
||||
|
@ -34,6 +34,7 @@ error: lint:not-iterable: Object of type `Bad` is not iterable because its `__it
|
|||
| ^^^^^
|
||||
9 | reveal_type(x) # revealed: Unknown
|
||||
|
|
||||
info: Its `__iter__` method returns an object of type `int`, which has no `__next__` method
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/loops/for.md
|
|||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:not-iterable: Object of type `Iterable` is not iterable because its `__iter__` method has an invalid signature (expected `def __iter__(self): ...`)
|
||||
error: lint:not-iterable: Object of type `Iterable` is not iterable
|
||||
--> /src/mdtest_snippet.py:12:10
|
||||
|
|
||||
11 | # error: [not-iterable]
|
||||
|
@ -38,6 +38,8 @@ error: lint:not-iterable: Object of type `Iterable` is not iterable because its
|
|||
| ^^^^^^^^^^
|
||||
13 | reveal_type(x) # revealed: int
|
||||
|
|
||||
info: Its `__iter__` method has an invalid signature
|
||||
info: Expected signature `def __iter__(self): ...`
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/loops/for.md
|
|||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:not-iterable: Object of type `Iterable1` is not iterable because its `__iter__` method returns an object of type `Iterator1`, which has an invalid `__next__` method (expected `def __next__(self): ...`)
|
||||
error: lint:not-iterable: Object of type `Iterable1` is not iterable
|
||||
--> /src/mdtest_snippet.py:19:10
|
||||
|
|
||||
18 | # error: [not-iterable]
|
||||
|
@ -49,6 +49,8 @@ error: lint:not-iterable: Object of type `Iterable1` is not iterable because its
|
|||
| ^^^^^^^^^^^
|
||||
20 | reveal_type(x) # revealed: int
|
||||
|
|
||||
info: Its `__iter__` method returns an object of type `Iterator1`, which has an invalid `__next__` method
|
||||
info: Expected signature for `__next__` is `def __next__(self): ...`
|
||||
|
||||
```
|
||||
|
||||
|
@ -67,7 +69,7 @@ info: revealed-type: Revealed type
|
|||
```
|
||||
|
||||
```
|
||||
error: lint:not-iterable: Object of type `Iterable2` is not iterable because its `__iter__` method returns an object of type `Iterator2`, which has a `__next__` attribute that is not callable
|
||||
error: lint:not-iterable: Object of type `Iterable2` is not iterable
|
||||
--> /src/mdtest_snippet.py:23:10
|
||||
|
|
||||
22 | # error: [not-iterable]
|
||||
|
@ -75,6 +77,7 @@ error: lint:not-iterable: Object of type `Iterable2` is not iterable because its
|
|||
| ^^^^^^^^^^^
|
||||
24 | reveal_type(y) # revealed: Unknown
|
||||
|
|
||||
info: Its `__iter__` method returns an object of type `Iterator2`, which has a `__next__` attribute that is not callable
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -18,11 +18,12 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/unpack
|
|||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:not-iterable: Object of type `Literal[1]` is not iterable because it doesn't have an `__iter__` method or a `__getitem__` method
|
||||
error: lint:not-iterable: Object of type `Literal[1]` is not iterable
|
||||
--> /src/mdtest_snippet.py:1:8
|
||||
|
|
||||
1 | a, b = 1 # error: [not-iterable]
|
||||
| ^
|
||||
|
|
||||
info: It doesn't have an `__iter__` method or a `__getitem__` method
|
||||
|
||||
```
|
||||
|
|
|
@ -42,6 +42,7 @@ use crate::symbol::{
|
|||
};
|
||||
use crate::types::call::{Bindings, CallArgumentTypes, CallableBinding};
|
||||
pub(crate) use crate::types::class_base::ClassBase;
|
||||
use crate::types::context::{LintDiagnosticGuard, LintDiagnosticGuardBuilder};
|
||||
use crate::types::diagnostic::{INVALID_TYPE_FORM, UNSUPPORTED_BOOL_CONVERSION};
|
||||
use crate::types::generics::{GenericContext, Specialization};
|
||||
use crate::types::infer::infer_unpack_types;
|
||||
|
@ -5505,219 +5506,281 @@ impl<'db> IterationError<'db> {
|
|||
iterable_type: Type<'db>,
|
||||
iterable_node: ast::AnyNodeRef,
|
||||
) {
|
||||
/// A little helper type for emitting a diagnostic
|
||||
/// based on the variant of iteration error.
|
||||
struct Reporter<'a> {
|
||||
db: &'a dyn Db,
|
||||
builder: LintDiagnosticGuardBuilder<'a, 'a>,
|
||||
iterable_type: Type<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Reporter<'a> {
|
||||
/// Emit a diagnostic that is certain that `iterable_type` is not iterable.
|
||||
///
|
||||
/// `because` should explain why `iterable_type` is not iterable.
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn is_not(self, because: impl std::fmt::Display) -> LintDiagnosticGuard<'a, 'a> {
|
||||
let mut diag = self.builder.into_diagnostic(format_args!(
|
||||
"Object of type `{iterable_type}` is not iterable",
|
||||
iterable_type = self.iterable_type.display(self.db),
|
||||
));
|
||||
diag.info(because);
|
||||
diag
|
||||
}
|
||||
|
||||
/// Emit a diagnostic that is uncertain that `iterable_type` is not iterable.
|
||||
///
|
||||
/// `because` should explain why `iterable_type` is likely not iterable.
|
||||
fn may_not(self, because: impl std::fmt::Display) -> LintDiagnosticGuard<'a, 'a> {
|
||||
let mut diag = self.builder.into_diagnostic(format_args!(
|
||||
"Object of type `{iterable_type}` may not be iterable",
|
||||
iterable_type = self.iterable_type.display(self.db),
|
||||
));
|
||||
diag.info(because);
|
||||
diag
|
||||
}
|
||||
}
|
||||
|
||||
let Some(builder) = context.report_lint(&NOT_ITERABLE, iterable_node) else {
|
||||
return;
|
||||
};
|
||||
let db = context.db();
|
||||
|
||||
let report_not_iterable = |arguments: std::fmt::Arguments| {
|
||||
builder.into_diagnostic(arguments);
|
||||
let reporter = Reporter {
|
||||
db,
|
||||
builder,
|
||||
iterable_type,
|
||||
};
|
||||
|
||||
// TODO: for all of these error variants, the "explanation" for the diagnostic
|
||||
// (everything after the "because") should really be presented as a "help:", "note",
|
||||
// or similar, rather than as part of the same sentence as the error message.
|
||||
match self {
|
||||
Self::IterCallError(CallErrorKind::NotCallable, bindings) => report_not_iterable(format_args!(
|
||||
"Object of type `{iterable_type}` is not iterable \
|
||||
because its `__iter__` attribute has type `{dunder_iter_type}`, \
|
||||
which is not callable",
|
||||
iterable_type = iterable_type.display(db),
|
||||
dunder_iter_type = bindings.callable_type().display(db),
|
||||
)),
|
||||
Self::IterCallError(CallErrorKind::PossiblyNotCallable, bindings) if bindings.is_single() => {
|
||||
report_not_iterable(format_args!(
|
||||
"Object of type `{iterable_type}` may not be iterable \
|
||||
because its `__iter__` attribute (with type `{dunder_iter_type}`) \
|
||||
may not be callable",
|
||||
iterable_type = iterable_type.display(db),
|
||||
Self::IterCallError(CallErrorKind::NotCallable, bindings) => {
|
||||
reporter.is_not(format_args!(
|
||||
"Its `__iter__` attribute has type `{dunder_iter_type}`, which is not callable",
|
||||
dunder_iter_type = bindings.callable_type().display(db),
|
||||
));
|
||||
}
|
||||
Self::IterCallError(CallErrorKind::PossiblyNotCallable, bindings)
|
||||
if bindings.is_single() =>
|
||||
{
|
||||
reporter.may_not(format_args!(
|
||||
"Its `__iter__` attribute (with type `{dunder_iter_type}`) \
|
||||
may not be callable",
|
||||
dunder_iter_type = bindings.callable_type().display(db),
|
||||
));
|
||||
}
|
||||
Self::IterCallError(CallErrorKind::PossiblyNotCallable, bindings) => {
|
||||
report_not_iterable(format_args!(
|
||||
"Object of type `{iterable_type}` may not be iterable \
|
||||
because its `__iter__` attribute (with type `{dunder_iter_type}`) \
|
||||
may not be callable",
|
||||
iterable_type = iterable_type.display(db),
|
||||
reporter.may_not(format_args!(
|
||||
"Its `__iter__` attribute (with type `{dunder_iter_type}`) \
|
||||
may not be callable",
|
||||
dunder_iter_type = bindings.callable_type().display(db),
|
||||
));
|
||||
}
|
||||
Self::IterCallError(CallErrorKind::BindingError, bindings) if bindings.is_single() => report_not_iterable(format_args!(
|
||||
"Object of type `{iterable_type}` is not iterable \
|
||||
because its `__iter__` method has an invalid signature \
|
||||
(expected `def __iter__(self): ...`)",
|
||||
iterable_type = iterable_type.display(db),
|
||||
)),
|
||||
Self::IterCallError(CallErrorKind::BindingError, bindings) => report_not_iterable(format_args!(
|
||||
"Object of type `{iterable_type}` may not be iterable \
|
||||
because its `__iter__` method (with type `{dunder_iter_type}`) \
|
||||
may have an invalid signature (expected `def __iter__(self): ...`)",
|
||||
iterable_type = iterable_type.display(db),
|
||||
dunder_iter_type = bindings.callable_type().display(db),
|
||||
)),
|
||||
Self::IterCallError(CallErrorKind::BindingError, bindings) if bindings.is_single() => {
|
||||
reporter
|
||||
.is_not("Its `__iter__` method has an invalid signature")
|
||||
.info("Expected signature `def __iter__(self): ...`");
|
||||
}
|
||||
Self::IterCallError(CallErrorKind::BindingError, bindings) => {
|
||||
let mut diag =
|
||||
reporter.may_not("Its `__iter__` method may have an invalid signature");
|
||||
diag.info(format_args!(
|
||||
"Type of `__iter__` is `{dunder_iter_type}`",
|
||||
dunder_iter_type = bindings.callable_type().display(db),
|
||||
));
|
||||
diag.info("Expected signature for `__iter__` is `def __iter__(self): ...`");
|
||||
}
|
||||
|
||||
Self::IterReturnsInvalidIterator {
|
||||
iterator,
|
||||
dunder_next_error
|
||||
dunder_next_error,
|
||||
} => match dunder_next_error {
|
||||
CallDunderError::MethodNotAvailable => report_not_iterable(format_args!(
|
||||
"Object of type `{iterable_type}` is not iterable \
|
||||
because its `__iter__` method returns an object of type `{iterator_type}`, \
|
||||
which has no `__next__` method",
|
||||
iterable_type = iterable_type.display(db),
|
||||
iterator_type = iterator.display(db),
|
||||
)),
|
||||
CallDunderError::PossiblyUnbound(_) => report_not_iterable(format_args!(
|
||||
"Object of type `{iterable_type}` may not be iterable \
|
||||
because its `__iter__` method returns an object of type `{iterator_type}`, \
|
||||
which may not have a `__next__` method",
|
||||
iterable_type = iterable_type.display(db),
|
||||
iterator_type = iterator.display(db),
|
||||
)),
|
||||
CallDunderError::CallError(CallErrorKind::NotCallable, _) => report_not_iterable(format_args!(
|
||||
"Object of type `{iterable_type}` is not iterable \
|
||||
because its `__iter__` method returns an object of type `{iterator_type}`, \
|
||||
which has a `__next__` attribute that is not callable",
|
||||
iterable_type = iterable_type.display(db),
|
||||
iterator_type = iterator.display(db),
|
||||
)),
|
||||
CallDunderError::CallError(CallErrorKind::PossiblyNotCallable, _) => report_not_iterable(format_args!(
|
||||
"Object of type `{iterable_type}` may not be iterable \
|
||||
because its `__iter__` method returns an object of type `{iterator_type}`, \
|
||||
which has a `__next__` attribute that may not be callable",
|
||||
iterable_type = iterable_type.display(db),
|
||||
iterator_type = iterator.display(db),
|
||||
)),
|
||||
CallDunderError::CallError(CallErrorKind::BindingError, bindings) if bindings.is_single() => report_not_iterable(format_args!(
|
||||
"Object of type `{iterable_type}` is not iterable \
|
||||
because its `__iter__` method returns an object of type `{iterator_type}`, \
|
||||
which has an invalid `__next__` method (expected `def __next__(self): ...`)",
|
||||
iterable_type = iterable_type.display(db),
|
||||
iterator_type = iterator.display(db),
|
||||
)),
|
||||
CallDunderError::CallError(CallErrorKind::BindingError, _) => report_not_iterable(format_args!(
|
||||
"Object of type `{iterable_type}` may not be iterable \
|
||||
because its `__iter__` method returns an object of type `{iterator_type}`, \
|
||||
which may have an invalid `__next__` method (expected `def __next__(self): ...`)",
|
||||
iterable_type = iterable_type.display(db),
|
||||
iterator_type = iterator.display(db),
|
||||
)),
|
||||
}
|
||||
CallDunderError::MethodNotAvailable => {
|
||||
reporter.is_not(format_args!(
|
||||
"Its `__iter__` method returns an object of type `{iterator_type}`, \
|
||||
which has no `__next__` method",
|
||||
iterator_type = iterator.display(db),
|
||||
));
|
||||
}
|
||||
CallDunderError::PossiblyUnbound(_) => {
|
||||
reporter.may_not(format_args!(
|
||||
"Its `__iter__` method returns an object of type `{iterator_type}`, \
|
||||
which may not have a `__next__` method",
|
||||
iterator_type = iterator.display(db),
|
||||
));
|
||||
}
|
||||
CallDunderError::CallError(CallErrorKind::NotCallable, _) => {
|
||||
reporter.is_not(format_args!(
|
||||
"Its `__iter__` method returns an object of type `{iterator_type}`, \
|
||||
which has a `__next__` attribute that is not callable",
|
||||
iterator_type = iterator.display(db),
|
||||
));
|
||||
}
|
||||
CallDunderError::CallError(CallErrorKind::PossiblyNotCallable, _) => {
|
||||
reporter.may_not(format_args!(
|
||||
"Its `__iter__` method returns an object of type `{iterator_type}`, \
|
||||
which has a `__next__` attribute that may not be callable",
|
||||
iterator_type = iterator.display(db),
|
||||
));
|
||||
}
|
||||
CallDunderError::CallError(CallErrorKind::BindingError, bindings)
|
||||
if bindings.is_single() =>
|
||||
{
|
||||
reporter
|
||||
.is_not(format_args!(
|
||||
"Its `__iter__` method returns an object of type `{iterator_type}`, \
|
||||
which has an invalid `__next__` method",
|
||||
iterator_type = iterator.display(db),
|
||||
))
|
||||
.info("Expected signature for `__next__` is `def __next__(self): ...`");
|
||||
}
|
||||
CallDunderError::CallError(CallErrorKind::BindingError, _) => {
|
||||
reporter
|
||||
.may_not(format_args!(
|
||||
"Its `__iter__` method returns an object of type `{iterator_type}`, \
|
||||
which may have an invalid `__next__` method",
|
||||
iterator_type = iterator.display(db),
|
||||
))
|
||||
.info("Expected signature for `__next__` is `def __next__(self): ...`)");
|
||||
}
|
||||
},
|
||||
|
||||
Self::PossiblyUnboundIterAndGetitemError {
|
||||
dunder_getitem_error, ..
|
||||
dunder_getitem_error,
|
||||
..
|
||||
} => match dunder_getitem_error {
|
||||
CallDunderError::MethodNotAvailable => report_not_iterable(format_args!(
|
||||
"Object of type `{}` may not be iterable \
|
||||
because it may not have an `__iter__` method \
|
||||
and it doesn't have a `__getitem__` method",
|
||||
iterable_type.display(db)
|
||||
)),
|
||||
CallDunderError::PossiblyUnbound(_) => report_not_iterable(format_args!(
|
||||
"Object of type `{}` may not be iterable \
|
||||
because it may not have an `__iter__` method or a `__getitem__` method",
|
||||
iterable_type.display(db)
|
||||
)),
|
||||
CallDunderError::CallError(CallErrorKind::NotCallable, bindings) => report_not_iterable(format_args!(
|
||||
"Object of type `{iterable_type}` may not be iterable \
|
||||
because it may not have an `__iter__` method \
|
||||
and its `__getitem__` attribute has type `{dunder_getitem_type}`, \
|
||||
which is not callable",
|
||||
iterable_type = iterable_type.display(db),
|
||||
dunder_getitem_type = bindings.callable_type().display(db),
|
||||
)),
|
||||
CallDunderError::CallError(CallErrorKind::PossiblyNotCallable, bindings) if bindings.is_single() => report_not_iterable(format_args!(
|
||||
"Object of type `{iterable_type}` may not be iterable \
|
||||
because it may not have an `__iter__` method \
|
||||
and its `__getitem__` attribute may not be callable",
|
||||
iterable_type = iterable_type.display(db),
|
||||
)),
|
||||
CallDunderError::CallError(CallErrorKind::PossiblyNotCallable, bindings) => {
|
||||
report_not_iterable(format_args!(
|
||||
"Object of type `{iterable_type}` may not be iterable \
|
||||
because it may not have an `__iter__` method \
|
||||
and its `__getitem__` attribute (with type `{dunder_getitem_type}`) \
|
||||
may not be callable",
|
||||
iterable_type = iterable_type.display(db),
|
||||
CallDunderError::MethodNotAvailable => {
|
||||
reporter.may_not(
|
||||
"It may not have an `__iter__` method \
|
||||
and it doesn't have a `__getitem__` method",
|
||||
);
|
||||
}
|
||||
CallDunderError::PossiblyUnbound(_) => {
|
||||
reporter
|
||||
.may_not("It may not have an `__iter__` method or a `__getitem__` method");
|
||||
}
|
||||
CallDunderError::CallError(CallErrorKind::NotCallable, bindings) => {
|
||||
reporter.may_not(format_args!(
|
||||
"It may not have an `__iter__` method \
|
||||
and its `__getitem__` attribute has type `{dunder_getitem_type}`, \
|
||||
which is not callable",
|
||||
dunder_getitem_type = bindings.callable_type().display(db),
|
||||
));
|
||||
}
|
||||
CallDunderError::CallError(CallErrorKind::BindingError, bindings) if bindings.is_single() => report_not_iterable(format_args!(
|
||||
"Object of type `{iterable_type}` may not be iterable \
|
||||
because it may not have an `__iter__` method \
|
||||
and its `__getitem__` method has an incorrect signature \
|
||||
for the old-style iteration protocol \
|
||||
(expected a signature at least as permissive as \
|
||||
`def __getitem__(self, key: int): ...`)",
|
||||
iterable_type = iterable_type.display(db),
|
||||
)),
|
||||
CallDunderError::CallError(CallErrorKind::BindingError, bindings) => report_not_iterable(format_args!(
|
||||
"Object of type `{iterable_type}` may not be iterable \
|
||||
because it may not have an `__iter__` method \
|
||||
and its `__getitem__` method (with type `{dunder_getitem_type}`) \
|
||||
may have an incorrect signature for the old-style iteration protocol \
|
||||
(expected a signature at least as permissive as \
|
||||
`def __getitem__(self, key: int): ...`)",
|
||||
iterable_type = iterable_type.display(db),
|
||||
dunder_getitem_type = bindings.callable_type().display(db),
|
||||
)),
|
||||
}
|
||||
CallDunderError::CallError(CallErrorKind::PossiblyNotCallable, bindings)
|
||||
if bindings.is_single() =>
|
||||
{
|
||||
reporter.may_not(
|
||||
"It may not have an `__iter__` method \
|
||||
and its `__getitem__` attribute may not be callable",
|
||||
);
|
||||
}
|
||||
CallDunderError::CallError(CallErrorKind::PossiblyNotCallable, bindings) => {
|
||||
reporter.may_not(format_args!(
|
||||
"It may not have an `__iter__` method \
|
||||
and its `__getitem__` attribute (with type `{dunder_getitem_type}`) \
|
||||
may not be callable",
|
||||
dunder_getitem_type = bindings.callable_type().display(db),
|
||||
));
|
||||
}
|
||||
CallDunderError::CallError(CallErrorKind::BindingError, bindings)
|
||||
if bindings.is_single() =>
|
||||
{
|
||||
reporter
|
||||
.may_not(
|
||||
"It may not have an `__iter__` method \
|
||||
and its `__getitem__` method has an incorrect signature \
|
||||
for the old-style iteration protocol",
|
||||
)
|
||||
.info(
|
||||
"`__getitem__` must be at least as permissive as \
|
||||
`def __getitem__(self, key: int): ...` \
|
||||
to satisfy the old-style iteration protocol",
|
||||
);
|
||||
}
|
||||
CallDunderError::CallError(CallErrorKind::BindingError, bindings) => {
|
||||
reporter
|
||||
.may_not(format_args!(
|
||||
"It may not have an `__iter__` method \
|
||||
and its `__getitem__` method (with type `{dunder_getitem_type}`) \
|
||||
may have an incorrect signature for the old-style iteration protocol",
|
||||
dunder_getitem_type = bindings.callable_type().display(db),
|
||||
))
|
||||
.info(
|
||||
"`__getitem__` must be at least as permissive as \
|
||||
`def __getitem__(self, key: int): ...` \
|
||||
to satisfy the old-style iteration protocol",
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
Self::UnboundIterAndGetitemError { dunder_getitem_error } => match dunder_getitem_error {
|
||||
CallDunderError::MethodNotAvailable => report_not_iterable(format_args!(
|
||||
"Object of type `{}` is not iterable because it doesn't have \
|
||||
an `__iter__` method or a `__getitem__` method",
|
||||
iterable_type.display(db)
|
||||
)),
|
||||
CallDunderError::PossiblyUnbound(_) => report_not_iterable(format_args!(
|
||||
"Object of type `{}` may not be iterable because it has no `__iter__` method \
|
||||
and it may not have a `__getitem__` method",
|
||||
iterable_type.display(db)
|
||||
)),
|
||||
CallDunderError::CallError(CallErrorKind::NotCallable, bindings) => report_not_iterable(format_args!(
|
||||
"Object of type `{iterable_type}` is not iterable \
|
||||
because it has no `__iter__` method and \
|
||||
its `__getitem__` attribute has type `{dunder_getitem_type}`, \
|
||||
which is not callable",
|
||||
iterable_type = iterable_type.display(db),
|
||||
dunder_getitem_type = bindings.callable_type().display(db),
|
||||
)),
|
||||
CallDunderError::CallError(CallErrorKind::PossiblyNotCallable, bindings) if bindings.is_single() => report_not_iterable(format_args!(
|
||||
"Object of type `{iterable_type}` may not be iterable \
|
||||
because it has no `__iter__` method and its `__getitem__` attribute \
|
||||
may not be callable",
|
||||
iterable_type = iterable_type.display(db),
|
||||
)),
|
||||
CallDunderError::CallError(CallErrorKind::PossiblyNotCallable, bindings) => {
|
||||
report_not_iterable(format_args!(
|
||||
"Object of type `{iterable_type}` may not be iterable \
|
||||
because it has no `__iter__` method and its `__getitem__` attribute \
|
||||
(with type `{dunder_getitem_type}`) may not be callable",
|
||||
iterable_type = iterable_type.display(db),
|
||||
Self::UnboundIterAndGetitemError {
|
||||
dunder_getitem_error,
|
||||
} => match dunder_getitem_error {
|
||||
CallDunderError::MethodNotAvailable => {
|
||||
reporter
|
||||
.is_not("It doesn't have an `__iter__` method or a `__getitem__` method");
|
||||
}
|
||||
CallDunderError::PossiblyUnbound(_) => {
|
||||
reporter.is_not(
|
||||
"It has no `__iter__` method and it may not have a `__getitem__` method",
|
||||
);
|
||||
}
|
||||
CallDunderError::CallError(CallErrorKind::NotCallable, bindings) => {
|
||||
reporter.is_not(format_args!(
|
||||
"It has no `__iter__` method and \
|
||||
its `__getitem__` attribute has type `{dunder_getitem_type}`, \
|
||||
which is not callable",
|
||||
dunder_getitem_type = bindings.callable_type().display(db),
|
||||
));
|
||||
}
|
||||
CallDunderError::CallError(CallErrorKind::BindingError, bindings) if bindings.is_single() => report_not_iterable(format_args!(
|
||||
"Object of type `{iterable_type}` is not iterable \
|
||||
because it has no `__iter__` method and \
|
||||
its `__getitem__` method has an incorrect signature \
|
||||
for the old-style iteration protocol \
|
||||
(expected a signature at least as permissive as \
|
||||
`def __getitem__(self, key: int): ...`)",
|
||||
iterable_type = iterable_type.display(db),
|
||||
)),
|
||||
CallDunderError::CallError(CallErrorKind::BindingError, bindings) => report_not_iterable(format_args!(
|
||||
"Object of type `{iterable_type}` may not be iterable \
|
||||
because it has no `__iter__` method and \
|
||||
its `__getitem__` method (with type `{dunder_getitem_type}`) \
|
||||
may have an incorrect signature for the old-style iteration protocol \
|
||||
(expected a signature at least as permissive as \
|
||||
`def __getitem__(self, key: int): ...`)",
|
||||
iterable_type = iterable_type.display(db),
|
||||
dunder_getitem_type = bindings.callable_type().display(db),
|
||||
)),
|
||||
}
|
||||
CallDunderError::CallError(CallErrorKind::PossiblyNotCallable, bindings)
|
||||
if bindings.is_single() =>
|
||||
{
|
||||
reporter.may_not(
|
||||
"It has no `__iter__` method and its `__getitem__` attribute \
|
||||
may not be callable",
|
||||
);
|
||||
}
|
||||
CallDunderError::CallError(CallErrorKind::PossiblyNotCallable, bindings) => {
|
||||
reporter.may_not(
|
||||
"It has no `__iter__` method and its `__getitem__` attribute is invalid",
|
||||
).info(format_args!(
|
||||
"`__getitem__` has type `{dunder_getitem_type}`, which is not callable",
|
||||
dunder_getitem_type = bindings.callable_type().display(db),
|
||||
));
|
||||
}
|
||||
CallDunderError::CallError(CallErrorKind::BindingError, bindings)
|
||||
if bindings.is_single() =>
|
||||
{
|
||||
reporter
|
||||
.is_not(
|
||||
"It has no `__iter__` method and \
|
||||
its `__getitem__` method has an incorrect signature \
|
||||
for the old-style iteration protocol",
|
||||
)
|
||||
.info(
|
||||
"`__getitem__` must be at least as permissive as \
|
||||
`def __getitem__(self, key: int): ...` \
|
||||
to satisfy the old-style iteration protocol",
|
||||
);
|
||||
}
|
||||
CallDunderError::CallError(CallErrorKind::BindingError, bindings) => {
|
||||
reporter
|
||||
.may_not(format_args!(
|
||||
"It has no `__iter__` method and \
|
||||
its `__getitem__` method (with type `{dunder_getitem_type}`) \
|
||||
may have an incorrect signature for the old-style iteration protocol",
|
||||
dunder_getitem_type = bindings.callable_type().display(db),
|
||||
))
|
||||
.info(
|
||||
"`__getitem__` must be at least as permissive as \
|
||||
`def __getitem__(self, key: int): ...` \
|
||||
to satisfy the old-style iteration protocol",
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue