diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index 81904bc245e0..a1b069bb324f 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -216,7 +216,9 @@ def visit_value_pattern(self, o: ValuePattern) -> PatternType: return PatternType(ok_type, rest_type, {}) def visit_singleton_pattern(self, o: SingletonPattern) -> PatternType: - current_type = self.type_context[-1] + # A bool subject with a known value (e.g. `match False:`) must not be + # expanded to both literals, or `case True:` would look reachable. + current_type = known_bool_value(self.type_context[-1]) value: bool | None = o.value if isinstance(value, bool): typ = self.chk.expr_checker.infer_literal_expr_type(value, "builtins.bool") @@ -881,3 +883,17 @@ def get_type_range(typ: Type) -> TypeRange: def is_uninhabited(typ: Type) -> bool: return isinstance(get_proper_type(typ), UninhabitedType) + + +def known_bool_value(typ: Type) -> Type: + """Replace bool types that have a known value with that literal.""" + p_typ = get_proper_type(typ) + if isinstance(p_typ, UnionType): + return UnionType.make_union([known_bool_value(t) for t in p_typ.items], p_typ.line) + if ( + isinstance(p_typ, Instance) + and p_typ.type.fullname == "builtins.bool" + and p_typ.last_known_value is not None + ): + return p_typ.last_known_value + return typ diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index 33b952f48f49..db0715885f1f 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -1979,6 +1979,43 @@ def b(value: bool) -> int: case False: return 3 +[case testMatchBoolLiteralSubjectExhaustive] +# flags: --strict-equality --warn-unreachable +def f() -> int: + match False: + case True: + return 1 # E: Statement is unreachable + case False: + return 0 + +def g(c: bool) -> int: + match True if c else False: + case True: + return 1 + case False: + return 0 + +def h() -> int: + match False: + case True: + return 1 # E: Statement is unreachable + case False: + return 0 + case other: + reveal_type(other) # E: Statement is unreachable + return 2 + +[case testBoolFinalFlagTruthinessNotNarrowedToKnownValue] +# flags: --warn-unreachable +from typing import Final + +DEBUG: Final = False + +def f() -> None: + if DEBUG: + x = 1 +[builtins fixtures/bool.pyi] + [case testMatchMiscNonExhaustiveReturn] # flags: --strict-equality --warn-unreachable class C: