ruff/crates/red_knot_python_semantic/resources/mdtest/comparison/unions.md
Micha Reiser 6aaf1d9446
[red-knot] Remove lint-phase (#13922)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-10-25 18:40:52 +00:00

2.5 KiB

Comparison: Unions

Union on one side of the comparison

Comparisons on union types need to consider all possible cases:

def bool_instance() -> bool:
    return True

flag = bool_instance()
one_or_two = 1 if flag else 2

reveal_type(one_or_two <= 2)  # revealed: Literal[True]
reveal_type(one_or_two <= 1)  # revealed: bool
reveal_type(one_or_two <= 0)  # revealed: Literal[False]

reveal_type(2 >= one_or_two)  # revealed: Literal[True]
reveal_type(1 >= one_or_two)  # revealed: bool
reveal_type(0 >= one_or_two)  # revealed: Literal[False]

reveal_type(one_or_two < 1)  # revealed: Literal[False]
reveal_type(one_or_two < 2)  # revealed: bool
reveal_type(one_or_two < 3)  # revealed: Literal[True]

reveal_type(one_or_two > 0)  # revealed: Literal[True]
reveal_type(one_or_two > 1)  # revealed: bool
reveal_type(one_or_two > 2)  # revealed: Literal[False]

reveal_type(one_or_two == 3)  # revealed: Literal[False]
reveal_type(one_or_two == 1)  # revealed: bool

reveal_type(one_or_two != 3)  # revealed: Literal[True]
reveal_type(one_or_two != 1)  # revealed: bool

a_or_ab = "a" if flag else "ab"

reveal_type(a_or_ab in "ab")  # revealed: Literal[True]
reveal_type("a" in a_or_ab)  # revealed: Literal[True]

reveal_type("c" not in a_or_ab)  # revealed: Literal[True]
reveal_type("a" not in a_or_ab)  # revealed: Literal[False]

reveal_type("b" in a_or_ab)  # revealed: bool
reveal_type("b" not in a_or_ab)  # revealed: bool

one_or_none = 1 if flag else None

reveal_type(one_or_none is None)  # revealed: bool
reveal_type(one_or_none is not None)  # revealed: bool

Union on both sides of the comparison

With unions on both sides, we need to consider the full cross product of options when building the resulting (union) type:

def bool_instance() -> bool:
    return True

flag_s, flag_l = bool_instance(), bool_instance()
small = 1 if flag_s else 2
large = 2 if flag_l else 3

reveal_type(small <= large)  # revealed: Literal[True]
reveal_type(small >= large)  # revealed: bool

reveal_type(small < large)  # revealed: bool
reveal_type(small > large)  # revealed: Literal[False]

Unsupported operations

Make sure we emit a diagnostic if any of the possible comparisons is unsupported. For now, we fall back to bool for the result type instead of trying to infer something more precise from the other (supported) variants:

def bool_instance() -> bool:
    return True

flag = bool_instance()
x = [1, 2] if flag else 1

result = 1 in x  # error: "Operator `in` is not supported"
reveal_type(result)  # revealed: bool