mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-01 06:11:43 +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
|
// In a type expression, a bare `type` is interpreted as "instance of `type`", which is
|
||||||
// equivalent to `type[object]`.
|
// equivalent to `type[object]`.
|
||||||
Type::ClassLiteral(_) | Type::SubclassOf(_) => self.to_instance(db),
|
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::Union(union) => union.map(db, |element| element.in_type_expression(db)),
|
||||||
Type::Unknown => Type::Unknown,
|
Type::Unknown => Type::Unknown,
|
||||||
// TODO map this to a new `Type::TypeVar` variant
|
// TODO map this to a new `Type::TypeVar` variant
|
||||||
|
@ -2136,6 +2138,8 @@ pub enum KnownInstanceType<'db> {
|
||||||
Never,
|
Never,
|
||||||
/// The symbol `typing.Any` (which can also be found as `typing_extensions.Any`)
|
/// The symbol `typing.Any` (which can also be found as `typing_extensions.Any`)
|
||||||
Any,
|
Any,
|
||||||
|
/// The symbol `typing.Type` (which can also be found as `typing_extensions.Type`)
|
||||||
|
Type,
|
||||||
/// A single instance of `typing.TypeVar`
|
/// A single instance of `typing.TypeVar`
|
||||||
TypeVar(TypeVarInstance<'db>),
|
TypeVar(TypeVarInstance<'db>),
|
||||||
/// A single instance of `typing.TypeAliasType` (PEP 695 type alias)
|
/// A single instance of `typing.TypeAliasType` (PEP 695 type alias)
|
||||||
|
@ -2154,6 +2158,7 @@ impl<'db> KnownInstanceType<'db> {
|
||||||
Self::NoReturn => "NoReturn",
|
Self::NoReturn => "NoReturn",
|
||||||
Self::Never => "Never",
|
Self::Never => "Never",
|
||||||
Self::Any => "Any",
|
Self::Any => "Any",
|
||||||
|
Self::Type => "Type",
|
||||||
Self::TypeAliasType(_) => "TypeAliasType",
|
Self::TypeAliasType(_) => "TypeAliasType",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2169,6 +2174,7 @@ impl<'db> KnownInstanceType<'db> {
|
||||||
| Self::NoReturn
|
| Self::NoReturn
|
||||||
| Self::Never
|
| Self::Never
|
||||||
| Self::Any
|
| Self::Any
|
||||||
|
| Self::Type
|
||||||
| Self::TypeAliasType(_) => Truthiness::AlwaysTrue,
|
| Self::TypeAliasType(_) => Truthiness::AlwaysTrue,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2183,6 +2189,7 @@ impl<'db> KnownInstanceType<'db> {
|
||||||
Self::NoReturn => "typing.NoReturn",
|
Self::NoReturn => "typing.NoReturn",
|
||||||
Self::Never => "typing.Never",
|
Self::Never => "typing.Never",
|
||||||
Self::Any => "typing.Any",
|
Self::Any => "typing.Any",
|
||||||
|
Self::Type => "typing.Type",
|
||||||
Self::TypeVar(typevar) => typevar.name(db),
|
Self::TypeVar(typevar) => typevar.name(db),
|
||||||
Self::TypeAliasType(_) => "typing.TypeAliasType",
|
Self::TypeAliasType(_) => "typing.TypeAliasType",
|
||||||
}
|
}
|
||||||
|
@ -2198,6 +2205,7 @@ impl<'db> KnownInstanceType<'db> {
|
||||||
Self::NoReturn => KnownClass::SpecialForm,
|
Self::NoReturn => KnownClass::SpecialForm,
|
||||||
Self::Never => KnownClass::SpecialForm,
|
Self::Never => KnownClass::SpecialForm,
|
||||||
Self::Any => KnownClass::Object,
|
Self::Any => KnownClass::Object,
|
||||||
|
Self::Type => KnownClass::Object,
|
||||||
Self::TypeVar(_) => KnownClass::TypeVar,
|
Self::TypeVar(_) => KnownClass::TypeVar,
|
||||||
Self::TypeAliasType(_) => KnownClass::TypeAliasType,
|
Self::TypeAliasType(_) => KnownClass::TypeAliasType,
|
||||||
}
|
}
|
||||||
|
@ -2224,6 +2232,7 @@ impl<'db> KnownInstanceType<'db> {
|
||||||
("typing" | "typing_extensions", "Union") => Some(Self::Union),
|
("typing" | "typing_extensions", "Union") => Some(Self::Union),
|
||||||
("typing" | "typing_extensions", "NoReturn") => Some(Self::NoReturn),
|
("typing" | "typing_extensions", "NoReturn") => Some(Self::NoReturn),
|
||||||
("typing" | "typing_extensions", "Never") => Some(Self::Never),
|
("typing" | "typing_extensions", "Never") => Some(Self::Never),
|
||||||
|
("typing" | "typing_extensions", "Type") => Some(Self::Type),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4861,6 +4861,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
);
|
);
|
||||||
Type::Unknown
|
Type::Unknown
|
||||||
}
|
}
|
||||||
|
KnownInstanceType::Type => self.infer_subclass_of_type_expression(parameters),
|
||||||
KnownInstanceType::Any => Type::Any,
|
KnownInstanceType::Any => Type::Any,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ impl<'db> Mro<'db> {
|
||||||
// but it's a common case (i.e., worth optimizing for),
|
// but it's a common case (i.e., worth optimizing for),
|
||||||
// and the `c3_merge` function requires lots of allocations.
|
// and the `c3_merge` function requires lots of allocations.
|
||||||
[single_base] => {
|
[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(
|
single_base.map_or_else(
|
||||||
|invalid_base_ty| {
|
|invalid_base_ty| {
|
||||||
let bases_info = Box::from([(0, 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![];
|
let mut invalid_bases = vec![];
|
||||||
|
|
||||||
for (i, base) in multiple_bases.iter().enumerate() {
|
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),
|
Ok(valid_base) => valid_bases.push(valid_base),
|
||||||
Err(invalid_base) => invalid_bases.push((i, invalid_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`.
|
/// Attempt to resolve `ty` into a `ClassBase`.
|
||||||
///
|
///
|
||||||
/// Return `None` if `ty` is not an acceptable type for a class base.
|
/// 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 {
|
match ty {
|
||||||
Type::Any => Some(Self::Any),
|
Type::Any => Some(Self::Any),
|
||||||
Type::Unknown => Some(Self::Unknown),
|
Type::Unknown => Some(Self::Unknown),
|
||||||
|
@ -371,6 +371,10 @@ impl<'db> ClassBase<'db> {
|
||||||
| KnownInstanceType::Never
|
| KnownInstanceType::Never
|
||||||
| KnownInstanceType::Optional => None,
|
| KnownInstanceType::Optional => None,
|
||||||
KnownInstanceType::Any => Some(Self::Any),
|
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