-
Notifications
You must be signed in to change notification settings - Fork 1.6k
[red-knot] Make' Type::in_type_expression()' exhaustive for Type::KnownInstance #16836
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1042121
50fbcf4
6306fd8
dcf394b
cd16ad6
6079ee4
5a17838
8b28a93
ff73a7f
fb56dc2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Personally I feel like the |
||
| /// 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)" | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the only two of these that are valid in type expressions when they are not parameterized are
TypingSelfandTypeAlias:ReadOnly,NotRequired,TypeIs,TypeGuard,UnpackandRequiredall require exactly one argumentConcatenaterequires at least two argumentsThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ReadOnly,NotRequiredandRequiredare also type qualifiers, so they are only valid in annotation expressions, not type expressions, even when they do have the necessary number of argumentsThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
arent ReadOnly, NotRequired, TypeIs, TypeGuard, Unpack and Required only allowed in certain places though? Like how ClassVar and Final are invalid here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's correct. But the
KnownInstancebranches of this method are essentially asking the question, "Is this symbol valid in a type expression if it appears without any type arguments?". And the answer is definitely "no" for all ofReadOnly,NotRequired,TypeIs,TypeGuard,UnpackandRequired. It's true that these symbols wouldn't even be allowed in a all type expressions even if they did have the correct number of type arguments. But that's not the question that this method is asking ;)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In terms of the exact error messages we should emit, I think probably:
ReadOnly,NotRequired,Required=>"Type qualifier `{}` is not allowed in type expressions (only in annotation expressions, and only with exactly one argument)"TypeIs,TypeGuard,Unpack=>"`{}` requires exactly one argument when used in a type expression"InvalidTypeExpression::RequiresOneArgumentvariant for theseConcatenate=>"`{}` requires at least two arguments when used in a type expression"InvalidTypeExpression::BareAnnotatedvariant so that it can be used to report errors forConcatenateas well asAnnotatedWould you be interested in filing a followup PR? :-)
It could also be nice to have more precise
todo_type!()messages for theTypingSelfandTypeAliasvariants:todo_type!("Support for `typing.Self`")andtodo_type!("Support for `typing.TypeAlias`"), respectivelyThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah ill get started on that, thanks
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Am i right in saying that these should be the same message as FinalInTypeExpression and ClassVarInTypeExpression?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Amazing, thank you! You're doing great work :D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not quite.
ClassVar,Final,Required,NotRequiredandReadOnlyare all type qualifiers, so they're all valid in annotation expressions but invalid in type expressions. Nonetheless, I think it would be nice to have different error messages because of the fact that they differ in the number of arguments they accept when they appear in annotation expressions:Finalcan be used both with and without type arguments in an annotation expressionClassVarit's... sort-of unclear whether it's valid without type arguments or not (see discussion at https://discuss.python.org/t/bare-classvar-annotation/81705)So that implies that for the error messages,
FinalandClassVarshould have this error message:But for the other three, they should have this error message: