[red-knot] Fix more [redundant-cast] false positives (#17170)
Fixes #17164. Simply checking whether one type is gradually equivalent to another is too simplistic here: `Any` is gradually equivalent to `Todo`, but we should permit users to cast from `Todo` or `Unknown` to `Any` without complaining about it. This changes our logic so that we only complain about redundant casts if: - the two types are exactly equal (when normalized) OR they are equivalent (we'll still complain about `Any -> Any` casts, and about `Any | str | int` -> `str | int | Any` casts, since their normalized forms are exactly equal, even though the type is not fully static -- and therefore does not participate in equivalence relations) - AND the casted type does not contain `Todo`
This commit is contained in:
@@ -49,3 +49,22 @@ def f(x: Callable[[dict[str, int]], None], y: tuple[dict[str, int]]):
|
||||
a = cast(Callable[[list[bytes]], None], x)
|
||||
b = cast(tuple[list[bytes]], y)
|
||||
```
|
||||
|
||||
A cast from `Todo` or `Unknown` to `Any` is not considered a "redundant cast": even if these are
|
||||
understood as gradually equivalent types by red-knot, they are understood as different types by
|
||||
human readers of red-knot's output. For `Unknown` in particular, we may consider it differently in
|
||||
the context of some opt-in diagnostics, as it indicates that the gradual type has come about due to
|
||||
an invalid annotation, missing annotation or missing type argument somewhere.
|
||||
|
||||
```py
|
||||
from knot_extensions import Unknown
|
||||
|
||||
def f(x: Any, y: Unknown, z: Any | str | int):
|
||||
a = cast(dict[str, Any], x)
|
||||
reveal_type(a) # revealed: @Todo(generics)
|
||||
|
||||
b = cast(Any, y)
|
||||
reveal_type(b) # revealed: Any
|
||||
|
||||
c = cast(str | int | Any, z) # error: [redundant-cast]
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user