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

Code before:
                        mapped_base = NewTypeBase::NewType(NewType::new(
                            db,
                            inner_newtype.name(db).clone(),
                            inner_newtype.definition(db),
                            Some(mapped_base),
                        ));
                    }
                    return Some(NewType::new(
                        db,
                        self.name(db).clone(),
                        self.definition(db),
                        Some(mapped_base),
                    ));
                }
                NewTypeBase::Float | NewTypeBase::Complex => {}
            }
        }
        // If we get here, there is no `ClassType` (because this newtype is either float/complex or
        // cyclic), and we don't call `f` at all.
        Some(self)
    }

    pub(crate) fn map_base_class_type(
        self,
        db: &'db dyn Db,
        f: impl FnOnce(ClassType<'db>) -> ClassType<'db>,
    ) -> Self {
        self.try_map_base_class_type(db, |class_type| Some(f(class_type)))
            .unwrap()
    }
}

pub(crate) fn walk_newtype_instance_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
    db: &'db dyn Db,
    newtype: NewType<'db>,
    visitor: &V,
) {
    visitor.visit_type(db, newtype.base(db).instance_type(db));
}

/// `typing.NewType` typically wraps a class type, but it can also wrap another newtype.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, get_size2::GetSize, salsa::Update)]
pub enum NewTypeBase<'db> {
    ClassType(ClassType<'db>),
    NewType(NewType<'db>),
    // `float` and `complex` are special-cased in type position, where they refer to `int | float`
    // and `int | float | complex` respectively. As an extension of that special case, we allow
    // them in `NewType` bases, even though unions and other typing constructs normally aren't
    // allowed.
    Float,
    Complex,

Review comment (suggestion):
It might be worth explicitly commenting here that mapping base class types is used for normalization and applying type mappings, and neither of these apply to int/float/complex (they are already fully normalized and not generic), so it's fine to ignore them here.

Code after:
                NewTypeBase::ClassType(base_class_type) => {
                    // Call `f`.
                    let mut mapped_base = NewTypeBase::ClassType(f(base_class_type)?);
                    // Re-wrap the mapped base class in however many newtypes we unwrapped.
                    for inner_newtype in inner_newtype_stack.into_iter().rev() {
                        mapped_base = NewTypeBase::NewType(NewType::new(
                            db,
                            inner_newtype.name(db).clone(),
                            inner_newtype.definition(db),
                            Some(mapped_base),
                        ));
                    }
                    return Some(NewType::new(
                        db,
                        self.name(db).clone(),
                        self.definition(db),
                        Some(mapped_base),
                    ));
                }
                // Mapping base class types is used for normalization and applying type mappings,
                // neither of which have any effect on `float` or `complex` (which are already
                // fully normalized and non-generic), so we don't need to bother calling `f`.
                NewTypeBase::Float | NewTypeBase::Complex => {}
            }
        }
        // If we get here, there is no `ClassType` (because this newtype is either float/complex or
        // cyclic), and we don't call `f` at all.
        Some(self)
    }

    pub(crate) fn map_base_class_type(
        self,
        db: &'db dyn Db,
        f: impl FnOnce(ClassType<'db>) -> ClassType<'db>,
    ) -> Self {
        self.try_map_base_class_type(db, |class_type| Some(f(class_type)))
            .unwrap()
    }
}

pub(crate) fn walk_newtype_instance_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
    db: &'db dyn Db,
    newtype: NewType<'db>,
    visitor: &V,
) {
    visitor.visit_type(db, newtype.base(db).instance_type(db));
}

/// `typing.NewType` typically wraps a class type, but it can also wrap another newtype.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, get_size2::GetSize, salsa::Update)]
pub enum NewTypeBase<'db> {