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

Code before:
            // We *could* have a branch here where both `self` and `other` are mixed tuples
            // with same-length prefixes and same-length suffixes. We *could* zip the two
            // `prefix` vecs together, unioning each pair of elements to create a new `prefix`
            // vec, and do the same for the `suffix` vecs. This would preserve the tuple specs
            // of the union elements more closely. But it's hard to think of a test where this
            // would actually lead to more precise inference, so it's probably not worth the
            // complexity.
            _ => {
                let unioned =
                    UnionType::from_elements(db, self.all_elements().chain(other.all_elements()));
                TupleSpecBuilder::Variable {
                    prefix: vec![],
                    variable: unioned,
                    suffix: vec![],
                }
            }
        }
    }

    /// Return a new tuple-spec builder that reflects the intersection of this tuple and another tuple.
    ///
    /// For example, if `self` is a tuple-spec builder for `tuple[int, str]` and `other` is a
    /// tuple-spec for `tuple[object, object]`, the result will be a tuple-spec builder for
    /// `tuple[int, str]` (since `int & object` simplifies to `int`, and `str & object` to `str`).
    ///
    /// We preserve "fixed-length-ness" in the following cases:
    /// - Both `self` and `other` are fixed-length with the same length: element-wise intersection
    /// - `self` is fixed-length and `other` is a homogeneous variable-length tuple: intersect each
    ///   element of `self` with `other`'s variable type
    ///
    /// For other cases (e.g., different fixed lengths, or complex variable-length tuples with
    /// prefixes or suffixes), we fall back to a homogeneous variable-length tuple.
    pub(crate) fn intersect(mut self, db: &'db dyn Db, other: &TupleSpec<'db>) -> Self {
        match (&mut self, other) {
            // Both fixed-length with the same length: element-wise intersection.
            (TupleSpecBuilder::Fixed(our_elements), TupleSpec::Fixed(new_elements))
                if our_elements.len() == new_elements.len() =>
            {
                for (existing, new) in our_elements.iter_mut().zip(new_elements.elements()) {
                    *existing = IntersectionType::from_elements(db, [*existing, *new]);
                }
                self
            }

            // Fixed-length intersected with a homogeneous variable-length tuple:
            // intersect each element with the variable type.
            (TupleSpecBuilder::Fixed(our_elements), TupleSpec::Variable(var))
                if var.prefix.is_empty() && var.suffix.is_empty() =>
            {
                for existing in our_elements.iter_mut() {
                    *existing = IntersectionType::from_elements(db, [*existing, var.variable]);

Review comment (bug):
My concern here is that if you have two tuples `X` and `Y`, the tuple spec of `X & Y` should _always_ be narrower than either the tuple spec for `X` or the tuple spec for `Y`. The last branch of this method violates that, I think: you could end up with a spec from the intersected tuples that's broader than one of the two tuples being thrown into the intersection, which is going to lead to some seriously weird behaviour at some point 😄

Here's a version of this method that's still not totally free of TODOs, but I think is much more generalised than what you have currently:

```rs
    /// Return a new tuple-spec builder that reflects the intersection of this tuple and another tuple.
    ///
    /// For example, if `self` is a tuple-spec builder for `tuple[int, str]` and `other` is a
    /// tuple-spec for `tuple[object, object]`, the result will be a tuple-spec builder for
    /// `tuple[int, str]` (since `int & object` simplifies to `int`, and `str & object` to `str`).
    pub(crate) fn intersect(mut self, db: &'db dyn Db, other: &TupleSpec<'db>) -> Self {
        match (&mut self, other) {
            // Both fixed-length with the same length: element-wise intersection.
            (TupleSpecBuilder::Fixed(our_elements), TupleSpec::Fixed(new_elements))
                if our_elements.len() == new_elements.len() =>
            {
                for (existing, new) in our_elements.iter_mut().zip(new_elements.elements()) {
                    *existing = IntersectionType::from_elements(db, [*existing, *new]);
                }
                return self;
            }

            (TupleSpecBuilder::Fixed(our_elements), TupleSpec::Variable(var)) => {
                if let Ok(tuple) = var.resize(db, TupleLength::Fixed(our_elements.len())) {
                    return self.intersect(db, &tuple);
                }
            }

            (TupleSpecBuilder::Variable { .. }, TupleSpec::Fixed(fixed)) => {
                if let Ok(tuple) = self
                    .clone()
                    .build()
                    .resize(db, TupleLength::Fixed(fixed.len()))
                {
                    return TupleSpecBuilder::from(&tuple).intersect(db, other);
                }
            }

            (
                TupleSpecBuilder::Variable {
                    prefix,
                    variable,
                    suffix,
                },
                TupleSpec::Variable(var),
            ) => {
                if prefix.len() == var.prefix.len() && suffix.len() == var.suffix.len() {
                    for (existing, new) in prefix.iter_mut().zip(var.prefix_elements()) {
                        *existing = IntersectionType::from_elements(db, [*existing, *new]);
                    }
                    *variable = IntersectionType::from_elements(db, [*variable, var.variable]);
                    for (existing, new) in suffix.iter_mut().zip(var.suffix_elements()) {
                        *existing = IntersectionType::from_elements(db, [*existing, *new]);
                    }
                    return self;
                }

                let self_built = self.clone().build();
                let self_len = self_built.len();
                if let Ok(resized) = var.resize(db, self_len) {
                    return self.intersect(db, &resized);
                } else if let Ok(resized) = self_built.resize(db, var.len()) {
                    return TupleSpecBuilder::from(&resized).intersect(db, other);
                }
            }

            _ => {}
        }

        // TODO: probably incorrect? `tuple[int, str] & tuple[int, str, bytes]` should resolve to `Never`.
        // So maybe this function should be fallible (return an `Option`)?
        let intersected =
            IntersectionType::from_elements(db, self.all_elements().chain(other.all_elements()));
        TupleSpecBuilder::Variable {
            prefix: vec![],
            variable: intersected,
            suffix: vec![],
        }
    }
```

Code after:

            // We *could* have a branch here where both `self` and `other` are mixed tuples
            // with same-length prefixes and same-length suffixes. We *could* zip the two
            // `prefix` vecs together, unioning each pair of elements to create a new `prefix`
            // vec, and do the same for the `suffix` vecs. This would preserve the tuple specs
            // of the union elements more closely. But it's hard to think of a test where this
            // would actually lead to more precise inference, so it's probably not worth the
            // complexity.
            _ => {
                let unioned =
                    UnionType::from_elements(db, self.all_elements().chain(other.all_elements()));
                TupleSpecBuilder::Variable {
                    prefix: vec![],
                    variable: unioned,
                    suffix: vec![],
                }
            }
        }
    }

    /// Return a new tuple-spec builder that reflects the intersection of this tuple and another tuple.
    ///
    /// For example, if `self` is a tuple-spec builder for `tuple[int, str]` and `other` is a
    /// tuple-spec for `tuple[object, object]`, the result will be a tuple-spec builder for
    /// `tuple[int, str]` (since `int & object` simplifies to `int`, and `str & object` to `str`).
    pub(crate) fn intersect(mut self, db: &'db dyn Db, other: &TupleSpec<'db>) -> Self {
        match (&mut self, other) {
            // Both fixed-length with the same length: element-wise intersection.
            (TupleSpecBuilder::Fixed(our_elements), TupleSpec::Fixed(new_elements))
                if our_elements.len() == new_elements.len() =>
            {
                for (existing, new) in our_elements.iter_mut().zip(new_elements.elements()) {
                    *existing = IntersectionType::from_elements(db, [*existing, *new]);
                }
                return self;
            }

            (TupleSpecBuilder::Fixed(our_elements), TupleSpec::Variable(var)) => {
                if let Ok(tuple) = var.resize(db, TupleLength::Fixed(our_elements.len())) {
                    return self.intersect(db, &tuple);
                }
            }

            (TupleSpecBuilder::Variable { .. }, TupleSpec::Fixed(fixed)) => {
                if let Ok(tuple) = self
                    .clone()
                    .build()
                    .resize(db, TupleLength::Fixed(fixed.len()))
                {
                    return TupleSpecBuilder::from(&tuple).intersect(db, other);
                }