diff --git a/crates/red_knot_python_semantic/src/types.rs b/crates/red_knot_python_semantic/src/types.rs index fc7ffc47e3..962775434c 100644 --- a/crates/red_knot_python_semantic/src/types.rs +++ b/crates/red_knot_python_semantic/src/types.rs @@ -369,6 +369,10 @@ impl<'db> Type<'db> { matches!(self, Type::Todo) } + pub const fn class_literal(class: Class<'db>) -> Self { + Self::ClassLiteral(ClassLiteralType { class }) + } + pub const fn into_class_literal(self) -> Option> { match self { Type::ClassLiteral(class_type) => Some(class_type), @@ -399,20 +403,6 @@ impl<'db> Type<'db> { .expect("Expected a Type::ModuleLiteral variant") } - #[must_use] - pub fn negate(&self, db: &'db dyn Db) -> Type<'db> { - IntersectionBuilder::new(db).add_negative(*self).build() - } - - #[must_use] - pub fn negate_if(&self, db: &'db dyn Db, yes: bool) -> Type<'db> { - if yes { - self.negate(db) - } else { - *self - } - } - pub const fn into_union(self) -> Option> { match self { Type::Union(union_type) => Some(union_type), @@ -480,6 +470,28 @@ impl<'db> Type<'db> { matches!(self, Type::LiteralString) } + pub const fn instance(class: Class<'db>) -> Self { + Self::Instance(InstanceType { class }) + } + + pub const fn subclass_of(class: Class<'db>) -> Self { + Self::SubclassOf(SubclassOfType { class }) + } + + #[must_use] + pub fn negate(&self, db: &'db dyn Db) -> Type<'db> { + IntersectionBuilder::new(db).add_negative(*self).build() + } + + #[must_use] + pub fn negate_if(&self, db: &'db dyn Db, yes: bool) -> Type<'db> { + if yes { + self.negate(db) + } else { + *self + } + } + /// Return true if this type is a [subtype of] type `target`. /// /// [subtype of]: https://typing.readthedocs.io/en/latest/spec/concepts.html#subtype-supertype-and-type-equivalence @@ -1371,12 +1383,8 @@ impl<'db> Type<'db> { Type::Todo => Type::Todo, Type::Unknown => Type::Unknown, Type::Never => Type::Never, - Type::ClassLiteral(ClassLiteralType { class }) => { - Type::Instance(InstanceType { class: *class }) - } - Type::SubclassOf(SubclassOfType { class }) => { - Type::Instance(InstanceType { class: *class }) - } + Type::ClassLiteral(ClassLiteralType { class }) => Type::instance(*class), + Type::SubclassOf(SubclassOfType { class }) => Type::instance(*class), Type::Union(union) => union.map(db, |element| element.to_instance(db)), // TODO: we can probably do better here: --Alex Type::Intersection(_) => Type::Todo, @@ -1420,13 +1428,13 @@ impl<'db> Type<'db> { Type::ModuleLiteral(_) => KnownClass::ModuleType.to_class(db), Type::Tuple(_) => KnownClass::Tuple.to_class(db), Type::ClassLiteral(ClassLiteralType { class }) => class.metaclass(db), - Type::SubclassOf(SubclassOfType { class }) => Type::SubclassOf( + Type::SubclassOf(SubclassOfType { class }) => Type::subclass_of( class .try_metaclass(db) .ok() .and_then(Type::into_class_literal) - .unwrap_or(KnownClass::Type.to_class(db).expect_class_literal()) - .to_subclass_of_type(), + .unwrap_or_else(|| KnownClass::Type.to_class(db).expect_class_literal()) + .class, ), Type::StringLiteral(_) | Type::LiteralString => KnownClass::Str.to_class(db), // TODO: `type[Any]`? @@ -2528,9 +2536,7 @@ impl<'db> Class<'db> { }); } - Ok(Type::ClassLiteral(ClassLiteralType { - class: candidate.metaclass, - })) + Ok(Type::class_literal(candidate.metaclass)) } /// Returns the class member of this class named `name`. @@ -2629,10 +2635,6 @@ impl<'db> ClassLiteralType<'db> { fn member(self, db: &'db dyn Db, name: &str) -> Symbol<'db> { self.class.class_member(db, name) } - - fn to_subclass_of_type(self) -> SubclassOfType<'db> { - SubclassOfType { class: self.class } - } } impl<'db> From> for Type<'db> { @@ -3053,13 +3055,11 @@ mod tests { assert!(literal_derived.is_class_literal()); // `subclass_of_base` represents `Type[Base]`. - let subclass_of_base = - Type::SubclassOf(literal_base.expect_class_literal().to_subclass_of_type()); + let subclass_of_base = Type::subclass_of(literal_base.expect_class_literal().class); assert!(literal_base.is_subtype_of(&db, subclass_of_base)); assert!(literal_derived.is_subtype_of(&db, subclass_of_base)); - let subclass_of_derived = - Type::SubclassOf(literal_derived.expect_class_literal().to_subclass_of_type()); + let subclass_of_derived = Type::subclass_of(literal_derived.expect_class_literal().class); assert!(literal_derived.is_subtype_of(&db, subclass_of_derived)); assert!(!literal_base.is_subtype_of(&db, subclass_of_derived)); @@ -3213,10 +3213,8 @@ mod tests { let literal_a = super::global_symbol(&db, module, "A").expect_type(); let literal_b = super::global_symbol(&db, module, "B").expect_type(); - let subclass_of_a = - Type::SubclassOf(literal_a.expect_class_literal().to_subclass_of_type()); - let subclass_of_b = - Type::SubclassOf(literal_b.expect_class_literal().to_subclass_of_type()); + let subclass_of_a = Type::subclass_of(literal_a.expect_class_literal().class); + let subclass_of_b = Type::subclass_of(literal_b.expect_class_literal().class); // Class literals are always disjoint. They are singleton types assert!(literal_a.is_disjoint_from(&db, literal_b)); diff --git a/crates/red_knot_python_semantic/src/types/infer.rs b/crates/red_knot_python_semantic/src/types/infer.rs index 7f6fbdd6bd..a876754b16 100644 --- a/crates/red_knot_python_semantic/src/types/infer.rs +++ b/crates/red_knot_python_semantic/src/types/infer.rs @@ -1042,7 +1042,7 @@ impl<'db> TypeInferenceBuilder<'db> { .and_then(|module| KnownClass::maybe_from_module(module, name.as_str())); let class = Class::new(self.db, &*name.id, body_scope, maybe_known_class); - let class_ty = Type::ClassLiteral(ClassLiteralType { class }); + let class_ty = Type::class_literal(class); self.add_declaration_with_binding(class_node.into(), definition, class_ty, class_ty); @@ -1349,15 +1349,13 @@ impl<'db> TypeInferenceBuilder<'db> { // anything else is invalid and should lead to a diagnostic being reported --Alex match node_ty { Type::Any | Type::Unknown => node_ty, - Type::ClassLiteral(ClassLiteralType { class }) => { - Type::Instance(InstanceType { class }) - } + Type::ClassLiteral(ClassLiteralType { class }) => Type::instance(class), Type::Tuple(tuple) => UnionType::from_elements( self.db, tuple.elements(self.db).iter().map(|ty| { ty.into_class_literal() .map_or(Type::Todo, |ClassLiteralType { class }| { - Type::Instance(InstanceType { class }) + Type::instance(class) }) }), ), @@ -4283,8 +4281,8 @@ impl<'db> TypeInferenceBuilder<'db> { match slice { ast::Expr::Name(name) => { let name_ty = self.infer_name_expression(name); - if let Some(class_literal) = name_ty.into_class_literal() { - Type::SubclassOf(class_literal.to_subclass_of_type()) + if let Some(ClassLiteralType { class }) = name_ty.into_class_literal() { + Type::subclass_of(class) } else { Type::Todo } diff --git a/crates/red_knot_python_semantic/src/types/mro.rs b/crates/red_knot_python_semantic/src/types/mro.rs index 8ff2279e36..0182db2994 100644 --- a/crates/red_knot_python_semantic/src/types/mro.rs +++ b/crates/red_knot_python_semantic/src/types/mro.rs @@ -406,7 +406,7 @@ impl<'db> From> for Type<'db> { ClassBase::Any => Type::Any, ClassBase::Todo => Type::Todo, ClassBase::Unknown => Type::Unknown, - ClassBase::Class(class) => Type::ClassLiteral(ClassLiteralType { class }), + ClassBase::Class(class) => Type::class_literal(class), } } } diff --git a/crates/red_knot_python_semantic/src/types/narrow.rs b/crates/red_knot_python_semantic/src/types/narrow.rs index 4c6ee34dad..f9b051ed20 100644 --- a/crates/red_knot_python_semantic/src/types/narrow.rs +++ b/crates/red_knot_python_semantic/src/types/narrow.rs @@ -5,7 +5,7 @@ use crate::semantic_index::expression::Expression; use crate::semantic_index::symbol::{ScopeId, ScopedSymbolId, SymbolTable}; use crate::semantic_index::symbol_table; use crate::types::{ - infer_expression_types, ClassLiteralType, InstanceType, IntersectionBuilder, KnownClass, + infer_expression_types, ClassLiteralType, IntersectionBuilder, KnownClass, KnownConstraintFunction, KnownFunction, Truthiness, Type, UnionBuilder, }; use crate::Db; @@ -353,14 +353,12 @@ impl<'db> NarrowingConstraintsBuilder<'db> { let to_constraint = match function { KnownConstraintFunction::IsInstance => { |class_literal: ClassLiteralType<'db>| { - Type::Instance(InstanceType { - class: class_literal.class, - }) + Type::instance(class_literal.class) } } KnownConstraintFunction::IsSubclass => { |class_literal: ClassLiteralType<'db>| { - Type::SubclassOf(class_literal.to_subclass_of_type()) + Type::subclass_of(class_literal.class) } } };