red_knot_python_semantic: improve diagnostics for unsupported boolean conversions
This mostly only improves things for incorrect arguments and for an incorrect return type. It doesn't do much to improve the case where `__bool__` isn't callable and leaves the union/other cases untouched completely. I picked this one because, at first glance, this _looked_ like a lower hanging fruit. The conceptual improvement here is pretty straight-forward: add annotations for relevant data. But it took me a bit to figure out how to connect all of the pieces.
This commit is contained in:
committed by
Andrew Gallant
parent
eb1d2518c1
commit
0f47810768
@@ -42,6 +42,6 @@ def _(flag: bool):
|
||||
class NotBoolable:
|
||||
__bool__: int = 3
|
||||
|
||||
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `NotBoolable`; its `__bool__` method isn't callable"
|
||||
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `NotBoolable`"
|
||||
3 if NotBoolable() else 4
|
||||
```
|
||||
|
||||
@@ -154,10 +154,10 @@ def _(flag: bool):
|
||||
class NotBoolable:
|
||||
__bool__: int = 3
|
||||
|
||||
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `NotBoolable`; its `__bool__` method isn't callable"
|
||||
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `NotBoolable`"
|
||||
if NotBoolable():
|
||||
...
|
||||
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `NotBoolable`; its `__bool__` method isn't callable"
|
||||
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `NotBoolable`"
|
||||
elif NotBoolable():
|
||||
...
|
||||
```
|
||||
|
||||
@@ -292,7 +292,7 @@ class NotBoolable:
|
||||
def _(target: int, flag: NotBoolable):
|
||||
y = 1
|
||||
match target:
|
||||
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `NotBoolable`; its `__bool__` method isn't callable"
|
||||
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `NotBoolable`"
|
||||
case 1 if flag:
|
||||
y = 2
|
||||
case 2:
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
class NotBoolable:
|
||||
__bool__: int = 3
|
||||
|
||||
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `NotBoolable`; its `__bool__` method isn't callable"
|
||||
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `NotBoolable`"
|
||||
assert NotBoolable()
|
||||
```
|
||||
|
||||
@@ -123,7 +123,7 @@ if NotBoolable():
|
||||
class NotBoolable:
|
||||
__bool__: None = None
|
||||
|
||||
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `NotBoolable`; its `__bool__` method isn't callable"
|
||||
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `NotBoolable`"
|
||||
if NotBoolable():
|
||||
...
|
||||
```
|
||||
@@ -135,7 +135,7 @@ def test(cond: bool):
|
||||
class NotBoolable:
|
||||
__bool__: int | None = None if cond else 3
|
||||
|
||||
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `NotBoolable`; its `__bool__` method isn't callable"
|
||||
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `NotBoolable`"
|
||||
if NotBoolable():
|
||||
...
|
||||
```
|
||||
@@ -149,7 +149,7 @@ def test(cond: bool):
|
||||
|
||||
a = 10 if cond else NotBoolable()
|
||||
|
||||
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `Literal[10] | NotBoolable`; its `__bool__` method isn't callable"
|
||||
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `Literal[10] | NotBoolable`"
|
||||
if a:
|
||||
...
|
||||
```
|
||||
|
||||
@@ -123,7 +123,7 @@ def _(flag: bool, flag2: bool):
|
||||
class NotBoolable:
|
||||
__bool__: int = 3
|
||||
|
||||
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `NotBoolable`; its `__bool__` method isn't callable"
|
||||
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `NotBoolable`"
|
||||
while NotBoolable():
|
||||
...
|
||||
```
|
||||
|
||||
@@ -270,7 +270,7 @@ def _(
|
||||
if af:
|
||||
reveal_type(af) # revealed: type[AmbiguousClass] & ~AlwaysFalsy
|
||||
|
||||
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `MetaDeferred`; the return type of its bool method (`MetaAmbiguous`) isn't assignable to `bool"
|
||||
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `MetaDeferred`"
|
||||
if d:
|
||||
# TODO: Should be `Unknown`
|
||||
reveal_type(d) # revealed: type[DeferredClass] & ~AlwaysFalsy
|
||||
|
||||
@@ -24,12 +24,13 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/binary/instances.m
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:unsupported-bool-conversion: Boolean conversion is unsupported for type `NotBoolable`; its `__bool__` method isn't callable
|
||||
error: lint:unsupported-bool-conversion: Boolean conversion is unsupported for type `NotBoolable`
|
||||
--> /src/mdtest_snippet.py:7:8
|
||||
|
|
||||
6 | # error: [unsupported-bool-conversion]
|
||||
7 | 10 and a and True
|
||||
| ^
|
||||
|
|
||||
info: `__bool__` on `NotBoolable` must be callable
|
||||
|
||||
```
|
||||
|
||||
@@ -28,7 +28,7 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/comparison/instanc
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:unsupported-bool-conversion: Boolean conversion is unsupported for type `NotBoolable`; its `__bool__` method isn't callable
|
||||
error: lint:unsupported-bool-conversion: Boolean conversion is unsupported for type `NotBoolable`
|
||||
--> /src/mdtest_snippet.py:9:1
|
||||
|
|
||||
8 | # error: [unsupported-bool-conversion]
|
||||
@@ -37,11 +37,12 @@ error: lint:unsupported-bool-conversion: Boolean conversion is unsupported for t
|
||||
10 | # error: [unsupported-bool-conversion]
|
||||
11 | 10 not in WithContains()
|
||||
|
|
||||
info: `__bool__` on `NotBoolable` must be callable
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error: lint:unsupported-bool-conversion: Boolean conversion is unsupported for type `NotBoolable`; its `__bool__` method isn't callable
|
||||
error: lint:unsupported-bool-conversion: Boolean conversion is unsupported for type `NotBoolable`
|
||||
--> /src/mdtest_snippet.py:11:1
|
||||
|
|
||||
9 | 10 in WithContains()
|
||||
@@ -49,5 +50,6 @@ error: lint:unsupported-bool-conversion: Boolean conversion is unsupported for t
|
||||
11 | 10 not in WithContains()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
info: `__bool__` on `NotBoolable` must be callable
|
||||
|
||||
```
|
||||
|
||||
@@ -22,12 +22,13 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/unary/not.md
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:unsupported-bool-conversion: Boolean conversion is unsupported for type `NotBoolable`; its `__bool__` method isn't callable
|
||||
error: lint:unsupported-bool-conversion: Boolean conversion is unsupported for type `NotBoolable`
|
||||
--> /src/mdtest_snippet.py:5:1
|
||||
|
|
||||
4 | # error: [unsupported-bool-conversion]
|
||||
5 | not NotBoolable()
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
info: `__bool__` on `NotBoolable` must be callable
|
||||
|
||||
```
|
||||
|
||||
@@ -33,7 +33,7 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/comparison/instanc
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:unsupported-bool-conversion: Boolean conversion is unsupported for type `NotBoolable`; its `__bool__` method isn't callable
|
||||
error: lint:unsupported-bool-conversion: Boolean conversion is unsupported for type `NotBoolable`
|
||||
--> /src/mdtest_snippet.py:12:1
|
||||
|
|
||||
11 | # error: [unsupported-bool-conversion]
|
||||
@@ -42,11 +42,12 @@ error: lint:unsupported-bool-conversion: Boolean conversion is unsupported for t
|
||||
13 | # error: [unsupported-bool-conversion]
|
||||
14 | 10 < Comparable() < Comparable()
|
||||
|
|
||||
info: `__bool__` on `NotBoolable` must be callable
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error: lint:unsupported-bool-conversion: Boolean conversion is unsupported for type `NotBoolable`; its `__bool__` method isn't callable
|
||||
error: lint:unsupported-bool-conversion: Boolean conversion is unsupported for type `NotBoolable`
|
||||
--> /src/mdtest_snippet.py:14:1
|
||||
|
|
||||
12 | 10 < Comparable() < 20
|
||||
@@ -56,5 +57,6 @@ error: lint:unsupported-bool-conversion: Boolean conversion is unsupported for t
|
||||
15 |
|
||||
16 | Comparable() < Comparable() # fine
|
||||
|
|
||||
info: `__bool__` on `NotBoolable` must be callable
|
||||
|
||||
```
|
||||
|
||||
@@ -34,7 +34,7 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/comparison/tuples.
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:unsupported-bool-conversion: Boolean conversion is unsupported for type `NotBoolable | Literal[False]`; its `__bool__` method isn't callable
|
||||
error: lint:unsupported-bool-conversion: Boolean conversion is unsupported for type `NotBoolable | Literal[False]`
|
||||
--> /src/mdtest_snippet.py:15:1
|
||||
|
|
||||
14 | # error: [unsupported-bool-conversion]
|
||||
@@ -43,5 +43,6 @@ error: lint:unsupported-bool-conversion: Boolean conversion is unsupported for t
|
||||
16 |
|
||||
17 | a < b # fine
|
||||
|
|
||||
info: `__bool__` on `NotBoolable | Literal[False]` must be callable
|
||||
|
||||
```
|
||||
|
||||
@@ -26,12 +26,13 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/comparison/tuples.
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:unsupported-bool-conversion: Boolean conversion is unsupported for type `NotBoolable`; its `__bool__` method isn't callable
|
||||
error: lint:unsupported-bool-conversion: Boolean conversion is unsupported for type `NotBoolable`
|
||||
--> /src/mdtest_snippet.py:9:1
|
||||
|
|
||||
8 | # error: [unsupported-bool-conversion]
|
||||
9 | (A(),) == (A(),)
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
info: `__bool__` on `NotBoolable` must be callable
|
||||
|
||||
```
|
||||
|
||||
@@ -24,12 +24,13 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/unsupp
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:unsupported-bool-conversion: Boolean conversion is unsupported for type `NotBoolable`; its `__bool__` method isn't callable
|
||||
error: lint:unsupported-bool-conversion: Boolean conversion is unsupported for type `NotBoolable`
|
||||
--> /src/mdtest_snippet.py:7:8
|
||||
|
|
||||
6 | # error: [unsupported-bool-conversion]
|
||||
7 | 10 and a and True
|
||||
| ^
|
||||
|
|
||||
info: `__bool__` must be callable
|
||||
|
||||
```
|
||||
|
||||
@@ -25,12 +25,22 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/unsupp
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:unsupported-bool-conversion: Boolean conversion is unsupported for type `NotBoolable`; the return type of its bool method (`str`) isn't assignable to `bool
|
||||
error: lint:unsupported-bool-conversion: Boolean conversion is unsupported for type `NotBoolable`
|
||||
--> /src/mdtest_snippet.py:8:8
|
||||
|
|
||||
7 | # error: [unsupported-bool-conversion]
|
||||
8 | 10 and a and True
|
||||
| ^
|
||||
|
|
||||
info: `str` is not assignable to `bool`
|
||||
--> /src/mdtest_snippet.py:2:9
|
||||
|
|
||||
1 | class NotBoolable:
|
||||
2 | def __bool__(self) -> str:
|
||||
| -------- ^^^ Incorrect return type
|
||||
| |
|
||||
| Method defined here
|
||||
3 | return "wat"
|
||||
|
|
||||
|
||||
```
|
||||
|
||||
@@ -25,12 +25,22 @@ mdtest path: crates/red_knot_python_semantic/resources/mdtest/diagnostics/unsupp
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
error: lint:unsupported-bool-conversion: Boolean conversion is unsupported for type `NotBoolable`; it incorrectly implements `__bool__`
|
||||
error: lint:unsupported-bool-conversion: Boolean conversion is unsupported for type `NotBoolable`
|
||||
--> /src/mdtest_snippet.py:8:8
|
||||
|
|
||||
7 | # error: [unsupported-bool-conversion]
|
||||
8 | 10 and a and True
|
||||
| ^
|
||||
|
|
||||
info: `__bool__` methods must only have a `self` parameter
|
||||
--> /src/mdtest_snippet.py:2:9
|
||||
|
|
||||
1 | class NotBoolable:
|
||||
2 | def __bool__(self, foo):
|
||||
| --------^^^^^^^^^^^ Incorrect parameters
|
||||
| |
|
||||
| Method defined here
|
||||
3 | return False
|
||||
|
|
||||
|
||||
```
|
||||
|
||||
@@ -235,7 +235,7 @@ class InvalidBoolDunder:
|
||||
def __bool__(self) -> int:
|
||||
return 1
|
||||
|
||||
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `InvalidBoolDunder`; the return type of its bool method (`int`) isn't assignable to `bool"
|
||||
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `InvalidBoolDunder`"
|
||||
static_assert(InvalidBoolDunder())
|
||||
```
|
||||
|
||||
|
||||
@@ -187,7 +187,7 @@ class MethodBoolInvalid:
|
||||
def __bool__(self) -> int:
|
||||
return 0
|
||||
|
||||
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `MethodBoolInvalid`; the return type of its bool method (`int`) isn't assignable to `bool"
|
||||
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `MethodBoolInvalid`"
|
||||
# revealed: bool
|
||||
reveal_type(not MethodBoolInvalid())
|
||||
|
||||
|
||||
Reference in New Issue
Block a user