Skip to content

Commit 3a5f1d4

Browse files
[red-knot] Make' Type::in_type_expression()' exhaustive for Type::KnownInstance (#16836)
<!-- Thank you for contributing to Ruff! To help us out with reviewing, please consider the following: - Does this pull request include a summary of the change? (See below.) - Does this pull request include a descriptive title? - Does this pull request include references to any relevant issues? --> ## Summary fixes #15048 We want to handle more types from Type::KnownInstance ## Test Plan Add tests for each type added explicitly in the match --------- Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
1 parent f3f3e55 commit 3a5f1d4

File tree

7 files changed

+108
-10
lines changed

7 files changed

+108
-10
lines changed

crates/red_knot_python_semantic/resources/mdtest/annotations/literal.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ def f():
156156
```py
157157
from typing import Literal
158158

159-
# error: [invalid-type-form] "`Literal` requires at least one argument when used in a type expression"
159+
# error: [invalid-type-form] "`typing.Literal` requires at least one argument when used in a type expression"
160160
def _(x: Literal):
161161
reveal_type(x) # revealed: Unknown
162162
```

crates/red_knot_python_semantic/resources/mdtest/annotations/optional.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,13 @@ def f():
4545
# revealed: int | None
4646
reveal_type(a)
4747
```
48+
49+
## Invalid
50+
51+
```py
52+
from typing import Optional
53+
54+
# error: [invalid-type-form] "`typing.Optional` requires exactly one argument when used in a type expression"
55+
def f(x: Optional) -> None:
56+
reveal_type(x) # revealed: Unknown
57+
```

crates/red_knot_python_semantic/resources/mdtest/annotations/union.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,13 @@ def f():
5959
# revealed: int | str
6060
reveal_type(a)
6161
```
62+
63+
## Invalid
64+
65+
```py
66+
from typing import Union
67+
68+
# error: [invalid-type-form] "`typing.Union` requires at least one argument when used in a type expression"
69+
def f(x: Union) -> None:
70+
reveal_type(x) # revealed: Unknown
71+
```

crates/red_knot_python_semantic/resources/mdtest/intersection_types.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,5 +846,19 @@ def mixed(
846846
reveal_type(i4) # revealed: Any & Unknown
847847
```
848848

849+
## Invalid
850+
851+
```py
852+
from knot_extensions import Intersection, Not
853+
854+
# error: [invalid-type-form] "`knot_extensions.Intersection` requires at least one argument when used in a type expression"
855+
def f(x: Intersection) -> None:
856+
reveal_type(x) # revealed: Unknown
857+
858+
# error: [invalid-type-form] "`knot_extensions.Not` requires exactly one argument when used in a type expression"
859+
def f(x: Not) -> None:
860+
reveal_type(x) # revealed: Unknown
861+
```
862+
849863
[complement laws]: https://en.wikipedia.org/wiki/Complement_(set_theory)
850864
[de morgan's laws]: https://en.wikipedia.org/wiki/De_Morgan%27s_laws

crates/red_knot_python_semantic/resources/mdtest/protocols.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,13 @@ def _(some_int: int, some_literal_int: Literal[1], some_indexable: SupportsIndex
1313
b: SupportsIndex = some_literal_int
1414
c: SupportsIndex = some_indexable
1515
```
16+
17+
## Invalid
18+
19+
```py
20+
from typing import Protocol
21+
22+
# error: [invalid-type-form] "`typing.Protocol` is not allowed in type expressions"
23+
def f(x: Protocol) -> None:
24+
reveal_type(x) # revealed: Unknown
25+
```

crates/red_knot_python_semantic/resources/mdtest/type_api.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,10 @@ def type_of_annotation() -> None:
392392

393393
# error: "Special form `knot_extensions.TypeOf` expected exactly one type parameter"
394394
t: TypeOf[int, str, bytes]
395+
396+
# error: [invalid-type-form] "`knot_extensions.TypeOf` requires exactly one argument when used in a type expression"
397+
def f(x: TypeOf) -> None:
398+
reveal_type(x) # revealed: Unknown
395399
```
396400

397401
## `CallableTypeFromFunction`
@@ -418,6 +422,10 @@ def f3(x: int, y: str) -> None:
418422
c1: CallableTypeFromFunction[f1, f2]
419423
# error: [invalid-type-form] "Expected the first argument to `knot_extensions.CallableTypeFromFunction` to be a function literal, but got `Literal[int]`"
420424
c2: CallableTypeFromFunction[int]
425+
426+
# error: [invalid-type-form] "`knot_extensions.CallableTypeFromFunction` requires exactly one argument when used in a type expression"
427+
def f(x: CallableTypeFromFunction) -> None:
428+
reveal_type(x) # revealed: Unknown
421429
```
422430

423431
Using it in annotation to reveal the signature of the function:

crates/red_knot_python_semantic/src/types.rs

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3276,10 +3276,6 @@ impl<'db> Type<'db> {
32763276
],
32773277
fallback_type: Type::unknown(),
32783278
}),
3279-
Type::KnownInstance(KnownInstanceType::Literal) => Err(InvalidTypeExpressionError {
3280-
invalid_expressions: smallvec::smallvec![InvalidTypeExpression::BareLiteral],
3281-
fallback_type: Type::unknown(),
3282-
}),
32833279
Type::KnownInstance(KnownInstanceType::Unknown) => Ok(Type::unknown()),
32843280
Type::KnownInstance(KnownInstanceType::AlwaysTruthy) => Ok(Type::AlwaysTruthy),
32853281
Type::KnownInstance(KnownInstanceType::AlwaysFalsy) => Ok(Type::AlwaysFalsy),
@@ -3289,7 +3285,44 @@ impl<'db> Type<'db> {
32893285
GeneralCallableType::unknown(db),
32903286
)))
32913287
}
3292-
Type::KnownInstance(_) => Ok(todo_type!(
3288+
Type::KnownInstance(
3289+
KnownInstanceType::Literal
3290+
| KnownInstanceType::Union
3291+
| KnownInstanceType::Intersection,
3292+
) => Err(InvalidTypeExpressionError {
3293+
invalid_expressions: smallvec::smallvec![InvalidTypeExpression::RequiresArguments(
3294+
*self
3295+
)],
3296+
fallback_type: Type::unknown(),
3297+
}),
3298+
Type::KnownInstance(
3299+
KnownInstanceType::Optional
3300+
| KnownInstanceType::Not
3301+
| KnownInstanceType::TypeOf
3302+
| KnownInstanceType::CallableTypeFromFunction,
3303+
) => Err(InvalidTypeExpressionError {
3304+
invalid_expressions: smallvec::smallvec![
3305+
InvalidTypeExpression::RequiresOneArgument(*self)
3306+
],
3307+
fallback_type: Type::unknown(),
3308+
}),
3309+
Type::KnownInstance(KnownInstanceType::Protocol) => Err(InvalidTypeExpressionError {
3310+
invalid_expressions: smallvec::smallvec![
3311+
InvalidTypeExpression::ProtocolInTypeExpression
3312+
],
3313+
fallback_type: Type::unknown(),
3314+
}),
3315+
Type::KnownInstance(
3316+
KnownInstanceType::TypingSelf
3317+
| KnownInstanceType::ReadOnly
3318+
| KnownInstanceType::TypeAlias
3319+
| KnownInstanceType::NotRequired
3320+
| KnownInstanceType::Concatenate
3321+
| KnownInstanceType::TypeIs
3322+
| KnownInstanceType::TypeGuard
3323+
| KnownInstanceType::Unpack
3324+
| KnownInstanceType::Required,
3325+
) => Ok(todo_type!(
32933326
"Invalid or unsupported `KnownInstanceType` in `Type::to_type_expression`"
32943327
)),
32953328
Type::Instance(_) => Ok(todo_type!(
@@ -3562,8 +3595,12 @@ impl<'db> InvalidTypeExpressionError<'db> {
35623595
enum InvalidTypeExpression<'db> {
35633596
/// `x: Annotated` is invalid as an annotation
35643597
BareAnnotated,
3565-
/// `x: Literal` is invalid as an annotation
3566-
BareLiteral,
3598+
/// Some types always require at least one argument when used in a type expression
3599+
RequiresArguments(Type<'db>),
3600+
/// Some types always require exactly one argument when used in a type expression
3601+
RequiresOneArgument(Type<'db>),
3602+
/// The `Protocol` type is invalid in type expressions
3603+
ProtocolInTypeExpression,
35673604
/// The `ClassVar` type qualifier was used in a type expression
35683605
ClassVarInTypeExpression,
35693606
/// The `Final` type qualifier was used in a type expression
@@ -3585,8 +3622,17 @@ impl<'db> InvalidTypeExpression<'db> {
35853622
InvalidTypeExpression::BareAnnotated => f.write_str(
35863623
"`Annotated` requires at least two arguments when used in an annotation or type expression"
35873624
),
3588-
InvalidTypeExpression::BareLiteral => f.write_str(
3589-
"`Literal` requires at least one argument when used in a type expression"
3625+
InvalidTypeExpression::RequiresOneArgument(ty) => write!(
3626+
f,
3627+
"`{ty}` requires exactly one argument when used in a type expression",
3628+
ty = ty.display(self.db)),
3629+
InvalidTypeExpression::RequiresArguments(ty) => write!(
3630+
f,
3631+
"`{ty}` requires at least one argument when used in a type expression",
3632+
ty = ty.display(self.db)
3633+
),
3634+
InvalidTypeExpression::ProtocolInTypeExpression => f.write_str(
3635+
"`typing.Protocol` is not allowed in type expressions"
35903636
),
35913637
InvalidTypeExpression::ClassVarInTypeExpression => f.write_str(
35923638
"Type qualifier `typing.ClassVar` is not allowed in type expressions (only in annotation expressions)"

0 commit comments

Comments
 (0)