mirror of
https://github.com/astral-sh/ruff.git
synced 2025-07-28 23:43:53 +00:00
[red-knot] Add Type
constructors for Instance
, ClassLiteral
and SubclassOf
variants (#14215)
## Summary Reduces some repetetiveness and verbosity at callsites. Addresses @carljm's review comments at https://github.com/astral-sh/ruff/pull/14155/files#r1833252458 ## Test Plan `cargo test -p red_knot_python_semantic`
This commit is contained in:
parent
eea6b31980
commit
d3f1c8e536
4 changed files with 45 additions and 51 deletions
|
@ -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<ClassLiteralType<'db>> {
|
||||
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<UnionType<'db>> {
|
||||
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<ClassLiteralType<'db>> 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));
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -406,7 +406,7 @@ impl<'db> From<ClassBase<'db>> 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue