mirror of
https://github.com/astral-sh/ruff
synced 2026-01-21 13:30:49 -05:00
## Summary Background - as a follow up to #16611 I noticed that there's a lot of code duplicated between the `is_assignable_to` and `is_subtype_of` functions and considered trying to merge them. [A subtype and an assignable type are pretty much the same](https://typing.python.org/en/latest/spec/concepts.html#the-assignable-to-or-consistent-subtyping-relation), except that subtypes are by definition fully static, so I think we can replace the whole of `is_subtype_of` with: ``` if !self.is_fully_static(db) || !target.is_fully_static(db) { return false; } return self.is_assignable_to(target) ``` if we move all of the logic to is_assignable_to and delete duplicate code. Then we can discuss if it even makes sense to have a separate is_subtype_of function (I think the answer is yes since it's used by a bunch of other places, but we may be able to basically rip out the concept). Anyways while playing with combining the functions I noticed is that the handling of Intersections in `is_subtype_of` has a special case for two intersections, which I didn't include in the last PR - rather I first handled right hand intersections before left hand, which should properly handle double intersections (hand-wavy explanation I can justify if needed - (A & B & C) is assignable to (A & B) because the left is assignable to both A and B, but none of A, B, or C is assignable to (A & B)). I took a look at what breaks if I remove the handling for double intersections, and the reason it is needed is because is_disjoint does not properly handle intersections with negative conditions (so instead `is_subtype_of` basically implements the check correctly). This PR adds support to is_disjoint for properly checking negative branches, which also lets us simplify `is_subtype_of`, bringing it in line with `is_assignable_to` ## Test Plan Added a bunch of tests, most of which failed before this fix --------- Co-authored-by: Carl Meyer <carl@astral.sh>