From f4a21a4010405a3e98d0345de6c8f7ca955ce892 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Sun, 31 Oct 2021 23:55:32 +0300 Subject: [PATCH] Fixes type narrowing for overlaping runtime types (#11273) Closes #11272 --- mypy/checker.py | 8 +++++--- mypy/subtypes.py | 2 ++ test-data/unit/check-inference.test | 2 +- test-data/unit/check-narrowing.test | 29 +++++++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 9c389a3d08d8c..c9fba158e0557 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -5395,9 +5395,11 @@ def conditional_type_map(expr: Expression, return None, {} else: # we can only restrict when the type is precise, not bounded - proposed_precise_type = UnionType([type_range.item - for type_range in proposed_type_ranges - if not type_range.is_upper_bound]) + proposed_precise_type = UnionType.make_union([ + type_range.item + for type_range in proposed_type_ranges + if not type_range.is_upper_bound + ]) remaining_type = restrict_subtype_away(current_type, proposed_precise_type) return {expr: proposed_type}, {expr: remaining_type} else: diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 7ec3c3a9b4b79..b145745c3f32f 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -1138,6 +1138,8 @@ def restrict_subtype_away(t: Type, s: Type, *, ignore_promotions: bool = False) if (isinstance(get_proper_type(item), AnyType) or not covers_at_runtime(item, s, ignore_promotions))] return UnionType.make_union(new_items) + elif covers_at_runtime(t, s, ignore_promotions): + return UninhabitedType() else: return t diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 80bc40b6ca98d..652a261638586 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -1975,7 +1975,7 @@ T = TypeVar('T') class A: def f(self) -> None: - self.g() # E: Too few arguments for "g" of "A" + self.g() # E: Too few arguments for "g" of "A" self.g(1) @dec def g(self, x: str) -> None: pass diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index 8cf85536f3b98..6b030ae49a866 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -1143,3 +1143,32 @@ else: reveal_type(abc) # N: Revealed type is "TypedDict('__main__.B', {'tag': Literal['B'], 'b': builtins.int})" [builtins fixtures/primitives.pyi] + + +[case testNarrowingRuntimeCover] +from typing import Dict, List, Union + +def unreachable(x: Union[str, List[str]]) -> None: + if isinstance(x, str): + reveal_type(x) # N: Revealed type is "builtins.str" + elif isinstance(x, list): + reveal_type(x) # N: Revealed type is "builtins.list[builtins.str]" + else: + reveal_type(x) # N: Revealed type is "" + +def all_parts_covered(x: Union[str, List[str], List[int], int]) -> None: + if isinstance(x, str): + reveal_type(x) # N: Revealed type is "builtins.str" + elif isinstance(x, list): + reveal_type(x) # N: Revealed type is "Union[builtins.list[builtins.str], builtins.list[builtins.int]]" + else: + reveal_type(x) # N: Revealed type is "builtins.int" + +def two_type_vars(x: Union[str, Dict[str, int], Dict[bool, object], int]) -> None: + if isinstance(x, str): + reveal_type(x) # N: Revealed type is "builtins.str" + elif isinstance(x, dict): + reveal_type(x) # N: Revealed type is "Union[builtins.dict[builtins.str, builtins.int], builtins.dict[builtins.bool, builtins.object]]" + else: + reveal_type(x) # N: Revealed type is "builtins.int" +[builtins fixtures/dict.pyi]