From e127ada8c428d62d8d2a60fa85a6743351ab3b63 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Tue, 5 Oct 2021 23:28:42 +0300 Subject: [PATCH 1/3] Fixes type narrowing for overlaping runtime types, refs #11272 --- mypy/checker.py | 2 +- mypy/subtypes.py | 2 ++ test-data/unit/check-inference.test | 2 +- test-data/unit/check-narrowing.test | 21 +++++++++++++++++++++ 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 4cd5c6c5580d..c85090e5535a 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -5325,7 +5325,7 @@ 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 + 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) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 52d03282494f..b1f6dba58cef 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -1131,6 +1131,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 80bc40b6ca98..652a26163858 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 8cf85536f3b9..7e0c4f366628 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -1143,3 +1143,24 @@ 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 List, Union + +def foo(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 bar(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" +[builtins fixtures/list.pyi] From 0ee45823f88b82fbb4e13d3c8ed8e40c58096167 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Tue, 5 Oct 2021 23:30:12 +0300 Subject: [PATCH 2/3] Fixes type narrowing for overlaping runtime types, refs #11272 --- mypy/checker.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index c85090e5535a..2c71c468ed46 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -5325,9 +5325,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.make_union([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: From f99462af00b0b6c177a47c01156d1cad381dc291 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Tue, 5 Oct 2021 23:52:21 +0300 Subject: [PATCH 3/3] Typo fixed --- test-data/unit/check-narrowing.test | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index 7e0c4f366628..6b030ae49a86 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -1146,9 +1146,9 @@ else: [case testNarrowingRuntimeCover] -from typing import List, Union +from typing import Dict, List, Union -def foo(x: Union[str, List[str]]) -> None: +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): @@ -1156,11 +1156,19 @@ def foo(x: Union[str, List[str]]) -> None: else: reveal_type(x) # N: Revealed type is "" -def bar(x: Union[str, List[str], List[int], int]) -> None: +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" -[builtins fixtures/list.pyi] + +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]