diff --git a/mypy/typeops.py b/mypy/typeops.py index d22448a715e5..7f530d13d4e2 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -657,6 +657,10 @@ def false_only(t: Type) -> ProperType: new_items = [false_only(item) for item in t.items] can_be_false_items = [item for item in new_items if item.can_be_false] return make_simplified_union(can_be_false_items, line=t.line, column=t.column) + elif isinstance(t, Instance) and t.type.fullname in ("builtins.str", "builtins.bytes"): + return LiteralType("", fallback=t) + elif isinstance(t, Instance) and t.type.fullname == "builtins.int": + return LiteralType(0, fallback=t) else: ret_type = _get_type_method_ret_type(t, name="__bool__") or _get_type_method_ret_type( t, name="__len__" diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index 61cee1d00c58..d5ddc910bcd6 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -342,7 +342,7 @@ b: bool i: str j = b or i if not j: - reveal_type(j) # N: Revealed type is "builtins.str" + reveal_type(j) # N: Revealed type is "Literal['']" [builtins fixtures/bool.pyi] [case testAndOr] diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index e142fdd5d060..0cb4bf8e4a19 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -1007,7 +1007,7 @@ str_or_false: Union[Literal[False], str] if str_or_false: reveal_type(str_or_false) # N: Revealed type is "builtins.str" else: - reveal_type(str_or_false) # N: Revealed type is "Union[Literal[False], builtins.str]" + reveal_type(str_or_false) # N: Revealed type is "Union[Literal[False], Literal['']]" true_or_false: Literal[True, False] @@ -1017,6 +1017,22 @@ else: reveal_type(true_or_false) # N: Revealed type is "Literal[False]" [builtins fixtures/primitives.pyi] +[case testNarrowingFalseyToLiteral] +from typing import Union + +a: str +b: bytes +c: int +d: Union[str, bytes, int] + +if not a: + reveal_type(a) # N: Revealed type is "Literal['']" +if not b: + reveal_type(b) # N: Revealed type is "Literal[b'']" +if not c: + reveal_type(c) # N: Revealed type is "Literal[0]" +if not d: + reveal_type(d) # N: Revealed type is "Union[Literal[''], Literal[b''], Literal[0]]" [case testNarrowingIsInstanceFinalSubclass] # flags: --warn-unreachable diff --git a/test-data/unit/check-optional.test b/test-data/unit/check-optional.test index f80aa5115bc3..683ce0446915 100644 --- a/test-data/unit/check-optional.test +++ b/test-data/unit/check-optional.test @@ -53,14 +53,14 @@ x = None # type: Optional[int] if x: reveal_type(x) # N: Revealed type is "builtins.int" else: - reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" + reveal_type(x) # N: Revealed type is "Union[Literal[0], None]" [builtins fixtures/bool.pyi] [case testIfNotCases] from typing import Optional x = None # type: Optional[int] if not x: - reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" + reveal_type(x) # N: Revealed type is "Union[Literal[0], None]" else: reveal_type(x) # N: Revealed type is "builtins.int" [builtins fixtures/bool.pyi] @@ -109,13 +109,13 @@ reveal_type(z2) # N: Revealed type is "Union[builtins.int, builtins.str, None]" from typing import Optional x = None # type: Optional[str] y1 = x and 'b' -reveal_type(y1) # N: Revealed type is "Union[builtins.str, None]" +reveal_type(y1) # N: Revealed type is "Union[Literal[''], None, builtins.str]" y2 = x and 1 # x could be '', so... -reveal_type(y2) # N: Revealed type is "Union[builtins.str, None, builtins.int]" +reveal_type(y2) # N: Revealed type is "Union[Literal[''], None, builtins.int]" z1 = 'b' and x reveal_type(z1) # N: Revealed type is "Union[builtins.str, None]" z2 = int() and x -reveal_type(z2) # N: Revealed type is "Union[builtins.int, builtins.str, None]" +reveal_type(z2) # N: Revealed type is "Union[Literal[0], builtins.str, None]" [case testLambdaReturningNone] f = lambda: None diff --git a/test-data/unit/check-python38.test b/test-data/unit/check-python38.test index dfb918defb0a..199014a66fed 100644 --- a/test-data/unit/check-python38.test +++ b/test-data/unit/check-python38.test @@ -463,9 +463,9 @@ def check_partial_list() -> None: if (x := 0): reveal_type(x) # E: Statement is unreachable else: - reveal_type(x) # N: Revealed type is "builtins.int" + reveal_type(x) # N: Revealed type is "Literal[0]" -reveal_type(x) # N: Revealed type is "builtins.int" +reveal_type(x) # N: Revealed type is "Literal[0]" [case testWalrusAssignmentAndConditionScopeForProperty] # flags: --warn-unreachable @@ -483,7 +483,7 @@ wrapper = PropertyWrapper() if x := wrapper.f: reveal_type(x) # N: Revealed type is "builtins.str" else: - reveal_type(x) # N: Revealed type is "builtins.str" + reveal_type(x) # N: Revealed type is "Literal['']" reveal_type(x) # N: Revealed type is "builtins.str" @@ -505,7 +505,7 @@ def f() -> str: ... if x := f(): reveal_type(x) # N: Revealed type is "builtins.str" else: - reveal_type(x) # N: Revealed type is "builtins.str" + reveal_type(x) # N: Revealed type is "Literal['']" reveal_type(x) # N: Revealed type is "builtins.str"