ruff/crates/ty_python_semantic/resources/mdtest/narrow
David Peter dc66019fbc
[ty] Expansion of enums into unions of literals (#19382)
## Summary

Implement expansion of enums into unions of enum literals (and the
reverse operation). For the enum below, this allows us to understand
that `Color = Literal[Color.RED, Color.GREEN, Color.BLUE]`, or that
`Color & ~Literal[Color.RED] = Literal[Color.GREEN, Color.BLUE]`. This
helps in exhaustiveness checking, which is why we see some removed
`assert_never` false positives. And since exhaustiveness checking also
helps with understanding terminal control flow, we also see a few
removed `invalid-return-type` and `possibly-unresolved-reference` false
positives. This PR also adds expansion of enums in overload resolution
and type narrowing constructs.

```py
from enum import Enum
from typing_extensions import Literal, assert_never
from ty_extensions import Intersection, Not, static_assert, is_equivalent_to

class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3

type Red = Literal[Color.RED]
type Green = Literal[Color.GREEN]
type Blue = Literal[Color.BLUE]

static_assert(is_equivalent_to(Red | Green | Blue, Color))
static_assert(is_equivalent_to(Intersection[Color, Not[Red]], Green | Blue))


def color_name(color: Color) -> str:  # no error here (we detect that this can not implicitly return None)
    if color is Color.RED:
        return "Red"
    elif color is Color.GREEN:
        return "Green"
    elif color is Color.BLUE:
        return "Blue"
    else:
        assert_never(color)  # no error here
```

## Performance

I avoided an initial regression here for large enums, but the
`UnionBuilder` and `IntersectionBuilder` parts can certainly still be
optimized. We might want to use the same technique that we also use for
unions of other literals. I didn't see any problems in our benchmarks so
far, so this is not included yet.

## Test Plan

Many new Markdown tests
2025-07-21 19:37:55 +02:00
..
conditionals [ty] Expansion of enums into unions of literals (#19382) 2025-07-21 19:37:55 +02:00
assert.md Rename Red Knot (#17820) 2025-05-03 19:49:15 +02:00
assignment.md [ty] Reduce false positives for TypedDict types (#19354) 2025-07-15 12:47:19 +01:00
bool-call.md Rename Red Knot (#17820) 2025-05-03 19:49:15 +02:00
boolean.md Rename Red Knot (#17820) 2025-05-03 19:49:15 +02:00
complex_target.md [ty] Homogeneous and mixed tuples (#18600) 2025-06-20 18:23:54 -04:00
hasattr.md [ty] Improve protocol member type checking and relation handling (#18847) 2025-06-29 10:46:33 +00:00
isinstance.md [ty] Support narrowing on isinstance()/issubclass() if the second argument is a dynamic, intersection, union or typevar type (#18900) 2025-06-24 10:55:26 +00:00
issubclass.md [ty] Support narrowing on isinstance()/issubclass() if the second argument is a dynamic, intersection, union or typevar type (#18900) 2025-06-24 10:55:26 +00:00
match.md [ty] Improve disjointness inference for NominalInstanceTypes and SubclassOfTypes (#18864) 2025-06-24 20:27:37 +00:00
post_if_statement.md Rename Red Knot (#17820) 2025-05-03 19:49:15 +02:00
truthiness.md [ty] Reachability constraints (#18621) 2025-06-17 09:24:28 +02:00
type.md [ty] Surface matched overload diagnostic directly (#18452) 2025-06-20 08:36:49 +05:30
type_guards.md [ty] Improve disjointness inference for NominalInstanceTypes and SubclassOfTypes (#18864) 2025-06-24 20:27:37 +00:00
while.md [ty] Eagerly simplify 'True' and 'False' constraints (#18998) 2025-06-30 13:11:52 +02:00