Do not narrow types to Never with binder (#18972)
Fixes https://github.com/python/mypy/issues/18967
Fixes https://github.com/python/mypy/issues/16494
Fixes https://github.com/python/mypy/issues/15793
Fixes https://github.com/python/mypy/issues/12949
As you can see from updated test cases, it is kind of gray area, so
whether we go this way will depend on the `mypy_primer` results (and
also potentially on Dropbox internal code bases, where the above issue
may cause problems).
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index 3fa4df2..008e056 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -6277,7 +6277,13 @@
known_type, restriction, prohibit_none_typevar_overlap=True
):
return None
- return narrow_declared_type(known_type, restriction)
+ narrowed = narrow_declared_type(known_type, restriction)
+ if isinstance(get_proper_type(narrowed), UninhabitedType):
+ # If we hit this case, it means that we can't reliably mark the code as
+ # unreachable, but the resulting type can't be expressed in type system.
+ # Falling back to restriction is more intuitive in most cases.
+ return restriction
+ return narrowed
return known_type
def has_abstract_type_part(self, caller_type: ProperType, callee_type: ProperType) -> bool:
diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test
index 49140bf..058db1e 100644
--- a/test-data/unit/check-isinstance.test
+++ b/test-data/unit/check-isinstance.test
@@ -1812,9 +1812,9 @@
if issubclass(fm, Foo):
reveal_type(fm) # N: Revealed type is "Type[__main__.Foo]"
if issubclass(fm, Bar):
- reveal_type(fm) # N: Revealed type is "Never"
+ reveal_type(fm) # N: Revealed type is "Type[__main__.Bar]"
if issubclass(fm, Baz):
- reveal_type(fm) # N: Revealed type is "Never"
+ reveal_type(fm) # N: Revealed type is "Type[__main__.Baz]"
[builtins fixtures/isinstance.pyi]
[case testIsinstanceAndNarrowTypeVariable]
diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test
index 1856ca2..dc2cfd4 100644
--- a/test-data/unit/check-narrowing.test
+++ b/test-data/unit/check-narrowing.test
@@ -1284,7 +1284,7 @@
reveal_type(a) # N: Revealed type is "__main__.A"
if type(b) is t:
- reveal_type(b) # N: Revealed type is "Never"
+ reveal_type(b) # N: Revealed type is "T`-1"
else:
reveal_type(b) # N: Revealed type is "__main__.B"
@@ -2413,3 +2413,14 @@
reveal_type(x) # N: Revealed type is "T`-1"
return x
[builtins fixtures/isinstance.pyi]
+
+[case testDoNotNarrowToNever]
+def any():
+ return 1
+
+def f() -> None:
+ x = "a"
+ x = any()
+ assert isinstance(x, int)
+ reveal_type(x) # N: Revealed type is "builtins.int"
+[builtins fixtures/isinstance.pyi]
diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test
index 016f505..a25a7b7 100644
--- a/test-data/unit/check-python310.test
+++ b/test-data/unit/check-python310.test
@@ -1299,7 +1299,7 @@
match m:
case a if a := 1: # E: Incompatible types in assignment (expression has type "int", variable has type "str")
- reveal_type(a) # N: Revealed type is "Never"
+ reveal_type(a) # N: Revealed type is "Literal[1]?"
[case testMatchAssigningPatternGuard]
m: str