Handle explicit specialization before outputting lints

This commit is contained in:
Douglas Creager
2025-03-24 14:06:20 -04:00
parent 2a47422dd5
commit 8dfb59a2b1
4 changed files with 21 additions and 29 deletions

View File

@@ -13,8 +13,6 @@ class C[T]: ...
A class that inherits from a generic class, and fills its type parameters with typevars, is generic:
```py
# TODO: no error
# error: [non-subscriptable]
class D[U](C[U]): ...
```
@@ -22,8 +20,6 @@ A class that inherits from a generic class, but fills its type parameters with c
_not_ generic:
```py
# TODO: no error
# error: [non-subscriptable]
class E(C[int]): ...
```
@@ -65,9 +61,7 @@ The type parameter can be specified explicitly:
class C[T]:
x: T
# TODO: no error
# TODO: revealed: C[int]
# error: [non-subscriptable]
reveal_type(C[int]()) # revealed: C
```
@@ -131,16 +125,11 @@ propagate through:
class Base[T]:
x: T | None = None
# TODO: no error
# error: [non-subscriptable]
class Sub[U](Base[U]): ...
# TODO: no error
# TODO: revealed: int | None
# error: [non-subscriptable]
reveal_type(Base[int].x) # revealed: T | None
# TODO: revealed: int | None
# error: [non-subscriptable]
reveal_type(Sub[int].x) # revealed: T | None
```
@@ -155,8 +144,6 @@ Here, `Sub` is not a generic class, since it fills its superclass's type paramet
```pyi
class Base[T]: ...
# TODO: no error
# error: [non-subscriptable]
class Sub(Base[Sub]): ...
reveal_type(Sub) # revealed: Literal[Sub]
@@ -169,8 +156,6 @@ A similar case can work in a non-stub file, if forward references are stringifie
```py
class Base[T]: ...
# TODO: no error
# error: [non-subscriptable]
class Sub(Base["Sub"]): ...
reveal_type(Sub) # revealed: Literal[Sub]
@@ -183,8 +168,6 @@ In a non-stub file, without stringified forward references, this raises a `NameE
```py
class Base[T]: ...
# TODO: the unresolved-reference error is correct, the non-subscriptable is not
# error: [non-subscriptable]
# error: [unresolved-reference]
class Sub(Base[Sub]): ...
```

View File

@@ -8,8 +8,6 @@ In type stubs, classes can reference themselves in their base class definitions.
```pyi
class Foo[T]: ...
# TODO: actually is subscriptable
# error: [non-subscriptable]
class Bar(Foo[Bar]): ...
reveal_type(Bar) # revealed: Literal[Bar]

View File

@@ -39,7 +39,7 @@ pub struct Class<'db> {
#[return_ref]
pub(crate) name: ast::name::Name,
generic_context: Option<GenericContext<'db>>,
pub(crate) generic_context: Option<GenericContext<'db>>,
body_scope: ScopeId<'db>,
pub(crate) known: Option<KnownClass>,

View File

@@ -64,6 +64,7 @@ use crate::symbol::{
typing_extensions_symbol, Boundness, LookupError,
};
use crate::types::call::{Argument, Bindings, CallArgumentTypes, CallArguments, CallError};
use crate::types::class::{ClassLiteralType, MetaclassErrorKind};
use crate::types::diagnostic::{
report_implicit_return_type, report_invalid_arguments_to_annotated,
report_invalid_arguments_to_callable, report_invalid_assignment,
@@ -80,12 +81,11 @@ use crate::types::generics::GenericContext;
use crate::types::mro::MroErrorKind;
use crate::types::unpacker::{UnpackResult, Unpacker};
use crate::types::{
class::MetaclassErrorKind, todo_type, Class, DynamicType, FunctionType, InstanceType,
IntersectionBuilder, IntersectionType, KnownClass, KnownFunction, KnownInstanceType,
MetaclassCandidate, Parameter, ParameterForm, Parameters, SliceLiteralType, SubclassOfType,
Symbol, SymbolAndQualifiers, Truthiness, TupleType, Type, TypeAliasType, TypeAndQualifiers,
TypeArrayDisplay, TypeQualifiers, TypeVarBoundOrConstraints, TypeVarInstance, UnionBuilder,
UnionType,
todo_type, Class, DynamicType, FunctionType, InstanceType, IntersectionBuilder,
IntersectionType, KnownClass, KnownFunction, KnownInstanceType, MetaclassCandidate, Parameter,
ParameterForm, Parameters, SliceLiteralType, SubclassOfType, Symbol, SymbolAndQualifiers,
Truthiness, TupleType, Type, TypeAliasType, TypeAndQualifiers, TypeArrayDisplay,
TypeQualifiers, TypeVarBoundOrConstraints, TypeVarInstance, UnionBuilder, UnionType,
};
use crate::types::{CallableType, GeneralCallableType, Signature};
use crate::unpack::Unpack;
@@ -5795,9 +5795,16 @@ impl<'db> TypeInferenceBuilder<'db> {
}
}
if matches!(value_ty, Type::ClassLiteral(class_literal) if class_literal.class().is_known(self.db(), KnownClass::Type))
{
return KnownClass::GenericAlias.to_instance(self.db());
if let Type::ClassLiteral(ClassLiteralType { class }) = value_ty {
if class.is_known(self.db(), KnownClass::Type) {
return KnownClass::GenericAlias.to_instance(self.db());
}
if class.generic_context(self.db()).is_some() {
// TODO: specialize the generic class using these explicit type
// variable assignments
return value_ty;
}
}
report_non_subscriptable(
@@ -5820,6 +5827,10 @@ impl<'db> TypeInferenceBuilder<'db> {
// TODO: proper support for generic classes
// For now, just infer `Sequence`, if we see something like `Sequence[str]`. This allows us
// to look up attributes on generic base classes, even if we don't understand generics yet.
// Note that this isn't handled by the clause up above for generic classes
// that use legacy type variables and an explicit `Generic` base class.
// Once we handle legacy typevars, this special case will be removed in
// favor of the specialization logic above.
value_ty
}
_ => Type::unknown(),