Skip to content
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

[red-knot] Allow type[] to be subscripted #13667

Merged
merged 1 commit into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions crates/red_knot_python_semantic/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,7 @@ pub enum KnownClass {
Set,
Dict,
// Types
GenericAlias,
ModuleType,
FunctionType,
// Typeshed
Expand All @@ -857,6 +858,7 @@ impl<'db> KnownClass {
Self::Dict => "dict",
Self::List => "list",
Self::Type => "type",
Self::GenericAlias => "GenericAlias",
Self::ModuleType => "ModuleType",
Self::FunctionType => "FunctionType",
Self::NoneType => "NoneType",
Expand All @@ -880,7 +882,9 @@ impl<'db> KnownClass {
| Self::Tuple
| Self::Set
| Self::Dict => builtins_symbol_ty(db, self.as_str()),
Self::ModuleType | Self::FunctionType => types_symbol_ty(db, self.as_str()),
Self::GenericAlias | Self::ModuleType | Self::FunctionType => {
types_symbol_ty(db, self.as_str())
}
Self::NoneType => typeshed_symbol_ty(db, self.as_str()),
}
}
Expand Down Expand Up @@ -910,6 +914,7 @@ impl<'db> KnownClass {
"set" => Some(Self::Set),
"dict" => Some(Self::Dict),
"list" => Some(Self::List),
"GenericAlias" => Some(Self::GenericAlias),
"NoneType" => Some(Self::NoneType),
"ModuleType" => Some(Self::ModuleType),
"FunctionType" => Some(Self::FunctionType),
Expand All @@ -934,7 +939,7 @@ impl<'db> KnownClass {
| Self::Tuple
| Self::Set
| Self::Dict => module.name() == "builtins",
Self::ModuleType | Self::FunctionType => module.name() == "types",
Self::GenericAlias | Self::ModuleType | Self::FunctionType => module.name() == "types",
Self::NoneType => matches!(module.name().as_str(), "_typeshed" | "types"),
}
}
Expand Down
19 changes: 12 additions & 7 deletions crates/red_knot_python_semantic/src/types/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2828,6 +2828,13 @@ impl<'db> TypeInferenceBuilder<'db> {

// Otherwise, if the value is itself a class and defines `__class_getitem__`,
// return its return type.
//
// TODO: lots of classes are only subscriptable at runtime on Python 3.9+,
// *but* we should also allow them to be subscripted in stubs
// (and in annotations if `from __future__ import annotations` is enabled),
// even if the target version is Python 3.8 or lower,
// despite the fact that there will be no corresponding `__class_getitem__`
// method in these `sys.version_info` branches.
if value_ty.is_class(self.db) {
let dunder_class_getitem_method = value_ty.member(self.db, "__class_getitem__");
if !dunder_class_getitem_method.is_unbound() {
Expand All @@ -2848,6 +2855,11 @@ impl<'db> TypeInferenceBuilder<'db> {
});
}

if matches!(value_ty, Type::Class(class) if class.is_known(self.db, KnownClass::Type))
{
return KnownClass::GenericAlias.to_instance(self.db);
}

self.non_subscriptable_diagnostic(
(&**value).into(),
value_ty,
Expand Down Expand Up @@ -6194,13 +6206,6 @@ mod tests {
)?;

let expected_diagnostics = &[
// TODO: these `__class_getitem__` diagnostics are all false positives:
// (`builtins.type` is unique at runtime
// as it can be subscripted even though it has no `__class_getitem__` method)
"Cannot subscript object of type `Literal[type]` with no `__class_getitem__` method",
"Cannot subscript object of type `Literal[type]` with no `__class_getitem__` method",
"Cannot subscript object of type `Literal[type]` with no `__class_getitem__` method",
"Cannot subscript object of type `Literal[type]` with no `__class_getitem__` method",
// Should be `AttributeError`:
"Revealed type is `@Todo`",
// Should be `OSError | RuntimeError`:
Expand Down
Loading