Skip to content

Commit

Permalink
Suppress second error message with := and [truthy-bool] (#15941)
Browse files Browse the repository at this point in the history
Closes #15685
CC @ikonst

Co-authored-by: Ilya Priven <[email protected]>
  • Loading branch information
sobolevn and ikonst authored Jun 24, 2024
1 parent 39b9b89 commit 18945af
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 7 deletions.
26 changes: 21 additions & 5 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -5762,7 +5762,9 @@ def combine_maps(list_maps: list[TypeMap]) -> TypeMap:
else_map = {}
return if_map, else_map

def find_isinstance_check(self, node: Expression) -> tuple[TypeMap, TypeMap]:
def find_isinstance_check(
self, node: Expression, *, in_boolean_context: bool = True
) -> tuple[TypeMap, TypeMap]:
"""Find any isinstance checks (within a chain of ands). Includes
implicit and explicit checks for None and calls to callable.
Also includes TypeGuard and TypeIs functions.
Expand All @@ -5773,15 +5775,24 @@ def find_isinstance_check(self, node: Expression) -> tuple[TypeMap, TypeMap]:
If either of the values in the tuple is None, then that particular
branch can never occur.
If `in_boolean_context=True` is passed, it means that we handle
a walrus expression. We treat rhs values
in expressions like `(a := A())` specially:
for example, some errors are suppressed.
May return {}, {}.
Can return None, None in situations involving NoReturn.
"""
if_map, else_map = self.find_isinstance_check_helper(node)
if_map, else_map = self.find_isinstance_check_helper(
node, in_boolean_context=in_boolean_context
)
new_if_map = self.propagate_up_typemap_info(if_map)
new_else_map = self.propagate_up_typemap_info(else_map)
return new_if_map, new_else_map

def find_isinstance_check_helper(self, node: Expression) -> tuple[TypeMap, TypeMap]:
def find_isinstance_check_helper(
self, node: Expression, *, in_boolean_context: bool = True
) -> tuple[TypeMap, TypeMap]:
if is_true_literal(node):
return {}, None
if is_false_literal(node):
Expand Down Expand Up @@ -6050,7 +6061,9 @@ def has_no_custom_eq_checks(t: Type) -> bool:
if else_assignment_map is not None:
else_map.update(else_assignment_map)

if_condition_map, else_condition_map = self.find_isinstance_check(node.value)
if_condition_map, else_condition_map = self.find_isinstance_check(
node.value, in_boolean_context=False
)

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

if_type = true_only(vartype)
Expand Down
6 changes: 6 additions & 0 deletions test-data/unit/check-errorcodes.test
Original file line number Diff line number Diff line change
Expand Up @@ -892,6 +892,12 @@ if a:
any_or_object: Union[object, Any]
if any_or_object:
pass

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]
pass

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]
pass
[builtins fixtures/list.pyi]

[case testTruthyFunctions]
Expand Down
3 changes: 1 addition & 2 deletions test-data/unit/check-python38.test
Original file line number Diff line number Diff line change
Expand Up @@ -297,8 +297,7 @@ def f(x: int = (c := 4)) -> int:
z2: NT # E: Variable "NT" is not valid as a type \
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases

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

Expand Down

0 comments on commit 18945af

Please sign in to comment.