Skip to content

Commit e061802

Browse files
authored
Fix an incremental crash bug caused by ad-hoc intersection (#8381)
Ad-hoc intersection was generating type names with dots in them, which are interpreted as module separators. I fixed this by making the names worse and just using the bare name of the class. There are other options that could be pursued: * Replacing "." with some other character and then either switching it back when displayed or just printing it. One option that I was very tempted by was to use U+2024 ONE DOT LEADER. * Compute the intersection names on the fly when formatting. I decided to do the most obvious and the least tricky thing because this isn't important.
1 parent 8888b1a commit e061802

File tree

4 files changed

+54
-8
lines changed

4 files changed

+54
-8
lines changed

mypy/checker.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3689,18 +3689,20 @@ def intersect_instances(self,
36893689
assert isinstance(curr_module, MypyFile)
36903690

36913691
base_classes = []
3692-
formatted_names = []
36933692
for inst in instances:
36943693
expanded = [inst]
36953694
if inst.type.is_intersection:
36963695
expanded = inst.type.bases
36973696

36983697
for expanded_inst in expanded:
36993698
base_classes.append(expanded_inst)
3700-
formatted_names.append(format_type_bare(expanded_inst))
37013699

3700+
# We use the pretty_names_list for error messages but can't
3701+
# use it for the real name that goes into the symbol table
3702+
# because it can have dots in it.
37023703
pretty_names_list = pretty_seq(format_type_distinctly(*base_classes, bare=True), "and")
3703-
short_name = '<subclass of {}>'.format(pretty_names_list)
3704+
names_list = pretty_seq([x.type.name for x in base_classes], "and")
3705+
short_name = '<subclass of {}>'.format(names_list)
37043706
full_name = gen_unique_name(short_name, curr_module.names)
37053707

37063708
old_msg = self.msg

test-data/unit/check-incremental.test

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5187,6 +5187,51 @@ reveal_type(Foo().x)
51875187
[out2]
51885188
tmp/b.py:2: note: Revealed type is 'a.<subclass of "A" and "B">'
51895189

5190+
[case testIsInstanceAdHocIntersectionIncrementalNoChangeSameName]
5191+
import b
5192+
[file c.py]
5193+
class B: pass
5194+
[file a.py]
5195+
import c
5196+
class B: pass
5197+
5198+
class Foo:
5199+
def __init__(self) -> None:
5200+
x: c.B
5201+
assert isinstance(x, B)
5202+
self.x = x
5203+
[file b.py]
5204+
from a import Foo
5205+
[file b.py.2]
5206+
from a import Foo
5207+
reveal_type(Foo().x)
5208+
[builtins fixtures/isinstance.pyi]
5209+
[out]
5210+
[out2]
5211+
tmp/b.py:2: note: Revealed type is 'a.<subclass of "B" and "B">'
5212+
5213+
5214+
[case testIsInstanceAdHocIntersectionIncrementalNoChangeTuple]
5215+
import b
5216+
[file a.py]
5217+
from typing import Tuple
5218+
class B: pass
5219+
5220+
class Foo:
5221+
def __init__(self) -> None:
5222+
x: Tuple[int, ...]
5223+
assert isinstance(x, B)
5224+
self.x = x
5225+
[file b.py]
5226+
from a import Foo
5227+
[file b.py.2]
5228+
from a import Foo
5229+
reveal_type(Foo().x)
5230+
[builtins fixtures/isinstance.pyi]
5231+
[out]
5232+
[out2]
5233+
tmp/b.py:2: note: Revealed type is 'a.<subclass of "tuple" and "B">'
5234+
51905235
[case testIsInstanceAdHocIntersectionIncrementalIsInstanceChange]
51915236
import c
51925237
[file a.py]
@@ -5316,4 +5361,3 @@ reveal_type(z)
53165361
tmp/c.py:2: note: Revealed type is 'a.A'
53175362
[out2]
53185363
tmp/c.py:2: note: Revealed type is 'a.<subclass of "A" and "B">'
5319-

test-data/unit/check-isinstance.test

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2369,14 +2369,14 @@ else:
23692369

23702370
y: A[Parent]
23712371
if isinstance(y, B):
2372-
reveal_type(y) # N: Revealed type is '__main__.<subclass of "A[Parent]" and "B">'
2372+
reveal_type(y) # N: Revealed type is '__main__.<subclass of "A" and "B">'
23732373
reveal_type(y.f()) # N: Revealed type is '__main__.Parent*'
23742374
else:
23752375
reveal_type(y) # N: Revealed type is '__main__.A[__main__.Parent]'
23762376

23772377
z: A[Child]
23782378
if isinstance(z, B):
2379-
reveal_type(z) # N: Revealed type is '__main__.<subclass of "A[Child]" and "B">'
2379+
reveal_type(z) # N: Revealed type is '__main__.<subclass of "A" and "B">1'
23802380
reveal_type(z.f()) # N: Revealed type is '__main__.Child*'
23812381
else:
23822382
reveal_type(z) # N: Revealed type is '__main__.A[__main__.Child]'
@@ -2518,7 +2518,7 @@ class A: pass
25182518

25192519
x: A
25202520
if isinstance(x, A2):
2521-
reveal_type(x) # N: Revealed type is '__main__.<subclass of "__main__.A" and "foo.A">'
2521+
reveal_type(x) # N: Revealed type is '__main__.<subclass of "A" and "A">'
25222522

25232523
[file foo.py]
25242524
class A: pass

test-data/unit/check-protocols.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1564,7 +1564,7 @@ if isinstance(c1i, P1):
15641564
else:
15651565
reveal_type(c1i) # Unreachable
15661566
if isinstance(c1i, P):
1567-
reveal_type(c1i) # N: Revealed type is '__main__.<subclass of "C1[int]" and "P">'
1567+
reveal_type(c1i) # N: Revealed type is '__main__.<subclass of "C1" and "P">'
15681568
else:
15691569
reveal_type(c1i) # N: Revealed type is '__main__.C1[builtins.int]'
15701570

0 commit comments

Comments
 (0)