mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-03 07:04:53 +00:00
[red-knot] Move InstanceType
to its own submodule (#17525)
This commit is contained in:
parent
d2b20f7367
commit
ae6fde152c
8 changed files with 577 additions and 528 deletions
|
@ -47,9 +47,10 @@ pub(crate) use crate::types::narrow::infer_narrowing_constraint;
|
|||
use crate::types::signatures::{Parameter, ParameterForm, Parameters};
|
||||
use crate::{Db, FxOrderSet, Module, Program};
|
||||
pub(crate) use class::{
|
||||
Class, ClassLiteralType, ClassType, GenericAlias, GenericClass, InstanceType, KnownClass,
|
||||
KnownInstanceType, NonGenericClass,
|
||||
Class, ClassLiteralType, ClassType, GenericAlias, GenericClass, KnownClass, NonGenericClass,
|
||||
};
|
||||
pub(crate) use instance::InstanceType;
|
||||
pub(crate) use known_instance::KnownInstanceType;
|
||||
|
||||
mod builder;
|
||||
mod call;
|
||||
|
@ -60,6 +61,8 @@ mod diagnostic;
|
|||
mod display;
|
||||
mod generics;
|
||||
mod infer;
|
||||
mod instance;
|
||||
mod known_instance;
|
||||
mod mro;
|
||||
mod narrow;
|
||||
mod signatures;
|
||||
|
@ -462,7 +465,8 @@ pub enum Type<'db> {
|
|||
GenericAlias(GenericAlias<'db>),
|
||||
/// The set of all class objects that are subclasses of the given class (C), spelled `type[C]`.
|
||||
SubclassOf(SubclassOfType<'db>),
|
||||
/// The set of Python objects with the given class in their __class__'s method resolution order
|
||||
/// The set of Python objects with the given class in their __class__'s method resolution order.
|
||||
/// Construct this variant using the `Type::instance` constructor function.
|
||||
Instance(InstanceType<'db>),
|
||||
/// A single Python object that requires special treatment in the type system
|
||||
KnownInstance(KnownInstanceType<'db>),
|
||||
|
@ -527,17 +531,20 @@ impl<'db> Type<'db> {
|
|||
|
||||
fn is_none(&self, db: &'db dyn Db) -> bool {
|
||||
self.into_instance()
|
||||
.is_some_and(|instance| instance.class.is_known(db, KnownClass::NoneType))
|
||||
.is_some_and(|instance| instance.class().is_known(db, KnownClass::NoneType))
|
||||
}
|
||||
|
||||
pub fn is_notimplemented(&self, db: &'db dyn Db) -> bool {
|
||||
self.into_instance()
|
||||
.is_some_and(|instance| instance.class.is_known(db, KnownClass::NotImplementedType))
|
||||
self.into_instance().is_some_and(|instance| {
|
||||
instance
|
||||
.class()
|
||||
.is_known(db, KnownClass::NotImplementedType)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_object(&self, db: &'db dyn Db) -> bool {
|
||||
self.into_instance()
|
||||
.is_some_and(|instance| instance.class.is_object(db))
|
||||
.is_some_and(|instance| instance.class().is_object(db))
|
||||
}
|
||||
|
||||
pub const fn is_todo(&self) -> bool {
|
||||
|
@ -689,10 +696,6 @@ impl<'db> Type<'db> {
|
|||
)
|
||||
}
|
||||
|
||||
pub const fn is_instance(&self) -> bool {
|
||||
matches!(self, Type::Instance(..))
|
||||
}
|
||||
|
||||
pub const fn is_property_instance(&self) -> bool {
|
||||
matches!(self, Type::PropertyInstance(..))
|
||||
}
|
||||
|
@ -793,13 +796,6 @@ impl<'db> Type<'db> {
|
|||
.expect("Expected a Type::IntLiteral variant")
|
||||
}
|
||||
|
||||
pub const fn into_instance(self) -> Option<InstanceType<'db>> {
|
||||
match self {
|
||||
Type::Instance(instance_type) => Some(instance_type),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn into_known_instance(self) -> Option<KnownInstanceType<'db>> {
|
||||
match self {
|
||||
Type::KnownInstance(known_instance) => Some(known_instance),
|
||||
|
@ -828,10 +824,6 @@ impl<'db> Type<'db> {
|
|||
matches!(self, Type::LiteralString)
|
||||
}
|
||||
|
||||
pub const fn instance(class: ClassType<'db>) -> Self {
|
||||
Self::Instance(InstanceType { class })
|
||||
}
|
||||
|
||||
pub fn string_literal(db: &'db dyn Db, string: &str) -> Self {
|
||||
Self::StringLiteral(StringLiteralType::new(db, string))
|
||||
}
|
||||
|
@ -963,7 +955,7 @@ impl<'db> Type<'db> {
|
|||
(_, Type::Never) => false,
|
||||
|
||||
// Everything is a subtype of `object`.
|
||||
(_, Type::Instance(InstanceType { class })) if class.is_object(db) => true,
|
||||
(_, Type::Instance(instance)) if instance.class().is_object(db) => true,
|
||||
|
||||
// A fully static typevar is always a subtype of itself, and is never a subtype of any
|
||||
// other typevar, since there is no guarantee that they will be specialized to the same
|
||||
|
@ -1285,7 +1277,7 @@ impl<'db> Type<'db> {
|
|||
|
||||
// All types are assignable to `object`.
|
||||
// TODO this special case might be removable once the below cases are comprehensive
|
||||
(_, Type::Instance(InstanceType { class })) if class.is_object(db) => true,
|
||||
(_, Type::Instance(instance)) if instance.class().is_object(db) => true,
|
||||
|
||||
// A typevar is always assignable to itself, and is never assignable to any other
|
||||
// typevar, since there is no guarantee that they will be specialized to the same
|
||||
|
@ -1440,13 +1432,13 @@ impl<'db> Type<'db> {
|
|||
|
||||
// TODO: This is a workaround to avoid false positives (e.g. when checking function calls
|
||||
// with `SupportsIndex` parameters), which should be removed when we understand protocols.
|
||||
(lhs, Type::Instance(InstanceType { class }))
|
||||
if class.is_known(db, KnownClass::SupportsIndex) =>
|
||||
(lhs, Type::Instance(instance))
|
||||
if instance.class().is_known(db, KnownClass::SupportsIndex) =>
|
||||
{
|
||||
match lhs {
|
||||
Type::Instance(InstanceType { class })
|
||||
Type::Instance(instance)
|
||||
if matches!(
|
||||
class.known(db),
|
||||
instance.class().known(db),
|
||||
Some(KnownClass::Int | KnownClass::SupportsIndex)
|
||||
) =>
|
||||
{
|
||||
|
@ -1458,9 +1450,7 @@ impl<'db> Type<'db> {
|
|||
}
|
||||
|
||||
// TODO: ditto for avoiding false positives when checking function calls with `Sized` parameters.
|
||||
(lhs, Type::Instance(InstanceType { class }))
|
||||
if class.is_known(db, KnownClass::Sized) =>
|
||||
{
|
||||
(lhs, Type::Instance(instance)) if instance.class().is_known(db, KnownClass::Sized) => {
|
||||
matches!(
|
||||
lhs.to_meta_type(db).member(db, "__len__"),
|
||||
SymbolAndQualifiers {
|
||||
|
@ -1771,9 +1761,9 @@ impl<'db> Type<'db> {
|
|||
.is_disjoint_from(db, other),
|
||||
},
|
||||
|
||||
(Type::KnownInstance(known_instance), Type::Instance(InstanceType { class }))
|
||||
| (Type::Instance(InstanceType { class }), Type::KnownInstance(known_instance)) => {
|
||||
!known_instance.is_instance_of(db, class)
|
||||
(Type::KnownInstance(known_instance), Type::Instance(instance))
|
||||
| (Type::Instance(instance), Type::KnownInstance(known_instance)) => {
|
||||
!known_instance.is_instance_of(db, instance.class())
|
||||
}
|
||||
|
||||
(known_instance_ty @ Type::KnownInstance(_), Type::Tuple(_))
|
||||
|
@ -1781,20 +1771,20 @@ impl<'db> Type<'db> {
|
|||
known_instance_ty.is_disjoint_from(db, KnownClass::Tuple.to_instance(db))
|
||||
}
|
||||
|
||||
(Type::BooleanLiteral(..), Type::Instance(InstanceType { class }))
|
||||
| (Type::Instance(InstanceType { class }), Type::BooleanLiteral(..)) => {
|
||||
(Type::BooleanLiteral(..), Type::Instance(instance))
|
||||
| (Type::Instance(instance), Type::BooleanLiteral(..)) => {
|
||||
// A `Type::BooleanLiteral()` must be an instance of exactly `bool`
|
||||
// (it cannot be an instance of a `bool` subclass)
|
||||
!KnownClass::Bool.is_subclass_of(db, class)
|
||||
!KnownClass::Bool.is_subclass_of(db, instance.class())
|
||||
}
|
||||
|
||||
(Type::BooleanLiteral(..), _) | (_, Type::BooleanLiteral(..)) => true,
|
||||
|
||||
(Type::IntLiteral(..), Type::Instance(InstanceType { class }))
|
||||
| (Type::Instance(InstanceType { class }), Type::IntLiteral(..)) => {
|
||||
(Type::IntLiteral(..), Type::Instance(instance))
|
||||
| (Type::Instance(instance), Type::IntLiteral(..)) => {
|
||||
// A `Type::IntLiteral()` must be an instance of exactly `int`
|
||||
// (it cannot be an instance of an `int` subclass)
|
||||
!KnownClass::Int.is_subclass_of(db, class)
|
||||
!KnownClass::Int.is_subclass_of(db, instance.class())
|
||||
}
|
||||
|
||||
(Type::IntLiteral(..), _) | (_, Type::IntLiteral(..)) => true,
|
||||
|
@ -1802,34 +1792,28 @@ impl<'db> Type<'db> {
|
|||
(Type::StringLiteral(..), Type::LiteralString)
|
||||
| (Type::LiteralString, Type::StringLiteral(..)) => false,
|
||||
|
||||
(
|
||||
Type::StringLiteral(..) | Type::LiteralString,
|
||||
Type::Instance(InstanceType { class }),
|
||||
)
|
||||
| (
|
||||
Type::Instance(InstanceType { class }),
|
||||
Type::StringLiteral(..) | Type::LiteralString,
|
||||
) => {
|
||||
(Type::StringLiteral(..) | Type::LiteralString, Type::Instance(instance))
|
||||
| (Type::Instance(instance), Type::StringLiteral(..) | Type::LiteralString) => {
|
||||
// A `Type::StringLiteral()` or a `Type::LiteralString` must be an instance of exactly `str`
|
||||
// (it cannot be an instance of a `str` subclass)
|
||||
!KnownClass::Str.is_subclass_of(db, class)
|
||||
!KnownClass::Str.is_subclass_of(db, instance.class())
|
||||
}
|
||||
|
||||
(Type::LiteralString, Type::LiteralString) => false,
|
||||
(Type::LiteralString, _) | (_, Type::LiteralString) => true,
|
||||
|
||||
(Type::BytesLiteral(..), Type::Instance(InstanceType { class }))
|
||||
| (Type::Instance(InstanceType { class }), Type::BytesLiteral(..)) => {
|
||||
(Type::BytesLiteral(..), Type::Instance(instance))
|
||||
| (Type::Instance(instance), Type::BytesLiteral(..)) => {
|
||||
// A `Type::BytesLiteral()` must be an instance of exactly `bytes`
|
||||
// (it cannot be an instance of a `bytes` subclass)
|
||||
!KnownClass::Bytes.is_subclass_of(db, class)
|
||||
!KnownClass::Bytes.is_subclass_of(db, instance.class())
|
||||
}
|
||||
|
||||
(Type::SliceLiteral(..), Type::Instance(InstanceType { class }))
|
||||
| (Type::Instance(InstanceType { class }), Type::SliceLiteral(..)) => {
|
||||
(Type::SliceLiteral(..), Type::Instance(instance))
|
||||
| (Type::Instance(instance), Type::SliceLiteral(..)) => {
|
||||
// A `Type::SliceLiteral` must be an instance of exactly `slice`
|
||||
// (it cannot be an instance of a `slice` subclass)
|
||||
!KnownClass::Slice.is_subclass_of(db, class)
|
||||
!KnownClass::Slice.is_subclass_of(db, instance.class())
|
||||
}
|
||||
|
||||
// A class-literal type `X` is always disjoint from an instance type `Y`,
|
||||
|
@ -1844,11 +1828,11 @@ impl<'db> Type<'db> {
|
|||
.metaclass_instance_type(db)
|
||||
.is_subtype_of(db, instance),
|
||||
|
||||
(Type::FunctionLiteral(..), Type::Instance(InstanceType { class }))
|
||||
| (Type::Instance(InstanceType { class }), Type::FunctionLiteral(..)) => {
|
||||
(Type::FunctionLiteral(..), Type::Instance(instance))
|
||||
| (Type::Instance(instance), Type::FunctionLiteral(..)) => {
|
||||
// A `Type::FunctionLiteral()` must be an instance of exactly `types.FunctionType`
|
||||
// (it cannot be an instance of a `types.FunctionType` subclass)
|
||||
!KnownClass::FunctionType.is_subclass_of(db, class)
|
||||
!KnownClass::FunctionType.is_subclass_of(db, instance.class())
|
||||
}
|
||||
|
||||
(Type::BoundMethod(_), other) | (other, Type::BoundMethod(_)) => KnownClass::MethodType
|
||||
|
@ -1907,13 +1891,7 @@ impl<'db> Type<'db> {
|
|||
other.is_disjoint_from(db, KnownClass::ModuleType.to_instance(db))
|
||||
}
|
||||
|
||||
(
|
||||
Type::Instance(InstanceType { class: left_class }),
|
||||
Type::Instance(InstanceType { class: right_class }),
|
||||
) => {
|
||||
(left_class.is_final(db) && !left_class.is_subclass_of(db, right_class))
|
||||
|| (right_class.is_final(db) && !right_class.is_subclass_of(db, left_class))
|
||||
}
|
||||
(Type::Instance(left), Type::Instance(right)) => left.is_disjoint_from(db, right),
|
||||
|
||||
(Type::Tuple(tuple), Type::Tuple(other_tuple)) => {
|
||||
let self_elements = tuple.elements(db);
|
||||
|
@ -2092,9 +2070,7 @@ impl<'db> Type<'db> {
|
|||
false
|
||||
}
|
||||
Type::DataclassDecorator(_) | Type::DataclassTransformer(_) => false,
|
||||
Type::Instance(InstanceType { class }) => {
|
||||
class.known(db).is_some_and(KnownClass::is_singleton)
|
||||
}
|
||||
Type::Instance(instance) => instance.is_singleton(db),
|
||||
Type::PropertyInstance(_) => false,
|
||||
Type::Tuple(..) => {
|
||||
// The empty tuple is a singleton on CPython and PyPy, but not on other Python
|
||||
|
@ -2166,9 +2142,7 @@ impl<'db> Type<'db> {
|
|||
.iter()
|
||||
.all(|elem| elem.is_single_valued(db)),
|
||||
|
||||
Type::Instance(InstanceType { class }) => {
|
||||
class.known(db).is_some_and(KnownClass::is_single_valued)
|
||||
}
|
||||
Type::Instance(instance) => instance.is_single_valued(db),
|
||||
|
||||
Type::BoundSuper(_) => {
|
||||
// At runtime two super instances never compare equal, even if their arguments are identical.
|
||||
|
@ -2309,7 +2283,7 @@ impl<'db> Type<'db> {
|
|||
// We eagerly normalize type[object], i.e. Type::SubclassOf(object) to `type`, i.e. Type::Instance(type).
|
||||
// So looking up a name in the MRO of `Type::Instance(type)` is equivalent to looking up the name in the
|
||||
// MRO of the class `object`.
|
||||
Type::Instance(InstanceType { class }) if class.is_known(db, KnownClass::Type) => {
|
||||
Type::Instance(instance) if instance.class().is_known(db, KnownClass::Type) => {
|
||||
KnownClass::Object
|
||||
.to_class_literal(db)
|
||||
.find_name_in_mro_with_policy(db, name, policy)
|
||||
|
@ -2399,7 +2373,7 @@ impl<'db> Type<'db> {
|
|||
|
||||
Type::Dynamic(_) | Type::Never => Symbol::bound(self).into(),
|
||||
|
||||
Type::Instance(InstanceType { class }) => class.instance_member(db, name),
|
||||
Type::Instance(instance) => instance.class().instance_member(db, name),
|
||||
|
||||
Type::FunctionLiteral(_) => KnownClass::FunctionType
|
||||
.to_instance(db)
|
||||
|
@ -2840,9 +2814,9 @@ impl<'db> Type<'db> {
|
|||
.to_instance(db)
|
||||
.member_lookup_with_policy(db, name, policy),
|
||||
|
||||
Type::Instance(InstanceType { class })
|
||||
Type::Instance(instance)
|
||||
if matches!(name.as_str(), "major" | "minor")
|
||||
&& class.is_known(db, KnownClass::VersionInfo) =>
|
||||
&& instance.class().is_known(db, KnownClass::VersionInfo) =>
|
||||
{
|
||||
let python_version = Program::get(db).python_version(db);
|
||||
let segment = if name == "major" {
|
||||
|
@ -2912,10 +2886,11 @@ impl<'db> Type<'db> {
|
|||
// attributes on the original type. But in typeshed its return type is `Any`.
|
||||
// It will need a special handling, so it remember the origin type to properly
|
||||
// resolve the attribute.
|
||||
if self.into_instance().is_some_and(|instance| {
|
||||
instance.class.is_known(db, KnownClass::ModuleType)
|
||||
|| instance.class.is_known(db, KnownClass::GenericAlias)
|
||||
}) {
|
||||
if matches!(
|
||||
self.into_instance()
|
||||
.and_then(|instance| instance.class().known(db)),
|
||||
Some(KnownClass::ModuleType | KnownClass::GenericAlias)
|
||||
) {
|
||||
return Symbol::Unbound.into();
|
||||
}
|
||||
|
||||
|
@ -3173,7 +3148,7 @@ impl<'db> Type<'db> {
|
|||
}
|
||||
},
|
||||
|
||||
Type::Instance(InstanceType { class }) => match class.known(db) {
|
||||
Type::Instance(instance) => match instance.class().known(db) {
|
||||
Some(known_class) => known_class.bool(),
|
||||
None => try_dunder_bool()?,
|
||||
},
|
||||
|
@ -4561,7 +4536,7 @@ impl<'db> Type<'db> {
|
|||
|
||||
Type::Dynamic(_) => Ok(*self),
|
||||
|
||||
Type::Instance(InstanceType { class }) => match class.known(db) {
|
||||
Type::Instance(instance) => match instance.class().known(db) {
|
||||
Some(KnownClass::TypeVar) => Ok(todo_type!(
|
||||
"Support for `typing.TypeVar` instances in type expressions"
|
||||
)),
|
||||
|
@ -4637,8 +4612,8 @@ impl<'db> Type<'db> {
|
|||
pub fn to_meta_type(&self, db: &'db dyn Db) -> Type<'db> {
|
||||
match self {
|
||||
Type::Never => Type::Never,
|
||||
Type::Instance(InstanceType { class }) => SubclassOfType::from(db, *class),
|
||||
Type::KnownInstance(known_instance) => known_instance.class().to_class_literal(db),
|
||||
Type::Instance(instance) => instance.to_meta_type(db),
|
||||
Type::KnownInstance(known_instance) => known_instance.to_meta_type(db),
|
||||
Type::PropertyInstance(_) => KnownClass::Property.to_class_literal(db),
|
||||
Type::Union(union) => union.map(db, |ty| ty.to_meta_type(db)),
|
||||
Type::BooleanLiteral(_) => KnownClass::Bool.to_class_literal(db),
|
||||
|
@ -4872,7 +4847,9 @@ impl<'db> Type<'db> {
|
|||
Some(TypeDefinition::Class(class_literal.definition(db)))
|
||||
}
|
||||
Self::GenericAlias(alias) => Some(TypeDefinition::Class(alias.definition(db))),
|
||||
Self::Instance(instance) => Some(TypeDefinition::Class(instance.class.definition(db))),
|
||||
Self::Instance(instance) => {
|
||||
Some(TypeDefinition::Class(instance.class().definition(db)))
|
||||
}
|
||||
Self::KnownInstance(instance) => match instance {
|
||||
KnownInstanceType::TypeVar(var) => {
|
||||
Some(TypeDefinition::TypeVar(var.definition(db)))
|
||||
|
@ -7198,7 +7175,7 @@ impl<'db> SuperOwnerKind<'db> {
|
|||
match self {
|
||||
SuperOwnerKind::Dynamic(dynamic) => Either::Left(ClassBase::Dynamic(dynamic).mro(db)),
|
||||
SuperOwnerKind::Class(class) => Either::Right(class.iter_mro(db)),
|
||||
SuperOwnerKind::Instance(instance) => Either::Right(instance.class.iter_mro(db)),
|
||||
SuperOwnerKind::Instance(instance) => Either::Right(instance.class().iter_mro(db)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7214,7 +7191,7 @@ impl<'db> SuperOwnerKind<'db> {
|
|||
match self {
|
||||
SuperOwnerKind::Dynamic(_) => None,
|
||||
SuperOwnerKind::Class(class) => Some(class),
|
||||
SuperOwnerKind::Instance(instance) => Some(instance.class),
|
||||
SuperOwnerKind::Instance(instance) => Some(instance.class()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7385,35 +7362,38 @@ impl<'db> BoundSuperType<'db> {
|
|||
policy: MemberLookupPolicy,
|
||||
) -> SymbolAndQualifiers<'db> {
|
||||
let owner = self.owner(db);
|
||||
match owner {
|
||||
SuperOwnerKind::Dynamic(_) => owner
|
||||
.into_type()
|
||||
.find_name_in_mro_with_policy(db, name, policy)
|
||||
.expect("Calling `find_name_in_mro` on dynamic type should return `Some`"),
|
||||
SuperOwnerKind::Class(class) | SuperOwnerKind::Instance(InstanceType { class }) => {
|
||||
let (class_literal, _) = class.class_literal(db);
|
||||
// TODO properly support super() with generic types
|
||||
// * requires a fix for https://github.com/astral-sh/ruff/issues/17432
|
||||
// * also requires understanding how we should handle cases like this:
|
||||
// ```python
|
||||
// b_int: B[int]
|
||||
// b_unknown: B
|
||||
//
|
||||
// super(B, b_int)
|
||||
// super(B[int], b_unknown)
|
||||
// ```
|
||||
match class_literal {
|
||||
ClassLiteralType::Generic(_) => {
|
||||
Symbol::bound(todo_type!("super in generic class")).into()
|
||||
}
|
||||
ClassLiteralType::NonGeneric(_) => class_literal.class_member_from_mro(
|
||||
db,
|
||||
name,
|
||||
policy,
|
||||
self.skip_until_after_pivot(db, owner.iter_mro(db)),
|
||||
),
|
||||
}
|
||||
let class = match owner {
|
||||
SuperOwnerKind::Dynamic(_) => {
|
||||
return owner
|
||||
.into_type()
|
||||
.find_name_in_mro_with_policy(db, name, policy)
|
||||
.expect("Calling `find_name_in_mro` on dynamic type should return `Some`")
|
||||
}
|
||||
SuperOwnerKind::Class(class) => *class,
|
||||
SuperOwnerKind::Instance(instance) => instance.class(),
|
||||
};
|
||||
|
||||
let (class_literal, _) = class.class_literal(db);
|
||||
// TODO properly support super() with generic types
|
||||
// * requires a fix for https://github.com/astral-sh/ruff/issues/17432
|
||||
// * also requires understanding how we should handle cases like this:
|
||||
// ```python
|
||||
// b_int: B[int]
|
||||
// b_unknown: B
|
||||
//
|
||||
// super(B, b_int)
|
||||
// super(B[int], b_unknown)
|
||||
// ```
|
||||
match class_literal {
|
||||
ClassLiteralType::Generic(_) => {
|
||||
Symbol::bound(todo_type!("super in generic class")).into()
|
||||
}
|
||||
ClassLiteralType::NonGeneric(_) => class_literal.class_member_from_mro(
|
||||
db,
|
||||
name,
|
||||
policy,
|
||||
self.skip_until_after_pivot(db, owner.iter_mro(db)),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -493,7 +493,7 @@ impl<'db> InnerIntersectionBuilder<'db> {
|
|||
_ => {
|
||||
let known_instance = new_positive
|
||||
.into_instance()
|
||||
.and_then(|instance| instance.class.known(db));
|
||||
.and_then(|instance| instance.class().known(db));
|
||||
|
||||
if known_instance == Some(KnownClass::Object) {
|
||||
// `object & T` -> `T`; it is always redundant to add `object` to an intersection
|
||||
|
@ -513,7 +513,7 @@ impl<'db> InnerIntersectionBuilder<'db> {
|
|||
new_positive = Type::BooleanLiteral(false);
|
||||
}
|
||||
Type::Instance(instance)
|
||||
if instance.class.is_known(db, KnownClass::Bool) =>
|
||||
if instance.class().is_known(db, KnownClass::Bool) =>
|
||||
{
|
||||
match new_positive {
|
||||
// `bool & AlwaysTruthy` -> `Literal[True]`
|
||||
|
@ -607,7 +607,7 @@ impl<'db> InnerIntersectionBuilder<'db> {
|
|||
self.positive
|
||||
.iter()
|
||||
.filter_map(|ty| ty.into_instance())
|
||||
.filter_map(|instance| instance.class.known(db))
|
||||
.filter_map(|instance| instance.class().known(db))
|
||||
.any(KnownClass::is_bool)
|
||||
};
|
||||
|
||||
|
@ -623,7 +623,7 @@ impl<'db> InnerIntersectionBuilder<'db> {
|
|||
Type::Never => {
|
||||
// Adding ~Never to an intersection is a no-op.
|
||||
}
|
||||
Type::Instance(instance) if instance.class.is_object(db) => {
|
||||
Type::Instance(instance) if instance.class().is_object(db) => {
|
||||
// Adding ~object to an intersection results in Never.
|
||||
*self = Self::default();
|
||||
self.positive.insert(Type::Never);
|
||||
|
|
|
@ -4,13 +4,15 @@ use std::sync::{LazyLock, Mutex};
|
|||
use super::{
|
||||
class_base::ClassBase, infer_expression_type, infer_unpack_types, IntersectionBuilder,
|
||||
KnownFunction, MemberLookupPolicy, Mro, MroError, MroIterator, SubclassOfType, Truthiness,
|
||||
Type, TypeAliasType, TypeQualifiers, TypeVarInstance,
|
||||
Type, TypeQualifiers,
|
||||
};
|
||||
use crate::semantic_index::definition::Definition;
|
||||
use crate::semantic_index::DeclarationWithConstraint;
|
||||
use crate::types::generics::{GenericContext, Specialization};
|
||||
use crate::types::signatures::{Parameter, Parameters};
|
||||
use crate::types::{CallableType, DataclassParams, DataclassTransformerParams, Signature};
|
||||
use crate::types::{
|
||||
CallableType, DataclassParams, DataclassTransformerParams, KnownInstanceType, Signature,
|
||||
};
|
||||
use crate::{
|
||||
module_resolver::file_to_module,
|
||||
semantic_index::{
|
||||
|
@ -1690,40 +1692,6 @@ impl InheritanceCycle {
|
|||
}
|
||||
}
|
||||
|
||||
/// A type representing the set of runtime objects which are instances of a certain class.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, salsa::Update)]
|
||||
pub struct InstanceType<'db> {
|
||||
pub class: ClassType<'db>,
|
||||
}
|
||||
|
||||
impl<'db> InstanceType<'db> {
|
||||
pub(super) fn is_subtype_of(self, db: &'db dyn Db, other: InstanceType<'db>) -> bool {
|
||||
// N.B. The subclass relation is fully static
|
||||
self.class.is_subclass_of(db, other.class)
|
||||
}
|
||||
|
||||
pub(super) fn is_equivalent_to(self, db: &'db dyn Db, other: InstanceType<'db>) -> bool {
|
||||
self.class.is_equivalent_to(db, other.class)
|
||||
}
|
||||
|
||||
pub(super) fn is_assignable_to(self, db: &'db dyn Db, other: InstanceType<'db>) -> bool {
|
||||
self.class.is_assignable_to(db, other.class)
|
||||
}
|
||||
|
||||
pub(super) fn is_gradual_equivalent_to(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
other: InstanceType<'db>,
|
||||
) -> bool {
|
||||
self.class.is_gradual_equivalent_to(db, other.class)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> From<InstanceType<'db>> for Type<'db> {
|
||||
fn from(value: InstanceType<'db>) -> Self {
|
||||
Self::Instance(value)
|
||||
}
|
||||
}
|
||||
/// Non-exhaustive enumeration of known classes (e.g. `builtins.int`, `typing.Any`, ...) to allow
|
||||
/// for easier syntax when interacting with very common classes.
|
||||
///
|
||||
|
@ -2447,357 +2415,6 @@ impl<'db> KnownClassLookupError<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Enumeration of specific runtime that are special enough to be considered their own type.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update)]
|
||||
pub enum KnownInstanceType<'db> {
|
||||
/// The symbol `typing.Annotated` (which can also be found as `typing_extensions.Annotated`)
|
||||
Annotated,
|
||||
/// The symbol `typing.Literal` (which can also be found as `typing_extensions.Literal`)
|
||||
Literal,
|
||||
/// The symbol `typing.LiteralString` (which can also be found as `typing_extensions.LiteralString`)
|
||||
LiteralString,
|
||||
/// The symbol `typing.Optional` (which can also be found as `typing_extensions.Optional`)
|
||||
Optional,
|
||||
/// The symbol `typing.Union` (which can also be found as `typing_extensions.Union`)
|
||||
Union,
|
||||
/// The symbol `typing.NoReturn` (which can also be found as `typing_extensions.NoReturn`)
|
||||
NoReturn,
|
||||
/// The symbol `typing.Never` available since 3.11 (which can also be found as `typing_extensions.Never`)
|
||||
Never,
|
||||
/// The symbol `typing.Any` (which can also be found as `typing_extensions.Any`)
|
||||
/// This is not used since typeshed switched to representing `Any` as a class; now we use
|
||||
/// `KnownClass::Any` instead. But we still support the old `Any = object()` representation, at
|
||||
/// least for now. TODO maybe remove?
|
||||
Any,
|
||||
/// The symbol `typing.Tuple` (which can also be found as `typing_extensions.Tuple`)
|
||||
Tuple,
|
||||
/// The symbol `typing.List` (which can also be found as `typing_extensions.List`)
|
||||
List,
|
||||
/// The symbol `typing.Dict` (which can also be found as `typing_extensions.Dict`)
|
||||
Dict,
|
||||
/// The symbol `typing.Set` (which can also be found as `typing_extensions.Set`)
|
||||
Set,
|
||||
/// The symbol `typing.FrozenSet` (which can also be found as `typing_extensions.FrozenSet`)
|
||||
FrozenSet,
|
||||
/// The symbol `typing.ChainMap` (which can also be found as `typing_extensions.ChainMap`)
|
||||
ChainMap,
|
||||
/// The symbol `typing.Counter` (which can also be found as `typing_extensions.Counter`)
|
||||
Counter,
|
||||
/// The symbol `typing.DefaultDict` (which can also be found as `typing_extensions.DefaultDict`)
|
||||
DefaultDict,
|
||||
/// The symbol `typing.Deque` (which can also be found as `typing_extensions.Deque`)
|
||||
Deque,
|
||||
/// The symbol `typing.OrderedDict` (which can also be found as `typing_extensions.OrderedDict`)
|
||||
OrderedDict,
|
||||
/// The symbol `typing.Protocol` (which can also be found as `typing_extensions.Protocol`)
|
||||
Protocol,
|
||||
/// The symbol `typing.Generic` (which can also be found as `typing_extensions.Generic`)
|
||||
Generic,
|
||||
/// 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)
|
||||
TypeAliasType(TypeAliasType<'db>),
|
||||
/// The symbol `knot_extensions.Unknown`
|
||||
Unknown,
|
||||
/// The symbol `knot_extensions.AlwaysTruthy`
|
||||
AlwaysTruthy,
|
||||
/// The symbol `knot_extensions.AlwaysFalsy`
|
||||
AlwaysFalsy,
|
||||
/// The symbol `knot_extensions.Not`
|
||||
Not,
|
||||
/// The symbol `knot_extensions.Intersection`
|
||||
Intersection,
|
||||
/// The symbol `knot_extensions.TypeOf`
|
||||
TypeOf,
|
||||
/// The symbol `knot_extensions.CallableTypeOf`
|
||||
CallableTypeOf,
|
||||
|
||||
// Various special forms, special aliases and type qualifiers that we don't yet understand
|
||||
// (all currently inferred as TODO in most contexts):
|
||||
TypingSelf,
|
||||
Final,
|
||||
ClassVar,
|
||||
Callable,
|
||||
Concatenate,
|
||||
Unpack,
|
||||
Required,
|
||||
NotRequired,
|
||||
TypeAlias,
|
||||
TypeGuard,
|
||||
TypeIs,
|
||||
ReadOnly,
|
||||
// TODO: fill this enum out with more special forms, etc.
|
||||
}
|
||||
|
||||
impl<'db> KnownInstanceType<'db> {
|
||||
/// Evaluate the known instance in boolean context
|
||||
pub(crate) const fn bool(self) -> Truthiness {
|
||||
match self {
|
||||
Self::Annotated
|
||||
| Self::Literal
|
||||
| Self::LiteralString
|
||||
| Self::Optional
|
||||
| Self::TypeVar(_)
|
||||
| Self::Union
|
||||
| Self::NoReturn
|
||||
| Self::Never
|
||||
| Self::Any
|
||||
| Self::Tuple
|
||||
| Self::Type
|
||||
| Self::TypingSelf
|
||||
| Self::Final
|
||||
| Self::ClassVar
|
||||
| Self::Callable
|
||||
| Self::Concatenate
|
||||
| Self::Unpack
|
||||
| Self::Required
|
||||
| Self::NotRequired
|
||||
| Self::TypeAlias
|
||||
| Self::TypeGuard
|
||||
| Self::TypeIs
|
||||
| Self::List
|
||||
| Self::Dict
|
||||
| Self::DefaultDict
|
||||
| Self::Set
|
||||
| Self::FrozenSet
|
||||
| Self::Counter
|
||||
| Self::Deque
|
||||
| Self::ChainMap
|
||||
| Self::OrderedDict
|
||||
| Self::Protocol
|
||||
| Self::Generic
|
||||
| Self::ReadOnly
|
||||
| Self::TypeAliasType(_)
|
||||
| Self::Unknown
|
||||
| Self::AlwaysTruthy
|
||||
| Self::AlwaysFalsy
|
||||
| Self::Not
|
||||
| Self::Intersection
|
||||
| Self::TypeOf
|
||||
| Self::CallableTypeOf => Truthiness::AlwaysTrue,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the repr of the symbol at runtime
|
||||
pub(crate) fn repr(self, db: &'db dyn Db) -> &'db str {
|
||||
match self {
|
||||
Self::Annotated => "typing.Annotated",
|
||||
Self::Literal => "typing.Literal",
|
||||
Self::LiteralString => "typing.LiteralString",
|
||||
Self::Optional => "typing.Optional",
|
||||
Self::Union => "typing.Union",
|
||||
Self::NoReturn => "typing.NoReturn",
|
||||
Self::Never => "typing.Never",
|
||||
Self::Any => "typing.Any",
|
||||
Self::Tuple => "typing.Tuple",
|
||||
Self::Type => "typing.Type",
|
||||
Self::TypingSelf => "typing.Self",
|
||||
Self::Final => "typing.Final",
|
||||
Self::ClassVar => "typing.ClassVar",
|
||||
Self::Callable => "typing.Callable",
|
||||
Self::Concatenate => "typing.Concatenate",
|
||||
Self::Unpack => "typing.Unpack",
|
||||
Self::Required => "typing.Required",
|
||||
Self::NotRequired => "typing.NotRequired",
|
||||
Self::TypeAlias => "typing.TypeAlias",
|
||||
Self::TypeGuard => "typing.TypeGuard",
|
||||
Self::TypeIs => "typing.TypeIs",
|
||||
Self::List => "typing.List",
|
||||
Self::Dict => "typing.Dict",
|
||||
Self::DefaultDict => "typing.DefaultDict",
|
||||
Self::Set => "typing.Set",
|
||||
Self::FrozenSet => "typing.FrozenSet",
|
||||
Self::Counter => "typing.Counter",
|
||||
Self::Deque => "typing.Deque",
|
||||
Self::ChainMap => "typing.ChainMap",
|
||||
Self::OrderedDict => "typing.OrderedDict",
|
||||
Self::Protocol => "typing.Protocol",
|
||||
Self::Generic => "typing.Generic",
|
||||
Self::ReadOnly => "typing.ReadOnly",
|
||||
Self::TypeVar(typevar) => typevar.name(db),
|
||||
Self::TypeAliasType(_) => "typing.TypeAliasType",
|
||||
Self::Unknown => "knot_extensions.Unknown",
|
||||
Self::AlwaysTruthy => "knot_extensions.AlwaysTruthy",
|
||||
Self::AlwaysFalsy => "knot_extensions.AlwaysFalsy",
|
||||
Self::Not => "knot_extensions.Not",
|
||||
Self::Intersection => "knot_extensions.Intersection",
|
||||
Self::TypeOf => "knot_extensions.TypeOf",
|
||||
Self::CallableTypeOf => "knot_extensions.CallableTypeOf",
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the [`KnownClass`] which this symbol is an instance of
|
||||
pub(crate) const fn class(self) -> KnownClass {
|
||||
match self {
|
||||
Self::Annotated => KnownClass::SpecialForm,
|
||||
Self::Literal => KnownClass::SpecialForm,
|
||||
Self::LiteralString => KnownClass::SpecialForm,
|
||||
Self::Optional => KnownClass::SpecialForm,
|
||||
Self::Union => KnownClass::SpecialForm,
|
||||
Self::NoReturn => KnownClass::SpecialForm,
|
||||
Self::Never => KnownClass::SpecialForm,
|
||||
Self::Any => KnownClass::Object,
|
||||
Self::Tuple => KnownClass::SpecialForm,
|
||||
Self::Type => KnownClass::SpecialForm,
|
||||
Self::TypingSelf => KnownClass::SpecialForm,
|
||||
Self::Final => KnownClass::SpecialForm,
|
||||
Self::ClassVar => KnownClass::SpecialForm,
|
||||
Self::Callable => KnownClass::SpecialForm,
|
||||
Self::Concatenate => KnownClass::SpecialForm,
|
||||
Self::Unpack => KnownClass::SpecialForm,
|
||||
Self::Required => KnownClass::SpecialForm,
|
||||
Self::NotRequired => KnownClass::SpecialForm,
|
||||
Self::TypeAlias => KnownClass::SpecialForm,
|
||||
Self::TypeGuard => KnownClass::SpecialForm,
|
||||
Self::TypeIs => KnownClass::SpecialForm,
|
||||
Self::ReadOnly => KnownClass::SpecialForm,
|
||||
Self::List => KnownClass::StdlibAlias,
|
||||
Self::Dict => KnownClass::StdlibAlias,
|
||||
Self::DefaultDict => KnownClass::StdlibAlias,
|
||||
Self::Set => KnownClass::StdlibAlias,
|
||||
Self::FrozenSet => KnownClass::StdlibAlias,
|
||||
Self::Counter => KnownClass::StdlibAlias,
|
||||
Self::Deque => KnownClass::StdlibAlias,
|
||||
Self::ChainMap => KnownClass::StdlibAlias,
|
||||
Self::OrderedDict => KnownClass::StdlibAlias,
|
||||
Self::Protocol => KnownClass::SpecialForm, // actually `_ProtocolMeta` at runtime but this is what typeshed says
|
||||
Self::Generic => KnownClass::SpecialForm, // actually `type` at runtime but this is what typeshed says
|
||||
Self::TypeVar(_) => KnownClass::TypeVar,
|
||||
Self::TypeAliasType(_) => KnownClass::TypeAliasType,
|
||||
Self::TypeOf => KnownClass::SpecialForm,
|
||||
Self::Not => KnownClass::SpecialForm,
|
||||
Self::Intersection => KnownClass::SpecialForm,
|
||||
Self::CallableTypeOf => KnownClass::SpecialForm,
|
||||
Self::Unknown => KnownClass::Object,
|
||||
Self::AlwaysTruthy => KnownClass::Object,
|
||||
Self::AlwaysFalsy => KnownClass::Object,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the instance type which this type is a subtype of.
|
||||
///
|
||||
/// For example, the symbol `typing.Literal` is an instance of `typing._SpecialForm`,
|
||||
/// so `KnownInstanceType::Literal.instance_fallback(db)`
|
||||
/// returns `Type::Instance(InstanceType { class: <typing._SpecialForm> })`.
|
||||
pub(super) fn instance_fallback(self, db: &dyn Db) -> Type {
|
||||
self.class().to_instance(db)
|
||||
}
|
||||
|
||||
/// Return `true` if this symbol is an instance of `class`.
|
||||
pub(super) fn is_instance_of(self, db: &'db dyn Db, class: ClassType<'db>) -> bool {
|
||||
self.class().is_subclass_of(db, class)
|
||||
}
|
||||
|
||||
pub(super) fn try_from_file_and_name(
|
||||
db: &'db dyn Db,
|
||||
file: File,
|
||||
symbol_name: &str,
|
||||
) -> Option<Self> {
|
||||
let candidate = match symbol_name {
|
||||
"Any" => Self::Any,
|
||||
"ClassVar" => Self::ClassVar,
|
||||
"Deque" => Self::Deque,
|
||||
"List" => Self::List,
|
||||
"Dict" => Self::Dict,
|
||||
"DefaultDict" => Self::DefaultDict,
|
||||
"Set" => Self::Set,
|
||||
"FrozenSet" => Self::FrozenSet,
|
||||
"Counter" => Self::Counter,
|
||||
"ChainMap" => Self::ChainMap,
|
||||
"OrderedDict" => Self::OrderedDict,
|
||||
"Generic" => Self::Generic,
|
||||
"Protocol" => Self::Protocol,
|
||||
"Optional" => Self::Optional,
|
||||
"Union" => Self::Union,
|
||||
"NoReturn" => Self::NoReturn,
|
||||
"Tuple" => Self::Tuple,
|
||||
"Type" => Self::Type,
|
||||
"Callable" => Self::Callable,
|
||||
"Annotated" => Self::Annotated,
|
||||
"Literal" => Self::Literal,
|
||||
"Never" => Self::Never,
|
||||
"Self" => Self::TypingSelf,
|
||||
"Final" => Self::Final,
|
||||
"Unpack" => Self::Unpack,
|
||||
"Required" => Self::Required,
|
||||
"TypeAlias" => Self::TypeAlias,
|
||||
"TypeGuard" => Self::TypeGuard,
|
||||
"TypeIs" => Self::TypeIs,
|
||||
"ReadOnly" => Self::ReadOnly,
|
||||
"Concatenate" => Self::Concatenate,
|
||||
"NotRequired" => Self::NotRequired,
|
||||
"LiteralString" => Self::LiteralString,
|
||||
"Unknown" => Self::Unknown,
|
||||
"AlwaysTruthy" => Self::AlwaysTruthy,
|
||||
"AlwaysFalsy" => Self::AlwaysFalsy,
|
||||
"Not" => Self::Not,
|
||||
"Intersection" => Self::Intersection,
|
||||
"TypeOf" => Self::TypeOf,
|
||||
"CallableTypeOf" => Self::CallableTypeOf,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
candidate
|
||||
.check_module(file_to_module(db, file)?.known()?)
|
||||
.then_some(candidate)
|
||||
}
|
||||
|
||||
/// Return `true` if `module` is a module from which this `KnownInstance` variant can validly originate.
|
||||
///
|
||||
/// Most variants can only exist in one module, which is the same as `self.class().canonical_module()`.
|
||||
/// Some variants could validly be defined in either `typing` or `typing_extensions`, however.
|
||||
pub(super) fn check_module(self, module: KnownModule) -> bool {
|
||||
match self {
|
||||
Self::Any
|
||||
| Self::ClassVar
|
||||
| Self::Deque
|
||||
| Self::List
|
||||
| Self::Dict
|
||||
| Self::DefaultDict
|
||||
| Self::Set
|
||||
| Self::FrozenSet
|
||||
| Self::Counter
|
||||
| Self::ChainMap
|
||||
| Self::OrderedDict
|
||||
| Self::Optional
|
||||
| Self::Union
|
||||
| Self::NoReturn
|
||||
| Self::Tuple
|
||||
| Self::Type
|
||||
| Self::Generic
|
||||
| Self::Callable => module.is_typing(),
|
||||
Self::Annotated
|
||||
| Self::Protocol
|
||||
| Self::Literal
|
||||
| Self::LiteralString
|
||||
| Self::Never
|
||||
| Self::TypingSelf
|
||||
| Self::Final
|
||||
| Self::Concatenate
|
||||
| Self::Unpack
|
||||
| Self::Required
|
||||
| Self::NotRequired
|
||||
| Self::TypeAlias
|
||||
| Self::TypeGuard
|
||||
| Self::TypeIs
|
||||
| Self::ReadOnly
|
||||
| Self::TypeAliasType(_)
|
||||
| Self::TypeVar(_) => {
|
||||
matches!(module, KnownModule::Typing | KnownModule::TypingExtensions)
|
||||
}
|
||||
Self::Unknown
|
||||
| Self::AlwaysTruthy
|
||||
| Self::AlwaysFalsy
|
||||
| Self::Not
|
||||
| Self::Intersection
|
||||
| Self::TypeOf
|
||||
| Self::CallableTypeOf => module.is_knot_extensions(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, salsa::Update)]
|
||||
pub(super) struct MetaclassError<'db> {
|
||||
kind: MetaclassErrorKind<'db>,
|
||||
|
|
|
@ -10,9 +10,9 @@ use crate::types::class::{ClassType, GenericAlias, GenericClass};
|
|||
use crate::types::generics::{GenericContext, Specialization};
|
||||
use crate::types::signatures::{Parameter, Parameters, Signature};
|
||||
use crate::types::{
|
||||
FunctionSignature, InstanceType, IntersectionType, KnownClass, MethodWrapperKind,
|
||||
StringLiteralType, SubclassOfInner, Type, TypeVarBoundOrConstraints, TypeVarInstance,
|
||||
UnionType, WrapperDescriptorKind,
|
||||
FunctionSignature, IntersectionType, KnownClass, MethodWrapperKind, StringLiteralType,
|
||||
SubclassOfInner, Type, TypeVarBoundOrConstraints, TypeVarInstance, UnionType,
|
||||
WrapperDescriptorKind,
|
||||
};
|
||||
use crate::Db;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
@ -73,7 +73,7 @@ impl Display for DisplayRepresentation<'_> {
|
|||
match self.ty {
|
||||
Type::Dynamic(dynamic) => dynamic.fmt(f),
|
||||
Type::Never => f.write_str("Never"),
|
||||
Type::Instance(InstanceType { class }) => match (class, class.known(self.db)) {
|
||||
Type::Instance(instance) => match (instance.class(), instance.class().known(self.db)) {
|
||||
(_, Some(KnownClass::NoneType)) => f.write_str("None"),
|
||||
(_, Some(KnownClass::NoDefaultType)) => f.write_str("NoDefault"),
|
||||
(ClassType::NonGeneric(class), _) => f.write_str(&class.class(self.db).name),
|
||||
|
|
|
@ -1041,7 +1041,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
Type::BooleanLiteral(_) | Type::IntLiteral(_) => {}
|
||||
Type::Instance(instance)
|
||||
if matches!(
|
||||
instance.class.known(self.db()),
|
||||
instance.class().known(self.db()),
|
||||
Some(KnownClass::Float | KnownClass::Int | KnownClass::Bool)
|
||||
) => {}
|
||||
_ => return false,
|
||||
|
@ -2475,7 +2475,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
}
|
||||
|
||||
// Super instances do not allow attribute assignment
|
||||
Type::Instance(instance) if instance.class.is_known(db, KnownClass::Super) => {
|
||||
Type::Instance(instance) if instance.class().is_known(db, KnownClass::Super) => {
|
||||
if emit_diagnostics {
|
||||
self.context.report_lint_old(
|
||||
&INVALID_ASSIGNMENT,
|
||||
|
@ -2991,7 +2991,10 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
|
||||
// Handle various singletons.
|
||||
if let Type::Instance(instance) = declared_ty.inner_type() {
|
||||
if instance.class.is_known(self.db(), KnownClass::SpecialForm) {
|
||||
if instance
|
||||
.class()
|
||||
.is_known(self.db(), KnownClass::SpecialForm)
|
||||
{
|
||||
if let Some(name_expr) = target.as_name_expr() {
|
||||
if let Some(known_instance) = KnownInstanceType::try_from_file_and_name(
|
||||
self.db(),
|
||||
|
@ -5780,7 +5783,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
range,
|
||||
),
|
||||
(Type::Tuple(_), Type::Instance(instance))
|
||||
if instance.class.is_known(self.db(), KnownClass::VersionInfo) =>
|
||||
if instance
|
||||
.class()
|
||||
.is_known(self.db(), KnownClass::VersionInfo) =>
|
||||
{
|
||||
self.infer_binary_type_comparison(
|
||||
left,
|
||||
|
@ -5790,7 +5795,9 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
)
|
||||
}
|
||||
(Type::Instance(instance), Type::Tuple(_))
|
||||
if instance.class.is_known(self.db(), KnownClass::VersionInfo) =>
|
||||
if instance
|
||||
.class()
|
||||
.is_known(self.db(), KnownClass::VersionInfo) =>
|
||||
{
|
||||
self.infer_binary_type_comparison(
|
||||
Type::version_info_tuple(self.db()),
|
||||
|
@ -6168,12 +6175,16 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
(
|
||||
Type::Instance(instance),
|
||||
Type::IntLiteral(_) | Type::BooleanLiteral(_) | Type::SliceLiteral(_),
|
||||
) if instance.class.is_known(self.db(), KnownClass::VersionInfo) => self
|
||||
.infer_subscript_expression_types(
|
||||
) if instance
|
||||
.class()
|
||||
.is_known(self.db(), KnownClass::VersionInfo) =>
|
||||
{
|
||||
self.infer_subscript_expression_types(
|
||||
value_node,
|
||||
Type::version_info_tuple(self.db()),
|
||||
slice_ty,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Ex) Given `("a", "b", "c", "d")[1]`, return `"b"`
|
||||
(Type::Tuple(tuple_ty), Type::IntLiteral(int)) if i32::try_from(int).is_ok() => {
|
||||
|
@ -6459,7 +6470,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
},
|
||||
Some(Type::BooleanLiteral(b)) => SliceArg::Arg(Some(i32::from(b))),
|
||||
Some(Type::Instance(instance))
|
||||
if instance.class.is_known(self.db(), KnownClass::NoneType) =>
|
||||
if instance.class().is_known(self.db(), KnownClass::NoneType) =>
|
||||
{
|
||||
SliceArg::Arg(None)
|
||||
}
|
||||
|
|
73
crates/red_knot_python_semantic/src/types/instance.rs
Normal file
73
crates/red_knot_python_semantic/src/types/instance.rs
Normal file
|
@ -0,0 +1,73 @@
|
|||
//! Instance types: both nominal and structural.
|
||||
|
||||
use super::{ClassType, KnownClass, SubclassOfType, Type};
|
||||
use crate::Db;
|
||||
|
||||
impl<'db> Type<'db> {
|
||||
pub(crate) const fn instance(class: ClassType<'db>) -> Self {
|
||||
Self::Instance(InstanceType { class })
|
||||
}
|
||||
|
||||
pub(crate) const fn into_instance(self) -> Option<InstanceType<'db>> {
|
||||
match self {
|
||||
Type::Instance(instance_type) => Some(instance_type),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A type representing the set of runtime objects which are instances of a certain nominal class.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, salsa::Update)]
|
||||
pub struct InstanceType<'db> {
|
||||
// Keep this field private, so that the only way of constructing `InstanceType` instances
|
||||
// is through the `Type::instance` constructor function.
|
||||
class: ClassType<'db>,
|
||||
}
|
||||
|
||||
impl<'db> InstanceType<'db> {
|
||||
pub(super) fn class(self) -> ClassType<'db> {
|
||||
self.class
|
||||
}
|
||||
|
||||
pub(super) fn is_subtype_of(self, db: &'db dyn Db, other: Self) -> bool {
|
||||
// N.B. The subclass relation is fully static
|
||||
self.class.is_subclass_of(db, other.class)
|
||||
}
|
||||
|
||||
pub(super) fn is_equivalent_to(self, db: &'db dyn Db, other: Self) -> bool {
|
||||
self.class.is_equivalent_to(db, other.class)
|
||||
}
|
||||
|
||||
pub(super) fn is_assignable_to(self, db: &'db dyn Db, other: Self) -> bool {
|
||||
self.class.is_assignable_to(db, other.class)
|
||||
}
|
||||
|
||||
pub(super) fn is_disjoint_from(self, db: &'db dyn Db, other: Self) -> bool {
|
||||
(self.class.is_final(db) && !self.class.is_subclass_of(db, other.class))
|
||||
|| (other.class.is_final(db) && !other.class.is_subclass_of(db, self.class))
|
||||
}
|
||||
|
||||
pub(super) fn is_gradual_equivalent_to(self, db: &'db dyn Db, other: Self) -> bool {
|
||||
self.class.is_gradual_equivalent_to(db, other.class)
|
||||
}
|
||||
|
||||
pub(super) fn is_singleton(self, db: &'db dyn Db) -> bool {
|
||||
self.class.known(db).is_some_and(KnownClass::is_singleton)
|
||||
}
|
||||
|
||||
pub(super) fn is_single_valued(self, db: &'db dyn Db) -> bool {
|
||||
self.class
|
||||
.known(db)
|
||||
.is_some_and(KnownClass::is_single_valued)
|
||||
}
|
||||
|
||||
pub(super) fn to_meta_type(self, db: &'db dyn Db) -> Type<'db> {
|
||||
SubclassOfType::from(db, self.class)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> From<InstanceType<'db>> for Type<'db> {
|
||||
fn from(value: InstanceType<'db>) -> Self {
|
||||
Self::Instance(value)
|
||||
}
|
||||
}
|
372
crates/red_knot_python_semantic/src/types/known_instance.rs
Normal file
372
crates/red_knot_python_semantic/src/types/known_instance.rs
Normal file
|
@ -0,0 +1,372 @@
|
|||
//! The `KnownInstance` type.
|
||||
//!
|
||||
//! Despite its name, this is quite a different type from [`super::InstanceType`].
|
||||
//! For the vast majority of instance-types in Python, we cannot say how many possible
|
||||
//! inhabitants there are or could be of that type at runtime. Each variant of the
|
||||
//! [`KnownInstanceType`] enum, however, represents a specific runtime symbol
|
||||
//! that requires heavy special-casing in the type system. Thus any one `KnownInstance`
|
||||
//! variant can only be inhabited by one or two specific objects at runtime with
|
||||
//! locations that are known in advance.
|
||||
|
||||
use super::{class::KnownClass, ClassType, Truthiness, Type, TypeAliasType, TypeVarInstance};
|
||||
use crate::db::Db;
|
||||
use crate::module_resolver::{file_to_module, KnownModule};
|
||||
use ruff_db::files::File;
|
||||
|
||||
/// Enumeration of specific runtime symbols that are special enough
|
||||
/// that they can each be considered to inhabit a unique type.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update)]
|
||||
pub enum KnownInstanceType<'db> {
|
||||
/// The symbol `typing.Annotated` (which can also be found as `typing_extensions.Annotated`)
|
||||
Annotated,
|
||||
/// The symbol `typing.Literal` (which can also be found as `typing_extensions.Literal`)
|
||||
Literal,
|
||||
/// The symbol `typing.LiteralString` (which can also be found as `typing_extensions.LiteralString`)
|
||||
LiteralString,
|
||||
/// The symbol `typing.Optional` (which can also be found as `typing_extensions.Optional`)
|
||||
Optional,
|
||||
/// The symbol `typing.Union` (which can also be found as `typing_extensions.Union`)
|
||||
Union,
|
||||
/// The symbol `typing.NoReturn` (which can also be found as `typing_extensions.NoReturn`)
|
||||
NoReturn,
|
||||
/// The symbol `typing.Never` available since 3.11 (which can also be found as `typing_extensions.Never`)
|
||||
Never,
|
||||
/// The symbol `typing.Any` (which can also be found as `typing_extensions.Any`)
|
||||
/// This is not used since typeshed switched to representing `Any` as a class; now we use
|
||||
/// `KnownClass::Any` instead. But we still support the old `Any = object()` representation, at
|
||||
/// least for now. TODO maybe remove?
|
||||
Any,
|
||||
/// The symbol `typing.Tuple` (which can also be found as `typing_extensions.Tuple`)
|
||||
Tuple,
|
||||
/// The symbol `typing.List` (which can also be found as `typing_extensions.List`)
|
||||
List,
|
||||
/// The symbol `typing.Dict` (which can also be found as `typing_extensions.Dict`)
|
||||
Dict,
|
||||
/// The symbol `typing.Set` (which can also be found as `typing_extensions.Set`)
|
||||
Set,
|
||||
/// The symbol `typing.FrozenSet` (which can also be found as `typing_extensions.FrozenSet`)
|
||||
FrozenSet,
|
||||
/// The symbol `typing.ChainMap` (which can also be found as `typing_extensions.ChainMap`)
|
||||
ChainMap,
|
||||
/// The symbol `typing.Counter` (which can also be found as `typing_extensions.Counter`)
|
||||
Counter,
|
||||
/// The symbol `typing.DefaultDict` (which can also be found as `typing_extensions.DefaultDict`)
|
||||
DefaultDict,
|
||||
/// The symbol `typing.Deque` (which can also be found as `typing_extensions.Deque`)
|
||||
Deque,
|
||||
/// The symbol `typing.OrderedDict` (which can also be found as `typing_extensions.OrderedDict`)
|
||||
OrderedDict,
|
||||
/// The symbol `typing.Protocol` (which can also be found as `typing_extensions.Protocol`)
|
||||
Protocol,
|
||||
/// The symbol `typing.Generic` (which can also be found as `typing_extensions.Generic`)
|
||||
Generic,
|
||||
/// 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)
|
||||
TypeAliasType(TypeAliasType<'db>),
|
||||
/// The symbol `knot_extensions.Unknown`
|
||||
Unknown,
|
||||
/// The symbol `knot_extensions.AlwaysTruthy`
|
||||
AlwaysTruthy,
|
||||
/// The symbol `knot_extensions.AlwaysFalsy`
|
||||
AlwaysFalsy,
|
||||
/// The symbol `knot_extensions.Not`
|
||||
Not,
|
||||
/// The symbol `knot_extensions.Intersection`
|
||||
Intersection,
|
||||
/// The symbol `knot_extensions.TypeOf`
|
||||
TypeOf,
|
||||
/// The symbol `knot_extensions.CallableTypeOf`
|
||||
CallableTypeOf,
|
||||
/// The symbol `typing.Callable`
|
||||
/// (which can also be found as `typing_extensions.Callable` or as `collections.abc.Callable`)
|
||||
Callable,
|
||||
|
||||
// Various special forms, special aliases and type qualifiers that we don't yet understand
|
||||
// (all currently inferred as TODO in most contexts):
|
||||
TypingSelf,
|
||||
Final,
|
||||
ClassVar,
|
||||
Concatenate,
|
||||
Unpack,
|
||||
Required,
|
||||
NotRequired,
|
||||
TypeAlias,
|
||||
TypeGuard,
|
||||
TypeIs,
|
||||
ReadOnly,
|
||||
// TODO: fill this enum out with more special forms, etc.
|
||||
}
|
||||
|
||||
impl<'db> KnownInstanceType<'db> {
|
||||
/// Evaluate the known instance in boolean context
|
||||
pub(crate) const fn bool(self) -> Truthiness {
|
||||
match self {
|
||||
Self::Annotated
|
||||
| Self::Literal
|
||||
| Self::LiteralString
|
||||
| Self::Optional
|
||||
| Self::TypeVar(_)
|
||||
| Self::Union
|
||||
| Self::NoReturn
|
||||
| Self::Never
|
||||
| Self::Any
|
||||
| Self::Tuple
|
||||
| Self::Type
|
||||
| Self::TypingSelf
|
||||
| Self::Final
|
||||
| Self::ClassVar
|
||||
| Self::Callable
|
||||
| Self::Concatenate
|
||||
| Self::Unpack
|
||||
| Self::Required
|
||||
| Self::NotRequired
|
||||
| Self::TypeAlias
|
||||
| Self::TypeGuard
|
||||
| Self::TypeIs
|
||||
| Self::List
|
||||
| Self::Dict
|
||||
| Self::DefaultDict
|
||||
| Self::Set
|
||||
| Self::FrozenSet
|
||||
| Self::Counter
|
||||
| Self::Deque
|
||||
| Self::ChainMap
|
||||
| Self::OrderedDict
|
||||
| Self::Protocol
|
||||
| Self::Generic
|
||||
| Self::ReadOnly
|
||||
| Self::TypeAliasType(_)
|
||||
| Self::Unknown
|
||||
| Self::AlwaysTruthy
|
||||
| Self::AlwaysFalsy
|
||||
| Self::Not
|
||||
| Self::Intersection
|
||||
| Self::TypeOf
|
||||
| Self::CallableTypeOf => Truthiness::AlwaysTrue,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the repr of the symbol at runtime
|
||||
pub(crate) fn repr(self, db: &'db dyn Db) -> &'db str {
|
||||
match self {
|
||||
Self::Annotated => "typing.Annotated",
|
||||
Self::Literal => "typing.Literal",
|
||||
Self::LiteralString => "typing.LiteralString",
|
||||
Self::Optional => "typing.Optional",
|
||||
Self::Union => "typing.Union",
|
||||
Self::NoReturn => "typing.NoReturn",
|
||||
Self::Never => "typing.Never",
|
||||
Self::Any => "typing.Any",
|
||||
Self::Tuple => "typing.Tuple",
|
||||
Self::Type => "typing.Type",
|
||||
Self::TypingSelf => "typing.Self",
|
||||
Self::Final => "typing.Final",
|
||||
Self::ClassVar => "typing.ClassVar",
|
||||
Self::Callable => "typing.Callable",
|
||||
Self::Concatenate => "typing.Concatenate",
|
||||
Self::Unpack => "typing.Unpack",
|
||||
Self::Required => "typing.Required",
|
||||
Self::NotRequired => "typing.NotRequired",
|
||||
Self::TypeAlias => "typing.TypeAlias",
|
||||
Self::TypeGuard => "typing.TypeGuard",
|
||||
Self::TypeIs => "typing.TypeIs",
|
||||
Self::List => "typing.List",
|
||||
Self::Dict => "typing.Dict",
|
||||
Self::DefaultDict => "typing.DefaultDict",
|
||||
Self::Set => "typing.Set",
|
||||
Self::FrozenSet => "typing.FrozenSet",
|
||||
Self::Counter => "typing.Counter",
|
||||
Self::Deque => "typing.Deque",
|
||||
Self::ChainMap => "typing.ChainMap",
|
||||
Self::OrderedDict => "typing.OrderedDict",
|
||||
Self::Protocol => "typing.Protocol",
|
||||
Self::Generic => "typing.Generic",
|
||||
Self::ReadOnly => "typing.ReadOnly",
|
||||
Self::TypeVar(typevar) => typevar.name(db),
|
||||
Self::TypeAliasType(_) => "typing.TypeAliasType",
|
||||
Self::Unknown => "knot_extensions.Unknown",
|
||||
Self::AlwaysTruthy => "knot_extensions.AlwaysTruthy",
|
||||
Self::AlwaysFalsy => "knot_extensions.AlwaysFalsy",
|
||||
Self::Not => "knot_extensions.Not",
|
||||
Self::Intersection => "knot_extensions.Intersection",
|
||||
Self::TypeOf => "knot_extensions.TypeOf",
|
||||
Self::CallableTypeOf => "knot_extensions.CallableTypeOf",
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the [`KnownClass`] which this symbol is an instance of
|
||||
pub(crate) const fn class(self) -> KnownClass {
|
||||
match self {
|
||||
Self::Annotated => KnownClass::SpecialForm,
|
||||
Self::Literal => KnownClass::SpecialForm,
|
||||
Self::LiteralString => KnownClass::SpecialForm,
|
||||
Self::Optional => KnownClass::SpecialForm,
|
||||
Self::Union => KnownClass::SpecialForm,
|
||||
Self::NoReturn => KnownClass::SpecialForm,
|
||||
Self::Never => KnownClass::SpecialForm,
|
||||
Self::Any => KnownClass::Object,
|
||||
Self::Tuple => KnownClass::SpecialForm,
|
||||
Self::Type => KnownClass::SpecialForm,
|
||||
Self::TypingSelf => KnownClass::SpecialForm,
|
||||
Self::Final => KnownClass::SpecialForm,
|
||||
Self::ClassVar => KnownClass::SpecialForm,
|
||||
Self::Callable => KnownClass::SpecialForm,
|
||||
Self::Concatenate => KnownClass::SpecialForm,
|
||||
Self::Unpack => KnownClass::SpecialForm,
|
||||
Self::Required => KnownClass::SpecialForm,
|
||||
Self::NotRequired => KnownClass::SpecialForm,
|
||||
Self::TypeAlias => KnownClass::SpecialForm,
|
||||
Self::TypeGuard => KnownClass::SpecialForm,
|
||||
Self::TypeIs => KnownClass::SpecialForm,
|
||||
Self::ReadOnly => KnownClass::SpecialForm,
|
||||
Self::List => KnownClass::StdlibAlias,
|
||||
Self::Dict => KnownClass::StdlibAlias,
|
||||
Self::DefaultDict => KnownClass::StdlibAlias,
|
||||
Self::Set => KnownClass::StdlibAlias,
|
||||
Self::FrozenSet => KnownClass::StdlibAlias,
|
||||
Self::Counter => KnownClass::StdlibAlias,
|
||||
Self::Deque => KnownClass::StdlibAlias,
|
||||
Self::ChainMap => KnownClass::StdlibAlias,
|
||||
Self::OrderedDict => KnownClass::StdlibAlias,
|
||||
Self::Protocol => KnownClass::SpecialForm, // actually `_ProtocolMeta` at runtime but this is what typeshed says
|
||||
Self::Generic => KnownClass::SpecialForm, // actually `type` at runtime but this is what typeshed says
|
||||
Self::TypeVar(_) => KnownClass::TypeVar,
|
||||
Self::TypeAliasType(_) => KnownClass::TypeAliasType,
|
||||
Self::TypeOf => KnownClass::SpecialForm,
|
||||
Self::Not => KnownClass::SpecialForm,
|
||||
Self::Intersection => KnownClass::SpecialForm,
|
||||
Self::CallableTypeOf => KnownClass::SpecialForm,
|
||||
Self::Unknown => KnownClass::Object,
|
||||
Self::AlwaysTruthy => KnownClass::Object,
|
||||
Self::AlwaysFalsy => KnownClass::Object,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the instance type which this type is a subtype of.
|
||||
///
|
||||
/// For example, the symbol `typing.Literal` is an instance of `typing._SpecialForm`,
|
||||
/// so `KnownInstanceType::Literal.instance_fallback(db)`
|
||||
/// returns `Type::Instance(InstanceType { class: <typing._SpecialForm> })`.
|
||||
pub(super) fn instance_fallback(self, db: &dyn Db) -> Type {
|
||||
self.class().to_instance(db)
|
||||
}
|
||||
|
||||
/// Return `true` if this symbol is an instance of `class`.
|
||||
pub(super) fn is_instance_of(self, db: &'db dyn Db, class: ClassType<'db>) -> bool {
|
||||
self.class().is_subclass_of(db, class)
|
||||
}
|
||||
|
||||
pub(super) fn try_from_file_and_name(
|
||||
db: &'db dyn Db,
|
||||
file: File,
|
||||
symbol_name: &str,
|
||||
) -> Option<Self> {
|
||||
let candidate = match symbol_name {
|
||||
"Any" => Self::Any,
|
||||
"ClassVar" => Self::ClassVar,
|
||||
"Deque" => Self::Deque,
|
||||
"List" => Self::List,
|
||||
"Dict" => Self::Dict,
|
||||
"DefaultDict" => Self::DefaultDict,
|
||||
"Set" => Self::Set,
|
||||
"FrozenSet" => Self::FrozenSet,
|
||||
"Counter" => Self::Counter,
|
||||
"ChainMap" => Self::ChainMap,
|
||||
"OrderedDict" => Self::OrderedDict,
|
||||
"Generic" => Self::Generic,
|
||||
"Protocol" => Self::Protocol,
|
||||
"Optional" => Self::Optional,
|
||||
"Union" => Self::Union,
|
||||
"NoReturn" => Self::NoReturn,
|
||||
"Tuple" => Self::Tuple,
|
||||
"Type" => Self::Type,
|
||||
"Callable" => Self::Callable,
|
||||
"Annotated" => Self::Annotated,
|
||||
"Literal" => Self::Literal,
|
||||
"Never" => Self::Never,
|
||||
"Self" => Self::TypingSelf,
|
||||
"Final" => Self::Final,
|
||||
"Unpack" => Self::Unpack,
|
||||
"Required" => Self::Required,
|
||||
"TypeAlias" => Self::TypeAlias,
|
||||
"TypeGuard" => Self::TypeGuard,
|
||||
"TypeIs" => Self::TypeIs,
|
||||
"ReadOnly" => Self::ReadOnly,
|
||||
"Concatenate" => Self::Concatenate,
|
||||
"NotRequired" => Self::NotRequired,
|
||||
"LiteralString" => Self::LiteralString,
|
||||
"Unknown" => Self::Unknown,
|
||||
"AlwaysTruthy" => Self::AlwaysTruthy,
|
||||
"AlwaysFalsy" => Self::AlwaysFalsy,
|
||||
"Not" => Self::Not,
|
||||
"Intersection" => Self::Intersection,
|
||||
"TypeOf" => Self::TypeOf,
|
||||
"CallableTypeOf" => Self::CallableTypeOf,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
candidate
|
||||
.check_module(file_to_module(db, file)?.known()?)
|
||||
.then_some(candidate)
|
||||
}
|
||||
|
||||
/// Return `true` if `module` is a module from which this `KnownInstance` variant can validly originate.
|
||||
///
|
||||
/// Most variants can only exist in one module, which is the same as `self.class().canonical_module()`.
|
||||
/// Some variants could validly be defined in either `typing` or `typing_extensions`, however.
|
||||
pub(super) fn check_module(self, module: KnownModule) -> bool {
|
||||
match self {
|
||||
Self::Any
|
||||
| Self::ClassVar
|
||||
| Self::Deque
|
||||
| Self::List
|
||||
| Self::Dict
|
||||
| Self::DefaultDict
|
||||
| Self::Set
|
||||
| Self::FrozenSet
|
||||
| Self::Counter
|
||||
| Self::ChainMap
|
||||
| Self::OrderedDict
|
||||
| Self::Optional
|
||||
| Self::Union
|
||||
| Self::NoReturn
|
||||
| Self::Tuple
|
||||
| Self::Type
|
||||
| Self::Generic
|
||||
| Self::Callable => module.is_typing(),
|
||||
Self::Annotated
|
||||
| Self::Protocol
|
||||
| Self::Literal
|
||||
| Self::LiteralString
|
||||
| Self::Never
|
||||
| Self::TypingSelf
|
||||
| Self::Final
|
||||
| Self::Concatenate
|
||||
| Self::Unpack
|
||||
| Self::Required
|
||||
| Self::NotRequired
|
||||
| Self::TypeAlias
|
||||
| Self::TypeGuard
|
||||
| Self::TypeIs
|
||||
| Self::ReadOnly
|
||||
| Self::TypeAliasType(_)
|
||||
| Self::TypeVar(_) => {
|
||||
matches!(module, KnownModule::Typing | KnownModule::TypingExtensions)
|
||||
}
|
||||
Self::Unknown
|
||||
| Self::AlwaysTruthy
|
||||
| Self::AlwaysFalsy
|
||||
| Self::Not
|
||||
| Self::Intersection
|
||||
| Self::TypeOf
|
||||
| Self::CallableTypeOf => module.is_knot_extensions(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn to_meta_type(self, db: &'db dyn Db) -> Type<'db> {
|
||||
self.class().to_class_literal(db)
|
||||
}
|
||||
}
|
|
@ -3,8 +3,8 @@ use std::cmp::Ordering;
|
|||
use crate::db::Db;
|
||||
|
||||
use super::{
|
||||
class_base::ClassBase, subclass_of::SubclassOfInner, DynamicType, InstanceType,
|
||||
KnownInstanceType, SuperOwnerKind, TodoType, Type,
|
||||
class_base::ClassBase, subclass_of::SubclassOfInner, DynamicType, KnownInstanceType,
|
||||
SuperOwnerKind, TodoType, Type,
|
||||
};
|
||||
|
||||
/// Return an [`Ordering`] that describes the canonical order in which two types should appear
|
||||
|
@ -126,10 +126,7 @@ pub(super) fn union_or_intersection_elements_ordering<'db>(
|
|||
|
||||
(Type::SubclassOf(_), _) => Ordering::Less,
|
||||
(_, Type::SubclassOf(_)) => Ordering::Greater,
|
||||
(
|
||||
Type::Instance(InstanceType { class: left }),
|
||||
Type::Instance(InstanceType { class: right }),
|
||||
) => left.cmp(right),
|
||||
(Type::Instance(left), Type::Instance(right)) => left.class().cmp(&right.class()),
|
||||
|
||||
(Type::Instance(_), _) => Ordering::Less,
|
||||
(_, Type::Instance(_)) => Ordering::Greater,
|
||||
|
@ -161,10 +158,9 @@ pub(super) fn union_or_intersection_elements_ordering<'db>(
|
|||
(SuperOwnerKind::Class(left), SuperOwnerKind::Class(right)) => left.cmp(right),
|
||||
(SuperOwnerKind::Class(_), _) => Ordering::Less,
|
||||
(_, SuperOwnerKind::Class(_)) => Ordering::Greater,
|
||||
(
|
||||
SuperOwnerKind::Instance(InstanceType { class: left }),
|
||||
SuperOwnerKind::Instance(InstanceType { class: right }),
|
||||
) => left.cmp(right),
|
||||
(SuperOwnerKind::Instance(left), SuperOwnerKind::Instance(right)) => {
|
||||
left.class().cmp(&right.class())
|
||||
}
|
||||
(SuperOwnerKind::Instance(_), _) => Ordering::Less,
|
||||
(_, SuperOwnerKind::Instance(_)) => Ordering::Greater,
|
||||
(SuperOwnerKind::Dynamic(left), SuperOwnerKind::Dynamic(right)) => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue