Repository: astral-sh/ruff
File: crates/ty_python_semantic/src/types/class.rs (Rust)

Code before:
    }

    pub(super) fn to_non_generic_instance(self, db: &'db dyn Db) -> Type<'db> {
        Type::instance(db, ClassType::NonGeneric(self.into()))
    }

    /// Return this class' involvement in an inheritance cycle, if any.
    ///
    /// A class definition like this will fail at runtime,
    /// but we must be resilient to it or we could panic.
    #[salsa::tracked(cycle_initial=|_, _, _| None, heap_size=ruff_memory_usage::heap_size)]
    pub(super) fn inheritance_cycle(self, db: &'db dyn Db) -> Option<InheritanceCycle> {
        /// Return `true` if the class is cyclically defined.
        ///
        /// Also, populates `visited_classes` with all base classes of `self`.
        fn is_cyclically_defined_recursive<'db>(
            db: &'db dyn Db,
            class: StaticClassLiteral<'db>,
            classes_on_stack: &mut IndexSet<StaticClassLiteral<'db>>,
            visited_classes: &mut IndexSet<StaticClassLiteral<'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.as_static(),
                    Type::GenericAlias(generic_alias) => Some(generic_alias.origin(db)),
                    _ => continue,
                };
                let Some(explicit_base_class_literal) = explicit_base_class_literal else {
                    continue;
                };
                if !classes_on_stack.insert(explicit_base_class_literal) {
                    return true;
                }

                if visited_classes.insert(explicit_base_class_literal) {
                    // If we find a cycle, keep searching to check if we can reach the starting class.
                    result |= is_cyclically_defined_recursive(
                        db,
                        explicit_base_class_literal,
                        classes_on_stack,
                        visited_classes,
                    );
                }
                classes_on_stack.pop();
            }
            result
        }

        tracing::trace!("Class::inheritance_cycle: {}", self.name(db));


Review comment (performance):
The `own_fields()` call here is probably somewhat expensive, since it isn't a Salsa-tracked method (`ClassType::fields()` is currently, but not `ClassType::own_fields()`). This may be the cause of the 2% perf regression on the `tanjun` benchmark on Codspeed. We could consider adding Salsa caching to that method and see if it gets rid of the regression -- though the regression is small enough that I don't think it should be blocking; we could try adding Salsa caching as a separate, standalone change.

Code after:
                }
            }
        } else {
            // This attribute is neither declared nor bound in the class body.
            // It could still be implicitly defined in a method.

            Self::implicit_attribute(db, body_scope, name, MethodDecorator::None)
        }
    }

    /// Returns `true` if `name` is a non-init-only field directly declared on this
    /// dataclass (i.e., a field that corresponds to an instance attribute).
    ///
    /// This is used to decide whether a bare class-body annotation like `x: int`
    /// should be treated as defining an instance attribute: dataclass fields are
    /// implicitly assigned in `__init__`, so they behave as instance attributes
    /// even though no explicit binding exists in the class body.
    fn is_own_dataclass_instance_field(self, db: &'db dyn Db, name: &str) -> bool {
        let Some(field_policy) = CodeGeneratorKind::from_static_class(db, self, None) else {
            return false;
        };
        if !matches!(field_policy, CodeGeneratorKind::DataclassLike(_)) {
            return false;
        }

        let fields = self.own_fields(db, None, field_policy);
        let Some(field) = fields.get(name) else {
            return false;
        };
        matches!(
            field.kind,
            FieldKind::Dataclass {
                init_only: false,
                ..
            }
        )
    }

    pub(super) fn to_non_generic_instance(self, db: &'db dyn Db) -> Type<'db> {
        Type::instance(db, ClassType::NonGeneric(self.into()))
    }

    /// Return this class' involvement in an inheritance cycle, if any.
    ///
    /// A class definition like this will fail at runtime,
    /// but we must be resilient to it or we could panic.
    #[salsa::tracked(cycle_initial=|_, _, _| None, heap_size=ruff_memory_usage::heap_size)]
    pub(super) fn inheritance_cycle(self, db: &'db dyn Db) -> Option<InheritanceCycle> {
        /// Return `true` if the class is cyclically defined.
        ///
        /// Also, populates `visited_classes` with all base classes of `self`.