[red-knot] Ban most Type::Instance types in type expressions (#16872)

## Summary

Catch some Instances, but raise type error for the rest of them
Fixes #16851 

## Test Plan

Extend invalid.md in annotations

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
Matthew Mckee
2025-03-20 22:19:56 +00:00
committed by GitHub
parent 296d67a496
commit 63e78b41cd
6 changed files with 144 additions and 69 deletions

View File

@@ -9,6 +9,8 @@ import typing
from knot_extensions import AlwaysTruthy, AlwaysFalsy
from typing_extensions import Literal, Never
class A: ...
def _(
a: type[int],
b: AlwaysTruthy,
@@ -18,30 +20,34 @@ def _(
f: Literal[b"foo"],
g: tuple[int, str],
h: Never,
i: int,
j: A,
):
def foo(): ...
def invalid(
i: a, # error: [invalid-type-form] "Variable of type `type[int]` is not allowed in a type expression"
j: b, # error: [invalid-type-form]
k: c, # error: [invalid-type-form]
l: d, # error: [invalid-type-form]
m: e, # error: [invalid-type-form]
n: f, # error: [invalid-type-form]
o: g, # error: [invalid-type-form]
p: h, # error: [invalid-type-form]
q: typing, # error: [invalid-type-form]
r: foo, # error: [invalid-type-form]
a_: a, # error: [invalid-type-form] "Variable of type `type[int]` is not allowed in a type expression"
b_: b, # error: [invalid-type-form]
c_: c, # error: [invalid-type-form]
d_: d, # error: [invalid-type-form]
e_: e, # error: [invalid-type-form]
f_: f, # error: [invalid-type-form]
g_: g, # error: [invalid-type-form]
h_: h, # error: [invalid-type-form]
i_: typing, # error: [invalid-type-form]
j_: foo, # error: [invalid-type-form]
k_: i, # error: [invalid-type-form] "Variable of type `int` is not allowed in a type expression"
l_: j, # error: [invalid-type-form] "Variable of type `A` is not allowed in a type expression"
):
reveal_type(i) # revealed: Unknown
reveal_type(j) # revealed: Unknown
reveal_type(k) # revealed: Unknown
reveal_type(l) # revealed: Unknown
reveal_type(m) # revealed: Unknown
reveal_type(n) # revealed: Unknown
reveal_type(o) # revealed: Unknown
reveal_type(p) # revealed: Unknown
reveal_type(q) # revealed: Unknown
reveal_type(r) # revealed: Unknown
reveal_type(a_) # revealed: Unknown
reveal_type(b_) # revealed: Unknown
reveal_type(c_) # revealed: Unknown
reveal_type(d_) # revealed: Unknown
reveal_type(e_) # revealed: Unknown
reveal_type(f_) # revealed: Unknown
reveal_type(g_) # revealed: Unknown
reveal_type(h_) # revealed: Unknown
reveal_type(i_) # revealed: Unknown
reveal_type(j_) # revealed: Unknown
```
## Invalid AST nodes

View File

@@ -0,0 +1,20 @@
# NewType
Currently, red-knot doesn't support `typing.NewType` in type annotations.
## Valid forms
```py
from typing_extensions import NewType
from types import GenericAlias
A = NewType("A", int)
B = GenericAlias(A, ())
def _(
a: A,
b: B,
):
reveal_type(a) # revealed: @Todo(Support for `typing.NewType` instances in type expressions)
reveal_type(b) # revealed: @Todo(Support for `typing.GenericAlias` instances in type expressions)
```

View File

@@ -43,18 +43,22 @@ class Foo:
One thing that is supported is error messages for using special forms in type expressions.
```py
from typing_extensions import Unpack, TypeGuard, TypeIs, Concatenate
from typing_extensions import Unpack, TypeGuard, TypeIs, Concatenate, ParamSpec
def _(
a: Unpack, # error: [invalid-type-form] "`typing.Unpack` requires exactly one argument when used in a type expression"
b: TypeGuard, # error: [invalid-type-form] "`typing.TypeGuard` requires exactly one argument when used in a type expression"
c: TypeIs, # error: [invalid-type-form] "`typing.TypeIs` requires exactly one argument when used in a type expression"
d: Concatenate, # error: [invalid-type-form] "`typing.Concatenate` requires at least two arguments when used in a type expression"
e: ParamSpec,
) -> None:
reveal_type(a) # revealed: Unknown
reveal_type(b) # revealed: Unknown
reveal_type(c) # revealed: Unknown
reveal_type(d) # revealed: Unknown
def foo(a_: e) -> None:
reveal_type(a_) # revealed: @Todo(Support for `typing.ParamSpec` instances in type expressions)
```
## Inheritance

View File

@@ -113,7 +113,7 @@ class Legacy(Generic[T]):
legacy: Legacy[int] = Legacy()
# TODO: revealed: str
reveal_type(legacy.m(1, "string")) # revealed: @Todo(Invalid or unsupported `Instance` in `Type::to_type_expression`)
reveal_type(legacy.m(1, "string")) # revealed: @Todo(Support for `typing.TypeVar` instances in type expressions)
```
With PEP 695 syntax, it is clearer that the method uses a separate typevar: