Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suppress second error message with := and [truthy-bool] #15941

Merged
merged 9 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading