[ty] Remove ClassBase::TypedDict
This commit is contained in:
@@ -476,7 +476,12 @@ impl<'db> BoundSuperType<'db> {
|
||||
},
|
||||
Type::SpecialForm(SpecialFormType::Protocol) => ClassBase::Protocol,
|
||||
Type::SpecialForm(SpecialFormType::Generic) => ClassBase::Generic,
|
||||
Type::SpecialForm(SpecialFormType::TypedDict) => ClassBase::TypedDict,
|
||||
Type::SpecialForm(SpecialFormType::TypedDict) => {
|
||||
match KnownClass::Dict.to_class_literal(db).to_class_type(db) {
|
||||
Some(dict_class) => ClassBase::Class(dict_class),
|
||||
None => ClassBase::unknown(),
|
||||
}
|
||||
}
|
||||
Type::Dynamic(dynamic) => ClassBase::Dynamic(dynamic),
|
||||
_ => {
|
||||
return Err(BoundSuperError::InvalidPivotClassType {
|
||||
@@ -491,7 +496,7 @@ impl<'db> BoundSuperType<'db> {
|
||||
let pivot_class = pivot_class.class_literal(db).0;
|
||||
if !owner_class.iter_mro(db).any(|superclass| match superclass {
|
||||
ClassBase::Dynamic(_) => true,
|
||||
ClassBase::Generic | ClassBase::Protocol | ClassBase::TypedDict => false,
|
||||
ClassBase::Generic | ClassBase::Protocol => false,
|
||||
ClassBase::Class(superclass) => superclass.class_literal(db).0 == pivot_class,
|
||||
}) {
|
||||
return Err(BoundSuperError::FailingConditionCheck {
|
||||
|
||||
@@ -645,10 +645,8 @@ impl<'db> ClassType<'db> {
|
||||
}
|
||||
},
|
||||
|
||||
// Protocol, Generic, and TypedDict are not represented by a ClassType.
|
||||
ClassBase::Protocol | ClassBase::Generic | ClassBase::TypedDict => {
|
||||
ConstraintSet::from(false)
|
||||
}
|
||||
// Protocol and Generic are not represented by a ClassType.
|
||||
ClassBase::Protocol | ClassBase::Generic => ConstraintSet::from(false),
|
||||
|
||||
ClassBase::Class(base) => match (base, other) {
|
||||
(ClassType::NonGeneric(base), ClassType::NonGeneric(other)) => {
|
||||
@@ -1961,8 +1959,15 @@ impl<'db> ClassLiteral<'db> {
|
||||
return known.is_typed_dict_subclass();
|
||||
}
|
||||
|
||||
self.iter_mro(db, None)
|
||||
.any(|base| matches!(base, ClassBase::TypedDict))
|
||||
self.explicit_bases(db).iter().any(|base| {
|
||||
if matches!(base, Type::SpecialForm(SpecialFormType::TypedDict)) {
|
||||
return true;
|
||||
}
|
||||
if let Some(class) = base.to_class_type(db) {
|
||||
return class.class_literal(db).0.is_typed_dict(db);
|
||||
}
|
||||
false
|
||||
})
|
||||
}
|
||||
|
||||
/// Compute `TypedDict` parameters dynamically based on MRO detection and AST parsing.
|
||||
@@ -2204,6 +2209,32 @@ impl<'db> ClassLiteral<'db> {
|
||||
ClassBase::Class(class) => {
|
||||
let known = class.known(db);
|
||||
|
||||
// For TypedDicts, when we reach the `dict` base, fall back to TypedDictFallback.
|
||||
if known == Some(KnownClass::Dict) && self.is_typed_dict(db) {
|
||||
return KnownClass::TypedDictFallback
|
||||
.to_class_literal(db)
|
||||
.find_name_in_mro_with_policy(db, name, policy)
|
||||
.expect("Will return Some() when called on class literal")
|
||||
.map_type(|ty| {
|
||||
ty.apply_type_mapping(
|
||||
db,
|
||||
&TypeMapping::ReplaceSelf {
|
||||
new_upper_bound: determine_upper_bound(
|
||||
db,
|
||||
self,
|
||||
None,
|
||||
|base| {
|
||||
base.into_class().is_some_and(|c| {
|
||||
c.is_known(db, KnownClass::Dict)
|
||||
})
|
||||
},
|
||||
),
|
||||
},
|
||||
TypeContext::default(),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
if known == Some(KnownClass::Object)
|
||||
// Only exclude `object` members if this is not an `object` class itself
|
||||
&& (policy.mro_no_object_fallback() && !self.is_known(db, KnownClass::Object))
|
||||
@@ -2230,26 +2261,6 @@ impl<'db> ClassLiteral<'db> {
|
||||
)
|
||||
});
|
||||
}
|
||||
ClassBase::TypedDict => {
|
||||
return KnownClass::TypedDictFallback
|
||||
.to_class_literal(db)
|
||||
.find_name_in_mro_with_policy(db, name, policy)
|
||||
.expect("Will return Some() when called on class literal")
|
||||
.map_type(|ty| {
|
||||
ty.apply_type_mapping(
|
||||
db,
|
||||
&TypeMapping::ReplaceSelf {
|
||||
new_upper_bound: determine_upper_bound(
|
||||
db,
|
||||
self,
|
||||
None,
|
||||
ClassBase::is_typed_dict,
|
||||
),
|
||||
},
|
||||
TypeContext::default(),
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
if lookup_result.is_ok() {
|
||||
break;
|
||||
@@ -3169,20 +3180,18 @@ impl<'db> ClassLiteral<'db> {
|
||||
.to_class_literal(db)
|
||||
.find_name_in_mro_with_policy(db, name, policy)
|
||||
.expect("`find_name_in_mro_with_policy` will return `Some()` when called on class literal")
|
||||
.map_type(|ty|
|
||||
.map_type(|ty| {
|
||||
ty.apply_type_mapping(
|
||||
db,
|
||||
&TypeMapping::ReplaceSelf {
|
||||
new_upper_bound: determine_upper_bound(
|
||||
db,
|
||||
self,
|
||||
specialization,
|
||||
ClassBase::is_typed_dict
|
||||
)
|
||||
new_upper_bound: determine_upper_bound(db, self, specialization, |base| {
|
||||
base.into_class()
|
||||
.is_some_and(|c| c.is_known(db, KnownClass::Dict))
|
||||
}),
|
||||
},
|
||||
TypeContext::default(),
|
||||
TypeContext::default(),
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3449,23 +3458,6 @@ impl<'db> ClassLiteral<'db> {
|
||||
union_qualifiers |= qualifiers;
|
||||
}
|
||||
}
|
||||
ClassBase::TypedDict => {
|
||||
return KnownClass::TypedDictFallback
|
||||
.to_instance(db)
|
||||
.instance_member(db, name)
|
||||
.map_type(|ty| {
|
||||
ty.apply_type_mapping(
|
||||
db,
|
||||
&TypeMapping::ReplaceSelf {
|
||||
new_upper_bound: Type::instance(
|
||||
db,
|
||||
self.unknown_specialization(db),
|
||||
),
|
||||
},
|
||||
TypeContext::default(),
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ pub enum ClassBase<'db> {
|
||||
/// but nonetheless appears in the MRO of classes that inherit from `Generic[T]`,
|
||||
/// `Protocol[T]`, or bare `Protocol`.
|
||||
Generic,
|
||||
TypedDict,
|
||||
}
|
||||
|
||||
impl<'db> ClassBase<'db> {
|
||||
@@ -39,7 +38,7 @@ impl<'db> ClassBase<'db> {
|
||||
match self {
|
||||
Self::Dynamic(dynamic) => Self::Dynamic(dynamic.normalized()),
|
||||
Self::Class(class) => Self::Class(class.normalized_impl(db, visitor)),
|
||||
Self::Protocol | Self::Generic | Self::TypedDict => self,
|
||||
Self::Protocol | Self::Generic => self,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +53,7 @@ impl<'db> ClassBase<'db> {
|
||||
Self::Class(class) => Some(Self::Class(
|
||||
class.recursive_type_normalized_impl(db, div, nested)?,
|
||||
)),
|
||||
Self::Protocol | Self::Generic | Self::TypedDict => Some(self),
|
||||
Self::Protocol | Self::Generic => Some(self),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +68,6 @@ impl<'db> ClassBase<'db> {
|
||||
ClassBase::Dynamic(DynamicType::Divergent(_)) => "Divergent",
|
||||
ClassBase::Protocol => "Protocol",
|
||||
ClassBase::Generic => "Generic",
|
||||
ClassBase::TypedDict => "TypedDict",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,10 +79,6 @@ impl<'db> ClassBase<'db> {
|
||||
.map_or(Self::unknown(), Self::Class)
|
||||
}
|
||||
|
||||
pub(super) const fn is_typed_dict(self) -> bool {
|
||||
matches!(self, ClassBase::TypedDict)
|
||||
}
|
||||
|
||||
/// Attempt to resolve `ty` into a `ClassBase`.
|
||||
///
|
||||
/// Return `None` if `ty` is not an acceptable type for a class base.
|
||||
@@ -295,7 +289,9 @@ impl<'db> ClassBase<'db> {
|
||||
SpecialFormType::OrderedDict => {
|
||||
Self::try_from_type(db, KnownClass::OrderedDict.to_class_literal(db), subclass)
|
||||
}
|
||||
SpecialFormType::TypedDict => Some(Self::TypedDict),
|
||||
SpecialFormType::TypedDict => {
|
||||
Self::try_from_type(db, KnownClass::Dict.to_class_literal(db), subclass)
|
||||
}
|
||||
SpecialFormType::Callable => Self::try_from_type(
|
||||
db,
|
||||
todo_type!("Support for Callable as a base class"),
|
||||
@@ -308,7 +304,7 @@ impl<'db> ClassBase<'db> {
|
||||
pub(super) fn into_class(self) -> Option<ClassType<'db>> {
|
||||
match self {
|
||||
Self::Class(class) => Some(class),
|
||||
Self::Dynamic(_) | Self::Generic | Self::Protocol | Self::TypedDict => None,
|
||||
Self::Dynamic(_) | Self::Generic | Self::Protocol => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,7 +319,7 @@ impl<'db> ClassBase<'db> {
|
||||
Self::Class(class) => {
|
||||
Self::Class(class.apply_type_mapping_impl(db, type_mapping, tcx, visitor))
|
||||
}
|
||||
Self::Dynamic(_) | Self::Generic | Self::Protocol | Self::TypedDict => self,
|
||||
Self::Dynamic(_) | Self::Generic | Self::Protocol => self,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,10 +361,7 @@ impl<'db> ClassBase<'db> {
|
||||
.try_mro(db, specialization)
|
||||
.is_err_and(MroError::is_cycle)
|
||||
}
|
||||
ClassBase::Dynamic(_)
|
||||
| ClassBase::Generic
|
||||
| ClassBase::Protocol
|
||||
| ClassBase::TypedDict => false,
|
||||
ClassBase::Dynamic(_) | ClassBase::Generic | ClassBase::Protocol => false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,9 +373,7 @@ impl<'db> ClassBase<'db> {
|
||||
) -> impl Iterator<Item = ClassBase<'db>> {
|
||||
match self {
|
||||
ClassBase::Protocol => ClassBaseMroIterator::length_3(db, self, ClassBase::Generic),
|
||||
ClassBase::Dynamic(_) | ClassBase::Generic | ClassBase::TypedDict => {
|
||||
ClassBaseMroIterator::length_2(db, self)
|
||||
}
|
||||
ClassBase::Dynamic(_) | ClassBase::Generic => ClassBaseMroIterator::length_2(db, self),
|
||||
ClassBase::Class(class) => {
|
||||
ClassBaseMroIterator::from_class(db, class, additional_specialization)
|
||||
}
|
||||
@@ -402,7 +393,6 @@ impl<'db> ClassBase<'db> {
|
||||
ClassBase::Class(class) => Type::from(class).display(self.db).fmt(f),
|
||||
ClassBase::Protocol => f.write_str("typing.Protocol"),
|
||||
ClassBase::Generic => f.write_str("typing.Generic"),
|
||||
ClassBase::TypedDict => f.write_str("typing.TypedDict"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -424,7 +414,6 @@ impl<'db> From<ClassBase<'db>> for Type<'db> {
|
||||
ClassBase::Class(class) => class.into(),
|
||||
ClassBase::Protocol => Type::SpecialForm(SpecialFormType::Protocol),
|
||||
ClassBase::Generic => Type::SpecialForm(SpecialFormType::Generic),
|
||||
ClassBase::TypedDict => Type::SpecialForm(SpecialFormType::TypedDict),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,10 +276,7 @@ impl<'db> Mro<'db> {
|
||||
continue;
|
||||
}
|
||||
match base {
|
||||
ClassBase::Class(_)
|
||||
| ClassBase::Generic
|
||||
| ClassBase::Protocol
|
||||
| ClassBase::TypedDict => {
|
||||
ClassBase::Class(_) | ClassBase::Generic | ClassBase::Protocol => {
|
||||
errors.push(DuplicateBaseError {
|
||||
duplicate_base: base,
|
||||
first_index: *first_index,
|
||||
|
||||
@@ -146,10 +146,12 @@ fn check_class_declaration<'db>(
|
||||
|
||||
let mut subclass_overrides_superclass_declaration = false;
|
||||
let mut has_dynamic_superclass = false;
|
||||
let mut has_typeddict_in_mro = false;
|
||||
let mut liskov_diagnostic_emitted = false;
|
||||
let mut overridden_final_method = None;
|
||||
|
||||
// Check if this class is a TypedDict upfront so we can fall back to TypedDictFallback.
|
||||
let is_typed_dict = literal.is_typed_dict(db);
|
||||
|
||||
for class_base in class.iter_mro(db).skip(1) {
|
||||
let superclass = match class_base {
|
||||
ClassBase::Protocol | ClassBase::Generic => continue,
|
||||
@@ -157,10 +159,6 @@ fn check_class_declaration<'db>(
|
||||
has_dynamic_superclass = true;
|
||||
continue;
|
||||
}
|
||||
ClassBase::TypedDict => {
|
||||
has_typeddict_in_mro = true;
|
||||
continue;
|
||||
}
|
||||
ClassBase::Class(class) => class,
|
||||
};
|
||||
|
||||
@@ -262,6 +260,12 @@ fn check_class_declaration<'db>(
|
||||
continue;
|
||||
}
|
||||
|
||||
// TypedDict classes cannot have methods, so there's no point in checking for Liskov
|
||||
// violations. We already emit `invalid-typed-dict-statement` for this case.
|
||||
if is_typed_dict {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(superclass_type_as_callable) = superclass_type.try_upcast_to_callable(db) else {
|
||||
continue;
|
||||
};
|
||||
@@ -286,7 +290,7 @@ fn check_class_declaration<'db>(
|
||||
}
|
||||
|
||||
if !subclass_overrides_superclass_declaration && !has_dynamic_superclass {
|
||||
if has_typeddict_in_mro {
|
||||
if is_typed_dict {
|
||||
if !KnownClass::TypedDictFallback
|
||||
.to_instance(db)
|
||||
.member(db, &member.name)
|
||||
|
||||
@@ -173,9 +173,6 @@ pub(super) fn union_or_intersection_elements_ordering<'db>(
|
||||
(ClassBase::Generic, _) => Ordering::Less,
|
||||
(_, ClassBase::Generic) => Ordering::Greater,
|
||||
|
||||
(ClassBase::TypedDict, _) => Ordering::Less,
|
||||
(_, ClassBase::TypedDict) => Ordering::Greater,
|
||||
|
||||
(ClassBase::Dynamic(left), ClassBase::Dynamic(right)) => {
|
||||
dynamic_elements_ordering(left, right)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user