Rename Red Knot (#17820)

This commit is contained in:
Micha Reiser 2025-05-03 19:49:15 +02:00 committed by GitHub
parent e6a798b962
commit b51c4f82ea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
1564 changed files with 1598 additions and 1578 deletions

View file

@ -0,0 +1,47 @@
# If expressions
## Simple if-expression
```py
def _(flag: bool):
x = 1 if flag else 2
reveal_type(x) # revealed: Literal[1, 2]
```
## If-expression with walrus operator
```py
def _(flag: bool):
y = 0
z = 0
x = (y := 1) if flag else (z := 2)
reveal_type(x) # revealed: Literal[1, 2]
reveal_type(y) # revealed: Literal[0, 1]
reveal_type(z) # revealed: Literal[0, 2]
```
## Nested if-expression
```py
def _(flag: bool, flag2: bool):
x = 1 if flag else 2 if flag2 else 3
reveal_type(x) # revealed: Literal[1, 2, 3]
```
## None
```py
def _(flag: bool):
x = 1 if flag else None
reveal_type(x) # revealed: Literal[1] | None
```
## Condition with object that implements `__bool__` incorrectly
```py
class NotBoolable:
__bool__: int = 3
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `NotBoolable`"
3 if NotBoolable() else 4
```

View file

@ -0,0 +1,163 @@
# If statements
## Simple if
```py
def _(flag: bool):
y = 1
y = 2
if flag:
y = 3
reveal_type(y) # revealed: Literal[2, 3]
```
## Simple if-elif-else
```py
def _(flag: bool, flag2: bool):
y = 1
y = 2
if flag:
y = 3
elif flag2:
y = 4
else:
r = y
y = 5
s = y
x = y
reveal_type(x) # revealed: Literal[3, 4, 5]
# revealed: Literal[2]
# error: [possibly-unresolved-reference]
reveal_type(r)
# revealed: Literal[5]
# error: [possibly-unresolved-reference]
reveal_type(s)
```
## Single symbol across if-elif-else
```py
def _(flag: bool, flag2: bool):
if flag:
y = 1
elif flag2:
y = 2
else:
y = 3
reveal_type(y) # revealed: Literal[1, 2, 3]
```
## if-elif-else without else assignment
```py
def _(flag: bool, flag2: bool):
y = 0
if flag:
y = 1
elif flag2:
y = 2
else:
pass
reveal_type(y) # revealed: Literal[0, 1, 2]
```
## if-elif-else with intervening assignment
```py
def _(flag: bool, flag2: bool):
y = 0
if flag:
y = 1
z = 3
elif flag2:
y = 2
else:
pass
reveal_type(y) # revealed: Literal[0, 1, 2]
```
## Nested if statement
```py
def _(flag: bool, flag2: bool):
y = 0
if flag:
if flag2:
y = 1
reveal_type(y) # revealed: Literal[0, 1]
```
## if-elif without else
```py
def _(flag: bool, flag2: bool):
y = 1
y = 2
if flag:
y = 3
elif flag2:
y = 4
reveal_type(y) # revealed: Literal[2, 3, 4]
```
## if-elif with assignment expressions in tests
```py
def check(x: int) -> bool:
return bool(x)
if check(x := 1):
x = 2
elif check(x := 3):
x = 4
reveal_type(x) # revealed: Literal[2, 3, 4]
```
## constraints apply to later test expressions
```py
def check(x) -> bool:
return bool(x)
def _(flag: bool):
x = 1 if flag else None
y = 0
if x is None:
pass
elif check(y := x):
pass
reveal_type(y) # revealed: Literal[0, 1]
```
## Condition with object that implements `__bool__` incorrectly
```py
class NotBoolable:
__bool__: int = 3
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `NotBoolable`"
if NotBoolable():
...
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `NotBoolable`"
elif NotBoolable():
...
```

View file

@ -0,0 +1,302 @@
# Pattern matching
```toml
[environment]
python-version = "3.10"
```
## With wildcard
```py
def _(target: int):
match target:
case 1:
y = 2
case _:
y = 3
reveal_type(y) # revealed: Literal[2, 3]
```
## Without wildcard
```py
def _(target: int):
match target:
case 1:
y = 2
case 2:
y = 3
# revealed: Literal[2, 3]
# error: [possibly-unresolved-reference]
reveal_type(y)
```
## Basic match
```py
def _(target: int):
y = 1
y = 2
match target:
case 1:
y = 3
case 2:
y = 4
reveal_type(y) # revealed: Literal[2, 3, 4]
```
## Value match
A value pattern matches based on equality: the first `case` branch here will be taken if `subject`
is equal to `2`, even if `subject` is not an instance of `int`. We can't know whether `C` here has a
custom `__eq__` implementation that might cause it to compare equal to `2`, so we have to consider
the possibility that the `case` branch might be taken even though the type `C` is disjoint from the
type `Literal[2]`.
This leads us to infer `Literal[1, 3]` as the type of `y` after the `match` statement, rather than
`Literal[1]`:
```py
from typing import final
@final
class C:
pass
def _(subject: C):
y = 1
match subject:
case 2:
y = 3
reveal_type(y) # revealed: Literal[1, 3]
```
## Class match
A `case` branch with a class pattern is taken if the subject is an instance of the given class, and
all subpatterns in the class pattern match.
```py
from typing import final
class Foo:
pass
class FooSub(Foo):
pass
class Bar:
pass
@final
class Baz:
pass
def _(target: FooSub):
y = 1
match target:
case Baz():
y = 2
case Foo():
y = 3
case Bar():
y = 4
reveal_type(y) # revealed: Literal[3]
def _(target: FooSub):
y = 1
match target:
case Baz():
y = 2
case Bar():
y = 3
case Foo():
y = 4
reveal_type(y) # revealed: Literal[3, 4]
def _(target: FooSub | str):
y = 1
match target:
case Baz():
y = 2
case Foo():
y = 3
case Bar():
y = 4
reveal_type(y) # revealed: Literal[1, 3, 4]
```
## Singleton match
Singleton patterns are matched based on identity, not equality comparisons or `isinstance()` checks.
```py
from typing import Literal
def _(target: Literal[True, False]):
y = 1
match target:
case True:
y = 2
case False:
y = 3
case None:
y = 4
# TODO: with exhaustiveness checking, this should be Literal[2, 3]
reveal_type(y) # revealed: Literal[1, 2, 3]
def _(target: bool):
y = 1
match target:
case True:
y = 2
case False:
y = 3
case None:
y = 4
# TODO: with exhaustiveness checking, this should be Literal[2, 3]
reveal_type(y) # revealed: Literal[1, 2, 3]
def _(target: None):
y = 1
match target:
case True:
y = 2
case False:
y = 3
case None:
y = 4
reveal_type(y) # revealed: Literal[4]
def _(target: None | Literal[True]):
y = 1
match target:
case True:
y = 2
case False:
y = 3
case None:
y = 4
# TODO: with exhaustiveness checking, this should be Literal[2, 4]
reveal_type(y) # revealed: Literal[1, 2, 4]
# bool is an int subclass
def _(target: int):
y = 1
match target:
case True:
y = 2
case False:
y = 3
case None:
y = 4
reveal_type(y) # revealed: Literal[1, 2, 3]
def _(target: str):
y = 1
match target:
case True:
y = 2
case False:
y = 3
case None:
y = 4
reveal_type(y) # revealed: Literal[1]
```
## Or match
A `|` pattern matches if any of the subpatterns match.
```py
from typing import Literal, final
def _(target: Literal["foo", "baz"]):
y = 1
match target:
case "foo" | "bar":
y = 2
case "baz":
y = 3
# TODO: with exhaustiveness, this should be Literal[2, 3]
reveal_type(y) # revealed: Literal[1, 2, 3]
def _(target: None):
y = 1
match target:
case None | 3:
y = 2
case "foo" | 4 | True:
y = 3
reveal_type(y) # revealed: Literal[2]
@final
class Baz:
pass
def _(target: int | None | float):
y = 1
match target:
case None | 3:
y = 2
case Baz():
y = 3
reveal_type(y) # revealed: Literal[1, 2]
def _(target: None | str):
y = 1
match target:
case Baz() | True | False:
y = 2
case int():
y = 3
reveal_type(y) # revealed: Literal[1, 3]
```
## Guard with object that implements `__bool__` incorrectly
```py
class NotBoolable:
__bool__: int = 3
def _(target: int, flag: NotBoolable):
y = 1
match target:
# error: [unsupported-bool-conversion] "Boolean conversion is unsupported for type `NotBoolable`"
case 1 if flag:
y = 2
case 2:
y = 3
reveal_type(y) # revealed: Literal[1, 2, 3]
```