Skip to content

Commit 9637f99

Browse files
committed
Fix crash with assignment to variable guarded with TypeGuard (#10683)
This is a quick fix to unblock the 0.910 release. The type guard type can be unrelated to the original type, so we shouldn't join it. I'm not sure whether this is right from first principles, but it seems to address the issue. Fixes #10671.
1 parent 680fded commit 9637f99

File tree

2 files changed

+53
-2
lines changed

2 files changed

+53
-2
lines changed

mypy/binder.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from typing_extensions import DefaultDict
66

77
from mypy.types import (
8-
Type, AnyType, PartialType, UnionType, TypeOfAny, NoneType, get_proper_type
8+
Type, AnyType, PartialType, UnionType, TypeOfAny, NoneType, TypeGuardType, get_proper_type
99
)
1010
from mypy.subtypes import is_subtype
1111
from mypy.join import join_simple
@@ -202,7 +202,9 @@ def update_from_options(self, frames: List[Frame]) -> bool:
202202
else:
203203
for other in resulting_values[1:]:
204204
assert other is not None
205-
type = join_simple(self.declarations[key], type, other)
205+
# Ignore the error about using get_proper_type().
206+
if not isinstance(other, TypeGuardType): # type: ignore[misc]
207+
type = join_simple(self.declarations[key], type, other)
206208
if current_value is None or not is_same_type(type, current_value):
207209
self._put(key, type)
208210
changed = True

test-data/unit/check-typeguard.test

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ def is_str_list(a: List[object]) -> TypeGuard[List[str]]: pass
8282
def main(a: List[object]):
8383
if is_str_list(a):
8484
reveal_type(a) # N: Revealed type is "builtins.list[builtins.str]"
85+
reveal_type(a) # N: Revealed type is "builtins.list[builtins.object]"
8586
[builtins fixtures/tuple.pyi]
8687

8788
[case testTypeGuardUnionIn]
@@ -91,6 +92,7 @@ def is_foo(a: Union[int, str]) -> TypeGuard[str]: pass
9192
def main(a: Union[str, int]) -> None:
9293
if is_foo(a):
9394
reveal_type(a) # N: Revealed type is "builtins.str"
95+
reveal_type(a) # N: Revealed type is "Union[builtins.str, builtins.int]"
9496
[builtins fixtures/tuple.pyi]
9597

9698
[case testTypeGuardUnionOut]
@@ -315,3 +317,50 @@ def coverage(obj: Any) -> bool:
315317
return True
316318
return False
317319
[builtins fixtures/classmethod.pyi]
320+
321+
[case testAssignToTypeGuardedVariable1]
322+
from typing_extensions import TypeGuard
323+
324+
class A: pass
325+
class B(A): pass
326+
327+
def guard(a: A) -> TypeGuard[B]:
328+
pass
329+
330+
a = A()
331+
if not guard(a):
332+
a = A()
333+
[builtins fixtures/tuple.pyi]
334+
335+
[case testAssignToTypeGuardedVariable2]
336+
from typing_extensions import TypeGuard
337+
338+
class A: pass
339+
class B: pass
340+
341+
def guard(a: A) -> TypeGuard[B]:
342+
pass
343+
344+
a = A()
345+
if not guard(a):
346+
a = A()
347+
[builtins fixtures/tuple.pyi]
348+
349+
[case testAssignToTypeGuardedVariable3]
350+
from typing_extensions import TypeGuard
351+
352+
class A: pass
353+
class B: pass
354+
355+
def guard(a: A) -> TypeGuard[B]:
356+
pass
357+
358+
a = A()
359+
if guard(a):
360+
reveal_type(a) # N: Revealed type is "__main__.B"
361+
a = B() # E: Incompatible types in assignment (expression has type "B", variable has type "A")
362+
reveal_type(a) # N: Revealed type is "__main__.B"
363+
a = A()
364+
reveal_type(a) # N: Revealed type is "__main__.A"
365+
reveal_type(a) # N: Revealed type is "__main__.A"
366+
[builtins fixtures/tuple.pyi]

0 commit comments

Comments
 (0)