Skip to content

Commit d7e3268

Browse files
authored
Allow dangerous identity comparisons to Any typed variables (#21142)
Fixes #21134
1 parent 232697e commit d7e3268

File tree

2 files changed

+61
-2
lines changed

2 files changed

+61
-2
lines changed

mypy/checkexpr.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3800,8 +3800,22 @@ def visit_comparison_expr(self, e: ComparisonExpr) -> Type:
38003800
elif operator == "is" or operator == "is not":
38013801
right_type = self.accept(right) # validate the right operand
38023802
sub_result = self.bool_type()
3803-
if not self.chk.can_skip_diagnostics and self.dangerous_comparison(
3804-
left_type, right_type, identity_check=True
3803+
if (
3804+
not self.chk.can_skip_diagnostics
3805+
and self.dangerous_comparison(left_type, right_type, identity_check=True)
3806+
# Allow dangerous identity comparisons with objects explicitly typed as Any
3807+
and not (
3808+
isinstance(left, NameExpr)
3809+
and isinstance(left.node, Var)
3810+
and not left.node.is_inferred
3811+
and isinstance(get_proper_type(left.node.type), AnyType)
3812+
)
3813+
and not (
3814+
isinstance(right, NameExpr)
3815+
and isinstance(right.node, Var)
3816+
and not right.node.is_inferred
3817+
and isinstance(get_proper_type(right.node.type), AnyType)
3818+
)
38053819
):
38063820
# Show the most specific literal types possible
38073821
left_type = try_getting_literal(left_type)

test-data/unit/check-expressions.test

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2380,6 +2380,51 @@ if 1 in ('x', 'y'): # E: Non-overlapping container check (element type: "int",
23802380
[builtins fixtures/tuple.pyi]
23812381
[typing fixtures/typing-full.pyi]
23822382

2383+
[case testStrictEqualityWithAnySentinel]
2384+
# flags: --strict-equality
2385+
from __future__ import annotations
2386+
from typing import Any, cast
2387+
2388+
class A: ...
2389+
class B: ...
2390+
2391+
sentinel: Any = object()
2392+
2393+
def f1(a: A = sentinel, b: B = sentinel):
2394+
if a is sentinel and b is sentinel:
2395+
raise
2396+
2397+
2398+
def f2(a: A | None = sentinel, b: B | None = sentinel):
2399+
if a is sentinel and b is sentinel:
2400+
raise
2401+
2402+
2403+
sentinel_strict = object()
2404+
2405+
def f3(a: A = sentinel_strict, b: B = sentinel_strict): # E: Incompatible default for parameter "a" (default has type "object", parameter has type "A") \
2406+
# E: Incompatible default for parameter "b" (default has type "object", parameter has type "B")
2407+
if a is sentinel_strict and b is sentinel_strict: # E: Non-overlapping identity check (left operand type: "B", right operand type: "A")
2408+
raise
2409+
2410+
2411+
def f4(a: A | None = sentinel_strict, b: B | None = sentinel_strict): # E: Incompatible default for parameter "a" (default has type "object", parameter has type "A | None") \
2412+
# E: Incompatible default for parameter "b" (default has type "object", parameter has type "B | None")
2413+
if a is sentinel_strict and b is sentinel_strict: # E: Non-overlapping identity check (left operand type: "B | None", right operand type: "A | None")
2414+
raise
2415+
2416+
2417+
sentinel_inferred = cast(Any, object())
2418+
2419+
def f5(a: A = sentinel_inferred, b: B = sentinel_inferred):
2420+
if a is sentinel_inferred and b is sentinel_inferred: # E: Non-overlapping identity check (left operand type: "B", right operand type: "A")
2421+
raise
2422+
2423+
def f6(a: A | None = sentinel_inferred, b: B | None = sentinel_inferred):
2424+
if a is sentinel_inferred and b is sentinel_inferred: # E: Non-overlapping identity check (left operand type: "B | None", right operand type: "A | None")
2425+
raise
2426+
[builtins fixtures/bool.pyi]
2427+
23832428
[case testOverlappingAnyTypeWithoutStrictOptional]
23842429
# flags: --no-strict-optional --strict-equality
23852430
from typing import Any, Optional

0 commit comments

Comments
 (0)