diff --git a/crates/red_knot_python_semantic/resources/mdtest/annotations/literal.md b/crates/red_knot_python_semantic/resources/mdtest/annotations/literal.md index 4544446f0e576..b09b6f87012da 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/annotations/literal.md +++ b/crates/red_knot_python_semantic/resources/mdtest/annotations/literal.md @@ -156,7 +156,7 @@ def f(): ```py from typing import Literal -# error: [invalid-type-form] "`Literal` requires at least one argument when used in a type expression" +# error: [invalid-type-form] "`typing.Literal` requires at least one argument when used in a type expression" def _(x: Literal): reveal_type(x) # revealed: Unknown ``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/annotations/optional.md b/crates/red_knot_python_semantic/resources/mdtest/annotations/optional.md index f4a6962e7f42e..654c88b19908b 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/annotations/optional.md +++ b/crates/red_knot_python_semantic/resources/mdtest/annotations/optional.md @@ -45,3 +45,13 @@ def f(): # revealed: int | None reveal_type(a) ``` + +## Invalid + +```py +from typing import Optional + +# error: [invalid-type-form] "`typing.Optional` requires exactly one argument when used in a type expression" +def f(x: Optional) -> None: + reveal_type(x) # revealed: Unknown +``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/annotations/union.md b/crates/red_knot_python_semantic/resources/mdtest/annotations/union.md index bdae8417fbbd3..253fe6dde6499 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/annotations/union.md +++ b/crates/red_knot_python_semantic/resources/mdtest/annotations/union.md @@ -59,3 +59,13 @@ def f(): # revealed: int | str reveal_type(a) ``` + +## Invalid + +```py +from typing import Union + +# error: [invalid-type-form] "`typing.Union` requires at least one argument when used in a type expression" +def f(x: Union) -> None: + reveal_type(x) # revealed: Unknown +``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/intersection_types.md b/crates/red_knot_python_semantic/resources/mdtest/intersection_types.md index a7f0441653aa9..0879592d3cfa0 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/intersection_types.md +++ b/crates/red_knot_python_semantic/resources/mdtest/intersection_types.md @@ -846,5 +846,19 @@ def mixed( reveal_type(i4) # revealed: Any & Unknown ``` +## Invalid + +```py +from knot_extensions import Intersection, Not + +# error: [invalid-type-form] "`knot_extensions.Intersection` requires at least one argument when used in a type expression" +def f(x: Intersection) -> None: + reveal_type(x) # revealed: Unknown + +# error: [invalid-type-form] "`knot_extensions.Not` requires exactly one argument when used in a type expression" +def f(x: Not) -> None: + reveal_type(x) # revealed: Unknown +``` + [complement laws]: https://en.wikipedia.org/wiki/Complement_(set_theory) [de morgan's laws]: https://en.wikipedia.org/wiki/De_Morgan%27s_laws diff --git a/crates/red_knot_python_semantic/resources/mdtest/protocols.md b/crates/red_knot_python_semantic/resources/mdtest/protocols.md index fe14d0024f136..10853318b8038 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/protocols.md +++ b/crates/red_knot_python_semantic/resources/mdtest/protocols.md @@ -13,3 +13,13 @@ def _(some_int: int, some_literal_int: Literal[1], some_indexable: SupportsIndex b: SupportsIndex = some_literal_int c: SupportsIndex = some_indexable ``` + +## Invalid + +```py +from typing import Protocol + +# error: [invalid-type-form] "`typing.Protocol` is not allowed in type expressions" +def f(x: Protocol) -> None: + reveal_type(x) # revealed: Unknown +``` diff --git a/crates/red_knot_python_semantic/resources/mdtest/type_api.md b/crates/red_knot_python_semantic/resources/mdtest/type_api.md index 19be965d3fdc3..94018eb5bed2a 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/type_api.md +++ b/crates/red_knot_python_semantic/resources/mdtest/type_api.md @@ -392,6 +392,10 @@ def type_of_annotation() -> None: # error: "Special form `knot_extensions.TypeOf` expected exactly one type parameter" t: TypeOf[int, str, bytes] + +# error: [invalid-type-form] "`knot_extensions.TypeOf` requires exactly one argument when used in a type expression" +def f(x: TypeOf) -> None: + reveal_type(x) # revealed: Unknown ``` ## `CallableTypeFromFunction` @@ -418,6 +422,10 @@ def f3(x: int, y: str) -> None: c1: CallableTypeFromFunction[f1, f2] # error: [invalid-type-form] "Expected the first argument to `knot_extensions.CallableTypeFromFunction` to be a function literal, but got `Literal[int]`" c2: CallableTypeFromFunction[int] + +# error: [invalid-type-form] "`knot_extensions.CallableTypeFromFunction` requires exactly one argument when used in a type expression" +def f(x: CallableTypeFromFunction) -> None: + reveal_type(x) # revealed: Unknown ``` Using it in annotation to reveal the signature of the function: diff --git a/crates/red_knot_python_semantic/src/types.rs b/crates/red_knot_python_semantic/src/types.rs index bf9b23f571fcb..ea1239591c6f1 100644 --- a/crates/red_knot_python_semantic/src/types.rs +++ b/crates/red_knot_python_semantic/src/types.rs @@ -3256,10 +3256,6 @@ impl<'db> Type<'db> { ], fallback_type: Type::unknown(), }), - Type::KnownInstance(KnownInstanceType::Literal) => Err(InvalidTypeExpressionError { - invalid_expressions: smallvec::smallvec![InvalidTypeExpression::BareLiteral], - fallback_type: Type::unknown(), - }), Type::KnownInstance(KnownInstanceType::Unknown) => Ok(Type::unknown()), Type::KnownInstance(KnownInstanceType::AlwaysTruthy) => Ok(Type::AlwaysTruthy), Type::KnownInstance(KnownInstanceType::AlwaysFalsy) => Ok(Type::AlwaysFalsy), @@ -3269,7 +3265,44 @@ impl<'db> Type<'db> { GeneralCallableType::unknown(db), ))) } - Type::KnownInstance(_) => Ok(todo_type!( + Type::KnownInstance( + KnownInstanceType::Literal + | KnownInstanceType::Union + | KnownInstanceType::Intersection, + ) => Err(InvalidTypeExpressionError { + invalid_expressions: smallvec::smallvec![InvalidTypeExpression::RequiresArguments( + *self + )], + fallback_type: Type::unknown(), + }), + Type::KnownInstance( + KnownInstanceType::Optional + | KnownInstanceType::Not + | KnownInstanceType::TypeOf + | KnownInstanceType::CallableTypeFromFunction, + ) => Err(InvalidTypeExpressionError { + invalid_expressions: smallvec::smallvec![ + InvalidTypeExpression::RequiresOneArgument(*self) + ], + fallback_type: Type::unknown(), + }), + Type::KnownInstance(KnownInstanceType::Protocol) => Err(InvalidTypeExpressionError { + invalid_expressions: smallvec::smallvec![ + InvalidTypeExpression::ProtocolInTypeExpression + ], + fallback_type: Type::unknown(), + }), + Type::KnownInstance( + KnownInstanceType::TypingSelf + | KnownInstanceType::ReadOnly + | KnownInstanceType::TypeAlias + | KnownInstanceType::NotRequired + | KnownInstanceType::Concatenate + | KnownInstanceType::TypeIs + | KnownInstanceType::TypeGuard + | KnownInstanceType::Unpack + | KnownInstanceType::Required, + ) => Ok(todo_type!( "Invalid or unsupported `KnownInstanceType` in `Type::to_type_expression`" )), Type::Instance(_) => Ok(todo_type!( @@ -3542,8 +3575,12 @@ impl<'db> InvalidTypeExpressionError<'db> { enum InvalidTypeExpression<'db> { /// `x: Annotated` is invalid as an annotation BareAnnotated, - /// `x: Literal` is invalid as an annotation - BareLiteral, + /// Some types always require at least one argument when used in a type expression + RequiresArguments(Type<'db>), + /// Some types always require exactly one argument when used in a type expression + RequiresOneArgument(Type<'db>), + /// The `Protocol` type is invalid in type expressions + ProtocolInTypeExpression, /// The `ClassVar` type qualifier was used in a type expression ClassVarInTypeExpression, /// The `Final` type qualifier was used in a type expression @@ -3565,8 +3602,17 @@ impl<'db> InvalidTypeExpression<'db> { InvalidTypeExpression::BareAnnotated => f.write_str( "`Annotated` requires at least two arguments when used in an annotation or type expression" ), - InvalidTypeExpression::BareLiteral => f.write_str( - "`Literal` requires at least one argument when used in a type expression" + InvalidTypeExpression::RequiresOneArgument(ty) => write!( + f, + "`{ty}` requires exactly one argument when used in a type expression", + ty = ty.display(self.db)), + InvalidTypeExpression::RequiresArguments(ty) => write!( + f, + "`{ty}` requires at least one argument when used in a type expression", + ty = ty.display(self.db) + ), + InvalidTypeExpression::ProtocolInTypeExpression => f.write_str( + "`typing.Protocol` is not allowed in type expressions" ), InvalidTypeExpression::ClassVarInTypeExpression => f.write_str( "Type qualifier `typing.ClassVar` is not allowed in type expressions (only in annotation expressions)"