diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 44855f49afaf9..699bd73bfc550 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -3770,6 +3770,7 @@ def visit_comparison_expr(self, e: ComparisonExpr) -> Type: pass elif ( container_errors.has_new_errors() + and not has_operator(item_type, "__contains__") and # is_valid_var_arg is True for any Iterable self.is_valid_var_arg(item_type) @@ -3800,21 +3801,17 @@ def visit_comparison_expr(self, e: ComparisonExpr) -> Type: failed_out = True if not encountered_partial_type and not failed_out: - iterable_type = UnionType.make_union(iterable_types) - if not is_subtype(left_type, iterable_type): - if not container_types: - self.msg.unsupported_operand_types("in", left_type, right_type, e) - else: - container_type = UnionType.make_union(container_types) - if not self.chk.can_skip_diagnostics and self.dangerous_comparison( - left_type, - container_type, - original_container=right_type, - prefer_literal=False, - ): - self.msg.dangerous_comparison( - left_type, container_type, "container", e - ) + all_item_types = container_types + iterable_types + if all_item_types: + combined_type = UnionType.make_union(all_item_types) + if not self.chk.can_skip_diagnostics and self.dangerous_comparison( + left_type, + combined_type, + original_container=right_type, + prefer_literal=False, + ): + kind = "container" if container_types else "iterable" + self.msg.dangerous_comparison(left_type, combined_type, kind, e) elif operator in operators.op_methods: method = operators.op_methods[operator] diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index 7123285e5eca1..b8dcd63973039 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -395,7 +395,7 @@ if int(): if int(): c = a in b # E: Unsupported right operand type for in ("B") if int(): - c = b in d # E: Unsupported operand types for in ("B" and "D") + c = b in d if int(): c = b in a if int(): @@ -2814,3 +2814,18 @@ class A: A().f(fobar=1) # E: Unexpected keyword argument "fobar" for overloaded function "f" of "A"; did you mean "foobar"? [builtins fixtures/list.pyi] +[case testInOperatorGeneratorOverlap_issue21601] +from typing import Generator +class Signals(int): pass +def f(gen: Generator[Signals, None, None]) -> None: + # This shouldn't be an error since Signals and int overlap. + 1 in gen + # This shouldn't be an error since float and int overlap. + 1.5 in gen + +[case testInOperatorIterableUnion_issue21595] +from typing import Union, List +def f(var: Union[List[int], str]) -> None: + # 2 in str throws TypeError at runtime, so mypy MUST catch this + # and not silence it just because list[int] accepts it. + 2 in var # E: Unsupported right operand type for in ("list[int] | str")