From 1813d5adce9aa413e1ce99753689d452acfd8bbb Mon Sep 17 00:00:00 2001 From: hauntsaninja Date: Tue, 29 Aug 2023 20:44:28 -0700 Subject: [PATCH] Fix partially defined in the case of missing type maps Thanks @AlexWaygood for sending me on this adventure. This took me a while to debug! It turns out when we don't need to warn about unreachable code, we don't end up calling `self.is_noop_for_reachability(s)` (which is meant to tell us whether the code should be warned about or is `raise AssertionError` or `typing.assert_never(never)` or something. https://github.com/python/mypy/blob/6f650cff9ab21f81069e0ae30c92eae94219ea63/mypy/checker.py#L2748 This innocuous check has a side effect that turns out to be important for the partially undefined checks. These checks work by reaching into the type map populated by the checker. But if we never actually ended up analysing the code, we never populate the type map. This therefore changes things to assume that if we couldn't find the expression in the type map, it's probably because it was unreachable. --- mypy/partially_defined.py | 2 +- test-data/unit/check-possibly-undefined.test | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/mypy/partially_defined.py b/mypy/partially_defined.py index 47cbd671f168..b7f577110fa8 100644 --- a/mypy/partially_defined.py +++ b/mypy/partially_defined.py @@ -506,7 +506,7 @@ def visit_break_stmt(self, o: BreakStmt) -> None: self.tracker.skip_branch() def visit_expression_stmt(self, o: ExpressionStmt) -> None: - if isinstance(self.type_map.get(o.expr, None), UninhabitedType): + if isinstance(self.type_map.get(o.expr, None), (UninhabitedType, type(None))): self.tracker.skip_branch() super().visit_expression_stmt(o) diff --git a/test-data/unit/check-possibly-undefined.test b/test-data/unit/check-possibly-undefined.test index ebceef88b537..ae277949c049 100644 --- a/test-data/unit/check-possibly-undefined.test +++ b/test-data/unit/check-possibly-undefined.test @@ -1026,3 +1026,20 @@ class B: else: # Same as above but in a loop. b = a # E: Name "a" may be undefined + +[case testUnreachableCausingMissingTypeMap] +# flags: --enable-error-code possibly-undefined --enable-error-code used-before-def --no-warn-unreachable +# Regression test for https://github.com/python/mypy/issues/15958 +from typing import Union, NoReturn + +def assert_never(__x: NoReturn) -> NoReturn: ... + +def foo(x: Union[int, str]) -> None: + if isinstance(x, str): + f = "foo" + elif isinstance(x, int): + f = "bar" + else: + assert_never(x) + f # OK +[builtins fixtures/tuple.pyi]