diff --git a/crates/ty_python_semantic/src/types/class.rs b/crates/ty_python_semantic/src/types/class.rs index 398696c928..874d3cd291 100644 --- a/crates/ty_python_semantic/src/types/class.rs +++ b/crates/ty_python_semantic/src/types/class.rs @@ -3531,8 +3531,9 @@ impl<'db> ClassLiteral<'db> { // The attribute is not *declared* in the class body. It could still be declared/bound // in a method. - let result = Self::implicit_attribute(db, body_scope, name, MethodDecorator::None); - self.apply_slots_constraints(db, name, result) + let result = + Self::implicit_attribute(db, body_scope, name, MethodDecorator::None); + self.apply_slots_constraints(db, name, result.inner) } } } else { @@ -3540,7 +3541,7 @@ impl<'db> ClassLiteral<'db> { // It could still be implicitly defined in a method. let result = Self::implicit_attribute(db, body_scope, name, MethodDecorator::None); - self.apply_slots_constraints(db, name, result) + self.apply_slots_constraints(db, name, result.inner) } } @@ -5527,14 +5528,15 @@ impl<'db> ClassLiteral<'db> { /// Extract the names of attributes defined in __slots__ as a set of strings. /// Returns None if __slots__ is not defined, empty, or dynamic. pub(super) fn slots_members(self, db: &'db dyn Db) -> Option> { - let Place::Type(slots_ty, bound) = self + let Place::Defined(slots_ty, _, definedness) = self .own_class_member(db, self.generic_context(db), None, "__slots__") + .inner .place else { return None; }; - if matches!(bound, Boundness::PossiblyUnbound) { + if matches!(definedness, Definedness::PossiblyUndefined) { return None; } @@ -5579,24 +5581,31 @@ impl<'db> ClassLiteral<'db> { db: &'db dyn Db, name: &str, result: PlaceAndQualifiers<'db>, - ) -> PlaceAndQualifiers<'db> { + ) -> Member<'db> { // TODO: This function will be extended to support: // - Inheritance: Check slots across the MRO chain // - `__dict__` special case: Allow dynamic attributes when `__dict__` is in slots - + if let Some(slots) = self.slots_members(db) { if slots.contains(name) { // Attribute is in __slots__, so it's allowed even if not found elsewhere - if result.place.is_unbound() { + if result.place.is_undefined() { // Return as possibly unbound since it's declared but not necessarily initialized - return Place::Type(Type::unknown(), Boundness::PossiblyUnbound).into(); + return Member { + inner: Place::Defined( + Type::unknown(), + TypeOrigin::Inferred, + Definedness::PossiblyUndefined, + ) + .into(), + }; } - return result; + return Member { inner: result }; } // Attribute is not in __slots__ - return Place::Unbound.into(); + return Member::unbound(); } - result + Member { inner: result } } }