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

Code before:
                        collapse = collapse || ignore || other_type_negated().is_subtype_of(db, ty);
                        !ty.is_subtype_of(db, other_type)
                    });
                    some_literals_remain = !literals.is_empty();
                } else {
                    return ReduceResult::KeepIf(
                        !Type::BytesLiteral(literals[0]).is_subtype_of(db, other_type),
                    );
                }
            }
            UnionElement::Type(existing) => return ReduceResult::Type(*existing),
        }

        if ignore {
            ReduceResult::Ignore
        } else if collapse {
            ReduceResult::CollapseToObject
        } else {
            ReduceResult::KeepIf(some_literals_remain)
        }
    }
}

enum ReduceResult<'db> {
    /// Reduction of this `UnionElement` is complete; keep it in the union if the nested
    /// boolean is true, eliminate it from the union if false.
    KeepIf(bool),
    /// Collapse this entire union to `object`.
    CollapseToObject,
    /// The new element is a subtype of an existing part of the `UnionElement`, ignore it.
    Ignore,
    /// The given `Type` can stand-in for the entire `UnionElement` for further union
    /// simplification checks.
    Type(Type<'db>),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, get_size2::GetSize)]
pub enum RecursivelyDefined {
    Yes,
    No,
}

impl RecursivelyDefined {
    const fn is_yes(self) -> bool {
        matches!(self, RecursivelyDefined::Yes)
    }

    const fn or(self, other: RecursivelyDefined) -> RecursivelyDefined {
        match (self, other) {
            (RecursivelyDefined::Yes, _) | (_, RecursivelyDefined::Yes) => RecursivelyDefined::Yes,
            _ => RecursivelyDefined::No,

Review comment (refactor):
Given that `ignore` always takes precedence. Should we use a single state variable instead, e.g. `result: Option<ReduceResult>` or are there cases where it's important to tell `ignore` and `collapse` apart?

This would also simplify the collapse case that you don't need to write `collapse = collapse || ignore || ... `

I think that might even allow you to remove the `some_literals_remain` state variable too

Code after:
            UnionElement::StringLiterals(literals) => {
                if other_type.splits_literals(db, LiteralKind::String) {
                    literals.retain(|literal| should_retain_type(Type::StringLiteral(*literal)));
                    !literals.is_empty()
                } else {
                    !Type::StringLiteral(literals[0]).is_subtype_of(db, other_type)
                }
            }
            UnionElement::BytesLiterals(literals) => {
                if other_type.splits_literals(db, LiteralKind::Bytes) {
                    literals.retain(|literal| should_retain_type(Type::BytesLiteral(*literal)));
                    !literals.is_empty()
                } else {
                    !Type::BytesLiteral(literals[0]).is_subtype_of(db, other_type)
                }
            }
            UnionElement::Type(existing) => return ReduceResult::Type(*existing),
        };

        if ignore {
            ReduceResult::Ignore
        } else if collapse {
            ReduceResult::CollapseToObject
        } else {
            ReduceResult::KeepIf(should_keep)
        }
    }
}

enum ReduceResult<'db> {
    /// Reduction of this `UnionElement` is complete; keep it in the union if the nested
    /// boolean is true, eliminate it from the union if false.
    KeepIf(bool),
    /// Collapse this entire union to `object`.
    CollapseToObject,
    /// The new element is a subtype of an existing part of the `UnionElement`, ignore it.
    Ignore,
    /// The given `Type` can stand-in for the entire `UnionElement` for further union
    /// simplification checks.
    Type(Type<'db>),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, get_size2::GetSize)]
pub enum RecursivelyDefined {
    Yes,
    No,
}

impl RecursivelyDefined {
    const fn is_yes(self) -> bool {
        matches!(self, RecursivelyDefined::Yes)