[ty] Cleanup various APIs

This commit is contained in:
Alex Waygood 2025-10-27 20:32:45 +00:00
parent 13375d0e42
commit 1a30934c33
5 changed files with 42 additions and 83 deletions

View File

@ -819,17 +819,6 @@ impl<'db> Type<'db> {
.is_some_and(|instance| instance.has_known_class(db, KnownClass::NoneType))
}
fn is_bool(&self, db: &'db dyn Db) -> bool {
self.as_nominal_instance()
.is_some_and(|instance| instance.has_known_class(db, KnownClass::Bool))
}
fn is_enum(&self, db: &'db dyn Db) -> bool {
self.as_nominal_instance()
.and_then(|instance| crate::types::enums::enum_metadata(db, instance.class_literal(db)))
.is_some()
}
/// Return true if this type overrides __eq__ or __ne__ methods
fn overrides_equality(&self, db: &'db dyn Db) -> bool {
let check_dunder = |dunder_name, allowed_return_value| {
@ -1142,7 +1131,7 @@ impl<'db> Type<'db> {
#[cfg(test)]
#[track_caller]
pub(crate) fn expect_function_literal(self) -> FunctionType<'db> {
pub(crate) const fn expect_function_literal(self) -> FunctionType<'db> {
self.as_function_literal()
.expect("Expected a Type::FunctionLiteral variant")
}
@ -1152,42 +1141,42 @@ impl<'db> Type<'db> {
}
pub(crate) fn is_union_of_single_valued(&self, db: &'db dyn Db) -> bool {
self.as_union().is_some_and(|union| {
union.elements(db).iter().all(|ty| {
ty.is_single_valued(db)
|| ty.is_bool(db)
|| ty.is_literal_string()
|| (ty.is_enum(db) && !ty.overrides_equality(db))
})
}) || self.is_bool(db)
|| self.is_literal_string()
|| (self.is_enum(db) && !self.overrides_equality(db))
match self {
Type::LiteralString => true,
Type::NominalInstance(instance) => {
instance.has_known_class(db, KnownClass::Bool)
|| (enums::enum_metadata(db, instance.class_literal(db)).is_some()
&& !self.overrides_equality(db))
}
Type::Union(union) => union.elements(db).iter().all(|element| {
element.is_single_valued(db) || element.is_union_of_single_valued(db)
}),
_ => false,
}
}
pub(crate) fn is_union_with_single_valued(&self, db: &'db dyn Db) -> bool {
self.as_union().is_some_and(|union| {
union.elements(db).iter().any(|ty| {
ty.is_single_valued(db)
|| ty.is_bool(db)
|| ty.is_literal_string()
|| (ty.is_enum(db) && !ty.overrides_equality(db))
})
}) || self.is_bool(db)
|| self.is_literal_string()
|| (self.is_enum(db) && !self.overrides_equality(db))
match self {
Type::LiteralString => true,
Type::NominalInstance(instance) => {
instance.has_known_class(db, KnownClass::Bool)
|| (enums::enum_metadata(db, instance.class_literal(db)).is_some()
&& !self.overrides_equality(db))
}
Type::Union(union) => union.elements(db).iter().any(|element| {
element.is_single_valued(db) || element.is_union_of_single_valued(db)
}),
_ => false,
}
}
pub(crate) fn as_string_literal(self) -> Option<StringLiteralType<'db>> {
pub(crate) const fn as_string_literal(self) -> Option<StringLiteralType<'db>> {
match self {
Type::StringLiteral(string_literal) => Some(string_literal),
_ => None,
}
}
pub(crate) const fn is_literal_string(&self) -> bool {
matches!(self, Type::LiteralString)
}
pub(crate) fn string_literal(db: &'db dyn Db, string: &str) -> Self {
Self::StringLiteral(StringLiteralType::new(db, string))
}
@ -7291,20 +7280,6 @@ impl<'db> Type<'db> {
}
}
pub(crate) fn generic_origin(self, db: &'db dyn Db) -> Option<ClassLiteral<'db>> {
match self {
Type::GenericAlias(generic) => Some(generic.origin(db)),
Type::NominalInstance(instance) => {
if let ClassType::Generic(generic) = instance.class(db) {
Some(generic.origin(db))
} else {
None
}
}
_ => None,
}
}
pub(super) fn has_divergent_type(self, db: &'db dyn Db, div: Type<'db>) -> bool {
any_over_type(db, self, &|ty| ty == div, false)
}
@ -11138,7 +11113,7 @@ impl<'db> TypeAliasType<'db> {
}
}
pub(crate) fn as_pep_695_type_alias(self) -> Option<PEP695TypeAliasType<'db>> {
pub(crate) const fn as_pep_695_type_alias(self) -> Option<PEP695TypeAliasType<'db>> {
match self {
TypeAliasType::PEP695(type_alias) => Some(type_alias),
TypeAliasType::ManualPEP695(_) => None,

View File

@ -256,15 +256,14 @@ impl<'a, 'db> FromIterator<(Argument<'a>, Option<Type<'db>>)> for CallArguments<
pub(crate) fn is_expandable_type<'db>(db: &'db dyn Db, ty: Type<'db>) -> bool {
match ty {
Type::NominalInstance(instance) => {
let class = instance.class(db);
class.is_known(db, KnownClass::Bool)
instance.has_known_class(db, KnownClass::Bool)
|| instance.tuple_spec(db).is_some_and(|spec| match &*spec {
Tuple::Fixed(fixed_length_tuple) => fixed_length_tuple
.all_elements()
.any(|element| is_expandable_type(db, *element)),
Tuple::Variable(_) => false,
})
|| enum_metadata(db, class.class_literal(db).0).is_some()
|| enum_metadata(db, instance.class_literal(db)).is_some()
}
Type::Union(_) => true,
_ => false,
@ -278,9 +277,7 @@ fn expand_type<'db>(db: &'db dyn Db, ty: Type<'db>) -> Option<Vec<Type<'db>>> {
// NOTE: Update `is_expandable_type` if this logic changes accordingly.
match ty {
Type::NominalInstance(instance) => {
let class = instance.class(db);
if class.is_known(db, KnownClass::Bool) {
if instance.has_known_class(db, KnownClass::Bool) {
return Some(vec![
Type::BooleanLiteral(true),
Type::BooleanLiteral(false),
@ -315,7 +312,7 @@ fn expand_type<'db>(db: &'db dyn Db, ty: Type<'db>) -> Option<Vec<Type<'db>>> {
};
}
if let Some(enum_members) = enum_member_literals(db, class.class_literal(db).0, None) {
if let Some(enum_members) = enum_member_literals(db, instance.class_literal(db), None) {
return Some(enum_members.collect());
}

View File

@ -377,10 +377,7 @@ impl<'db> ClassType<'db> {
}
pub(super) fn has_pep_695_type_params(self, db: &'db dyn Db) -> bool {
match self {
Self::NonGeneric(class) => class.has_pep_695_type_params(db),
Self::Generic(generic) => generic.origin(db).has_pep_695_type_params(db),
}
self.class_literal(db).0.has_pep_695_type_params(db)
}
/// Returns the class literal and specialization for this class. For a non-generic class, this
@ -3463,11 +3460,10 @@ impl<'db> ClassLiteral<'db> {
) -> bool {
let mut result = false;
for explicit_base in class.explicit_bases(db) {
let explicit_base_class_literal = match explicit_base {
Type::ClassLiteral(class_literal) => *class_literal,
Type::GenericAlias(generic_alias) => generic_alias.origin(db),
_ => continue,
let Some(explicit_base) = explicit_base.to_class_type(db) else {
continue;
};
let explicit_base_class_literal = explicit_base.class_literal(db).0;
if !classes_on_stack.insert(explicit_base_class_literal) {
return true;
}

View File

@ -1379,16 +1379,11 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
KnownClass::OrderedDict,
];
SAFE_MUTABLE_CLASSES
.iter()
.map(|class| class.to_instance(db))
.any(|safe_mutable_class| {
ty.is_equivalent_to(db, safe_mutable_class)
|| ty
.generic_origin(db)
.zip(safe_mutable_class.generic_origin(db))
.is_some_and(|(l, r)| l == r)
})
ty.as_nominal_instance().is_some_and(|instance| {
SAFE_MUTABLE_CLASSES
.iter()
.any(|known_class| instance.has_known_class(db, *known_class))
})
}
debug_assert!(

View File

@ -614,9 +614,7 @@ impl<'db, 'ast> NarrowingConstraintsBuilder<'db, 'ast> {
for element in lhs_union.elements(self.db) {
// Keep only the non-single-valued portion of the original type.
if !element.is_single_valued(self.db)
&& !element.is_literal_string()
&& !element.is_bool(self.db)
&& (!element.is_enum(self.db) || element.overrides_equality(self.db))
&& !element.is_union_of_single_valued(self.db)
{
builder = builder.add(*element);
}
@ -650,9 +648,7 @@ impl<'db, 'ast> NarrowingConstraintsBuilder<'db, 'ast> {
if let Some(lhs_union) = lhs_ty.as_union() {
for element in lhs_union.elements(self.db) {
if element.is_single_valued(self.db)
|| element.is_literal_string()
|| element.is_bool(self.db)
|| (element.is_enum(self.db) && !element.overrides_equality(self.db))
|| element.is_union_of_single_valued(self.db)
{
single_builder = single_builder.add(*element);
} else {