diff --git a/mypy/checker.py b/mypy/checker.py index d56414dcb693..522fb1c818e0 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -6726,37 +6726,37 @@ def narrow_type_by_identity_equality( else_map = {} # this is the big difference compared to the above partial_type_maps.append((if_map, else_map)) - exprs_in_type_calls = [] for i in expr_indices: - expr = operands[i] - if isinstance(expr, CallExpr) and is_type_call(expr): - exprs_in_type_calls.append(expr.args[0]) - - if exprs_in_type_calls: - for expr_in_type_call in exprs_in_type_calls: - for i in expr_indices: - expr = operands[i] - if isinstance(expr, CallExpr) and is_type_call(expr): - continue - - current_type_range = self.get_isinstance_type(expr) - if_map, else_map = conditional_types_to_typemaps( - expr_in_type_call, - *self.conditional_types_with_intersection( - self.lookup_type(expr_in_type_call), - current_type_range, - expr_in_type_call, - ), - ) + type_expr = operands[i] + if ( + isinstance(type_expr, CallExpr) + and refers_to_fullname(type_expr.callee, "builtins.type") + and len(type_expr.args) == 1 + ): + expr_in_type_expr = type_expr.args[0] + else: + continue + for j in expr_indices: + if i == j: + continue + expr = operands[j] + + current_type_range = self.get_isinstance_type(expr) + if_map, else_map = conditional_types_to_typemaps( + expr_in_type_expr, + *self.conditional_types_with_intersection( + self.lookup_type(expr_in_type_expr), current_type_range, expr_in_type_expr + ), + ) - is_final = ( - expr.node.is_final - if isinstance(expr, RefExpr) and isinstance(expr.node, TypeInfo) - else False - ) - if not is_final: - else_map = {} - partial_type_maps.append((if_map, else_map)) + is_final = ( + expr.node.is_final + if isinstance(expr, RefExpr) and isinstance(expr.node, TypeInfo) + else False + ) + if not is_final: + else_map = {} + partial_type_maps.append((if_map, else_map)) # We will not have duplicate entries in our type maps if we only have two operands, # so we can skip running meets on the intersections @@ -8562,11 +8562,6 @@ def has_custom_eq_checks(t: Type) -> bool: ) -def is_type_call(expr: CallExpr) -> bool: - """Is expr a call to type with one argument?""" - return refers_to_fullname(expr.callee, "builtins.type") and len(expr.args) == 1 - - def convert_to_typetype(type_map: TypeMap) -> TypeMap: converted_type_map: dict[Expression, Type] = {} if type_map is None: diff --git a/test-data/unit/check-narrowing.test b/test-data/unit/check-narrowing.test index 9a784e72726b..fb14efea6663 100644 --- a/test-data/unit/check-narrowing.test +++ b/test-data/unit/check-narrowing.test @@ -3021,6 +3021,32 @@ if type(x) is not int: else: reveal_type(x) # N: Revealed type is "builtins.int" +[case testTypeNarrowingAgainstType] +# flags: --strict-equality --warn-unreachable +class A: + def foo(self, x: object) -> None: + reveal_type(self) # N: Revealed type is "__main__.A" + reveal_type(x) # N: Revealed type is "builtins.object" + if type(self) is type(x): + reveal_type(self) # N: Revealed type is "__main__.A" + reveal_type(x) # N: Revealed type is "__main__.A" + else: + reveal_type(self) # N: Revealed type is "__main__.A" + reveal_type(x) # N: Revealed type is "builtins.object" + if type(self) == type(x): + reveal_type(self) # N: Revealed type is "__main__.A" + reveal_type(x) # N: Revealed type is "__main__.A" + else: + reveal_type(self) # N: Revealed type is "__main__.A" + reveal_type(x) # N: Revealed type is "builtins.object" + +class B: + y: int + + def __eq__(self, other: object) -> bool: + return type(other) is type(self) and other.y == self.y +[builtins fixtures/primitives.pyi] + [case testNarrowInElseCaseIfFinal] # flags: --strict-equality --warn-unreachable from typing import final, Union