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

Always infer in operator as returning bool #5688

Merged
merged 4 commits into from Feb 1, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 7 additions & 6 deletions mypy/checkexpr.py
Expand Up @@ -1686,6 +1686,8 @@ def visit_ellipsis(self, e: EllipsisExpr) -> Type:

def visit_op_expr(self, e: OpExpr) -> Type:
"""Type check a binary operator expression."""
if e.op == 'in':
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean that we won't check type errors within the subexpressions of the in expressions?

Concretely, does mypy still report an error on code like 1 in ([1] + ['x'])?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example you gave still produces an error, but just to be on the safe side, I've added some extra type checking, and a test to make sure this is still an error.

return self.bool_type()
if e.op == 'and' or e.op == 'or':
return self.check_boolean_op(e, e)
if e.op == '*' and isinstance(e.left, ListExpr):
Expand Down Expand Up @@ -1718,7 +1720,8 @@ def visit_comparison_expr(self, e: ComparisonExpr) -> Type:
Comparison expressions are type checked consecutive-pair-wise
That is, 'a < b > c == d' is check as 'a < b and b > c and c == d'
"""
result = None # type: Optional[Type]
result = None # type: Optional[Type]
sub_result = None # type: Optional[Type]

# Check each consecutive operand pair and their operator
for left, right, operator in zip(e.operands, e.operands[1:], e.operators):
Expand All @@ -1733,8 +1736,9 @@ def visit_comparison_expr(self, e: ComparisonExpr) -> Type:
# are just to verify whether something is valid typing wise).
local_errors = self.msg.copy()
local_errors.disable_count = 0
sub_result, method_type = self.check_op_local_by_name('__contains__', right_type,
left, e, local_errors)
_, method_type = self.check_op_local_by_name('__contains__', right_type,
left, e, local_errors)
sub_result = self.bool_type()
if isinstance(right_type, PartialType):
# We don't really know if this is an error or not, so just shut up.
pass
Expand All @@ -1748,13 +1752,10 @@ def visit_comparison_expr(self, e: ComparisonExpr) -> Type:
[None],
self.bool_type(),
self.named_type('builtins.function'))
sub_result = self.bool_type()
if not is_subtype(left_type, itertype):
self.msg.unsupported_operand_types('in', left_type, right_type, e)
else:
self.msg.add_errors(local_errors)
if operator == 'not in':
sub_result = self.bool_type()
elif operator in nodes.op_methods:
method = self.get_operator_method(operator)
sub_result, method_type = self.check_op(method, left_type, right, e,
Expand Down
2 changes: 1 addition & 1 deletion test-data/unit/check-dynamic-typing.test
Expand Up @@ -122,7 +122,7 @@ n = 0

a and d
a or d
c = a in d
c = a in d # E: Incompatible types in assignment (expression has type "bool", variable has type "C")
c = b and d # E: Incompatible types in assignment (expression has type "Union[bool, Any]", variable has type "C")
c = b or d # E: Incompatible types in assignment (expression has type "Union[bool, Any]", variable has type "C")
b = a + d
Expand Down
9 changes: 6 additions & 3 deletions test-data/unit/check-expressions.test
Expand Up @@ -406,15 +406,18 @@ main:6: error: Unsupported operand types for in ("B" and "D")

[case testNonBooleanContainsReturnValue]

a, b = None, None # type: (A, bool)
a, b, c = None, None, None # type: (A, bool, int)
b = a not in a
b = a in a
c = a not in a # Fail
c = a in a # Fail

class A:
def __contains__(self, x: 'A') -> object: pass
def __contains__(self, x: 'A') -> int: pass
[builtins fixtures/bool.pyi]
[out]
main:4: error: Incompatible types in assignment (expression has type "object", variable has type "bool")
main:5: error: Incompatible types in assignment (expression has type "bool", variable has type "int")
main:6: error: Incompatible types in assignment (expression has type "bool", variable has type "int")

[case testEq]

Expand Down
3 changes: 2 additions & 1 deletion test-data/unit/deps-expressions.test
Expand Up @@ -303,8 +303,9 @@ class A:
def __contains__(self, x: B) -> int: pass
class B: pass

def f() -> int:
def f() -> bool:
return B() in A()
[builtins fixtures/bool.pyi]
[out]
<m.A.__contains__> -> m.f
<m.A.__init__> -> m.f
Expand Down