mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-03 10:22:24 +00:00
[red-knot] Understand typing.Type
(#14904)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
This commit is contained in:
parent
c8d505c8ea
commit
f30227c436
4 changed files with 49 additions and 3 deletions
|
@ -0,0 +1,32 @@
|
|||
# `typing.Type`
|
||||
|
||||
## Annotation
|
||||
|
||||
`typing.Type` can be used interchangeably with `type`:
|
||||
|
||||
```py
|
||||
from typing import Type
|
||||
|
||||
class A: ...
|
||||
|
||||
def _(c: Type, d: Type[A], e: Type[A]):
|
||||
reveal_type(c) # revealed: type
|
||||
reveal_type(d) # revealed: type[A]
|
||||
c = d # fine
|
||||
d = c # fine
|
||||
```
|
||||
|
||||
## Inheritance
|
||||
|
||||
Inheriting from `Type` results in a MRO with `builtins.type` and `typing.Generic`. `Type` itself is
|
||||
not a class.
|
||||
|
||||
```py
|
||||
from typing import Type
|
||||
|
||||
class C(Type): ...
|
||||
|
||||
# Runtime value: `(C, type, typing.Generic, object)`
|
||||
# TODO: Add `Generic` to the MRO
|
||||
reveal_type(C.__mro__) # revealed: tuple[Literal[C], Literal[type], Literal[object]]
|
||||
```
|
|
@ -1757,6 +1757,8 @@ impl<'db> Type<'db> {
|
|||
// In a type expression, a bare `type` is interpreted as "instance of `type`", which is
|
||||
// equivalent to `type[object]`.
|
||||
Type::ClassLiteral(_) | Type::SubclassOf(_) => self.to_instance(db),
|
||||
// We treat `typing.Type` exactly the same as `builtins.type`:
|
||||
Type::KnownInstance(KnownInstanceType::Type) => KnownClass::Type.to_instance(db),
|
||||
Type::Union(union) => union.map(db, |element| element.in_type_expression(db)),
|
||||
Type::Unknown => Type::Unknown,
|
||||
// TODO map this to a new `Type::TypeVar` variant
|
||||
|
@ -2136,6 +2138,8 @@ pub enum KnownInstanceType<'db> {
|
|||
Never,
|
||||
/// The symbol `typing.Any` (which can also be found as `typing_extensions.Any`)
|
||||
Any,
|
||||
/// The symbol `typing.Type` (which can also be found as `typing_extensions.Type`)
|
||||
Type,
|
||||
/// A single instance of `typing.TypeVar`
|
||||
TypeVar(TypeVarInstance<'db>),
|
||||
/// A single instance of `typing.TypeAliasType` (PEP 695 type alias)
|
||||
|
@ -2154,6 +2158,7 @@ impl<'db> KnownInstanceType<'db> {
|
|||
Self::NoReturn => "NoReturn",
|
||||
Self::Never => "Never",
|
||||
Self::Any => "Any",
|
||||
Self::Type => "Type",
|
||||
Self::TypeAliasType(_) => "TypeAliasType",
|
||||
}
|
||||
}
|
||||
|
@ -2169,6 +2174,7 @@ impl<'db> KnownInstanceType<'db> {
|
|||
| Self::NoReturn
|
||||
| Self::Never
|
||||
| Self::Any
|
||||
| Self::Type
|
||||
| Self::TypeAliasType(_) => Truthiness::AlwaysTrue,
|
||||
}
|
||||
}
|
||||
|
@ -2183,6 +2189,7 @@ impl<'db> KnownInstanceType<'db> {
|
|||
Self::NoReturn => "typing.NoReturn",
|
||||
Self::Never => "typing.Never",
|
||||
Self::Any => "typing.Any",
|
||||
Self::Type => "typing.Type",
|
||||
Self::TypeVar(typevar) => typevar.name(db),
|
||||
Self::TypeAliasType(_) => "typing.TypeAliasType",
|
||||
}
|
||||
|
@ -2198,6 +2205,7 @@ impl<'db> KnownInstanceType<'db> {
|
|||
Self::NoReturn => KnownClass::SpecialForm,
|
||||
Self::Never => KnownClass::SpecialForm,
|
||||
Self::Any => KnownClass::Object,
|
||||
Self::Type => KnownClass::Object,
|
||||
Self::TypeVar(_) => KnownClass::TypeVar,
|
||||
Self::TypeAliasType(_) => KnownClass::TypeAliasType,
|
||||
}
|
||||
|
@ -2224,6 +2232,7 @@ impl<'db> KnownInstanceType<'db> {
|
|||
("typing" | "typing_extensions", "Union") => Some(Self::Union),
|
||||
("typing" | "typing_extensions", "NoReturn") => Some(Self::NoReturn),
|
||||
("typing" | "typing_extensions", "Never") => Some(Self::Never),
|
||||
("typing" | "typing_extensions", "Type") => Some(Self::Type),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4861,6 +4861,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
);
|
||||
Type::Unknown
|
||||
}
|
||||
KnownInstanceType::Type => self.infer_subclass_of_type_expression(parameters),
|
||||
KnownInstanceType::Any => Type::Any,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ impl<'db> Mro<'db> {
|
|||
// but it's a common case (i.e., worth optimizing for),
|
||||
// and the `c3_merge` function requires lots of allocations.
|
||||
[single_base] => {
|
||||
let single_base = ClassBase::try_from_ty(*single_base).ok_or(*single_base);
|
||||
let single_base = ClassBase::try_from_ty(*single_base, db).ok_or(*single_base);
|
||||
single_base.map_or_else(
|
||||
|invalid_base_ty| {
|
||||
let bases_info = Box::from([(0, invalid_base_ty)]);
|
||||
|
@ -102,7 +102,7 @@ impl<'db> Mro<'db> {
|
|||
let mut invalid_bases = vec![];
|
||||
|
||||
for (i, base) in multiple_bases.iter().enumerate() {
|
||||
match ClassBase::try_from_ty(*base).ok_or(*base) {
|
||||
match ClassBase::try_from_ty(*base, db).ok_or(*base) {
|
||||
Ok(valid_base) => valid_bases.push(valid_base),
|
||||
Err(invalid_base) => invalid_bases.push((i, invalid_base)),
|
||||
}
|
||||
|
@ -341,7 +341,7 @@ impl<'db> ClassBase<'db> {
|
|||
/// Attempt to resolve `ty` into a `ClassBase`.
|
||||
///
|
||||
/// Return `None` if `ty` is not an acceptable type for a class base.
|
||||
fn try_from_ty(ty: Type<'db>) -> Option<Self> {
|
||||
fn try_from_ty(ty: Type<'db>, db: &'db dyn Db) -> Option<Self> {
|
||||
match ty {
|
||||
Type::Any => Some(Self::Any),
|
||||
Type::Unknown => Some(Self::Unknown),
|
||||
|
@ -371,6 +371,10 @@ impl<'db> ClassBase<'db> {
|
|||
| KnownInstanceType::Never
|
||||
| KnownInstanceType::Optional => None,
|
||||
KnownInstanceType::Any => Some(Self::Any),
|
||||
// TODO: classes inheriting from `typing.Type` also have `Generic` in their MRO
|
||||
KnownInstanceType::Type => {
|
||||
ClassBase::try_from_ty(KnownClass::Type.to_class_literal(db), db)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue