Skip to content

Commit 18945af

Browse files
sobolevnikonst
andauthored
Suppress second error message with := and [truthy-bool] (#15941)
Closes #15685 CC @ikonst Co-authored-by: Ilya Priven <[email protected]>
1 parent 39b9b89 commit 18945af

File tree

3 files changed

+28
-7
lines changed

3 files changed

+28
-7
lines changed

mypy/checker.py

+21-5
Original file line numberDiff line numberDiff line change
@@ -5762,7 +5762,9 @@ def combine_maps(list_maps: list[TypeMap]) -> TypeMap:
57625762
else_map = {}
57635763
return if_map, else_map
57645764

5765-
def find_isinstance_check(self, node: Expression) -> tuple[TypeMap, TypeMap]:
5765+
def find_isinstance_check(
5766+
self, node: Expression, *, in_boolean_context: bool = True
5767+
) -> tuple[TypeMap, TypeMap]:
57665768
"""Find any isinstance checks (within a chain of ands). Includes
57675769
implicit and explicit checks for None and calls to callable.
57685770
Also includes TypeGuard and TypeIs functions.
@@ -5773,15 +5775,24 @@ def find_isinstance_check(self, node: Expression) -> tuple[TypeMap, TypeMap]:
57735775
If either of the values in the tuple is None, then that particular
57745776
branch can never occur.
57755777
5778+
If `in_boolean_context=True` is passed, it means that we handle
5779+
a walrus expression. We treat rhs values
5780+
in expressions like `(a := A())` specially:
5781+
for example, some errors are suppressed.
5782+
57765783
May return {}, {}.
57775784
Can return None, None in situations involving NoReturn.
57785785
"""
5779-
if_map, else_map = self.find_isinstance_check_helper(node)
5786+
if_map, else_map = self.find_isinstance_check_helper(
5787+
node, in_boolean_context=in_boolean_context
5788+
)
57805789
new_if_map = self.propagate_up_typemap_info(if_map)
57815790
new_else_map = self.propagate_up_typemap_info(else_map)
57825791
return new_if_map, new_else_map
57835792

5784-
def find_isinstance_check_helper(self, node: Expression) -> tuple[TypeMap, TypeMap]:
5793+
def find_isinstance_check_helper(
5794+
self, node: Expression, *, in_boolean_context: bool = True
5795+
) -> tuple[TypeMap, TypeMap]:
57855796
if is_true_literal(node):
57865797
return {}, None
57875798
if is_false_literal(node):
@@ -6050,7 +6061,9 @@ def has_no_custom_eq_checks(t: Type) -> bool:
60506061
if else_assignment_map is not None:
60516062
else_map.update(else_assignment_map)
60526063

6053-
if_condition_map, else_condition_map = self.find_isinstance_check(node.value)
6064+
if_condition_map, else_condition_map = self.find_isinstance_check(
6065+
node.value, in_boolean_context=False
6066+
)
60546067

60556068
if if_condition_map is not None:
60566069
if_map.update(if_condition_map)
@@ -6112,7 +6125,10 @@ def has_no_custom_eq_checks(t: Type) -> bool:
61126125
# Restrict the type of the variable to True-ish/False-ish in the if and else branches
61136126
# respectively
61146127
original_vartype = self.lookup_type(node)
6115-
self._check_for_truthy_type(original_vartype, node)
6128+
if in_boolean_context:
6129+
# We don't check `:=` values in expressions like `(a := A())`,
6130+
# because they produce two error messages.
6131+
self._check_for_truthy_type(original_vartype, node)
61166132
vartype = try_expanding_sum_type_to_union(original_vartype, "builtins.bool")
61176133

61186134
if_type = true_only(vartype)

test-data/unit/check-errorcodes.test

+6
Original file line numberDiff line numberDiff line change
@@ -892,6 +892,12 @@ if a:
892892
any_or_object: Union[object, Any]
893893
if any_or_object:
894894
pass
895+
896+
if (my_foo := Foo()): # E: "__main__.my_foo" has type "Foo" which does not implement __bool__ or __len__ so it could always be true in boolean context [truthy-bool]
897+
pass
898+
899+
if my_a := (a or Foo()): # E: "__main__.Foo" returns "Foo" which does not implement __bool__ or __len__ so it could always be true in boolean context [truthy-bool]
900+
pass
895901
[builtins fixtures/list.pyi]
896902

897903
[case testTruthyFunctions]

test-data/unit/check-python38.test

+1-2
Original file line numberDiff line numberDiff line change
@@ -297,8 +297,7 @@ def f(x: int = (c := 4)) -> int:
297297
z2: NT # E: Variable "NT" is not valid as a type \
298298
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
299299

300-
if Alias := int: # E: Function "Alias" could always be true in boolean context \
301-
# E: Function "int" could always be true in boolean context
300+
if Alias := int: # E: Function "Alias" could always be true in boolean context
302301
z3: Alias # E: Variable "Alias" is not valid as a type \
303302
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
304303

0 commit comments

Comments
 (0)