Skip to content

Commit 1eaf4c7

Browse files
authored
Fix crash on missing indirect dependencies (#13847)
Fixes #13825 We add also types encountered in locally defined symbols, not just expressions. I also added base-classes/metaclass etc for `TypeInfo`s, as they also cause crashes.
1 parent 2f08e40 commit 1eaf4c7

File tree

2 files changed

+224
-6
lines changed

2 files changed

+224
-6
lines changed

mypy/build.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
from mypy.errors import CompileError, ErrorInfo, Errors, report_internal_error
4949
from mypy.indirection import TypeIndirectionVisitor
5050
from mypy.messages import MessageBuilder
51-
from mypy.nodes import Import, ImportAll, ImportBase, ImportFrom, MypyFile, SymbolTable
51+
from mypy.nodes import Import, ImportAll, ImportBase, ImportFrom, MypyFile, SymbolTable, TypeInfo
5252
from mypy.partially_defined import PartiallyDefinedVariableVisitor
5353
from mypy.semanal import SemanticAnalyzer
5454
from mypy.semanal_pass1 import SemanticAnalyzerPreAnalysis
@@ -2363,7 +2363,24 @@ def finish_passes(self) -> None:
23632363

23642364
# We should always patch indirect dependencies, even in full (non-incremental) builds,
23652365
# because the cache still may be written, and it must be correct.
2366-
self._patch_indirect_dependencies(self.type_checker().module_refs, self.type_map())
2366+
# TODO: find a more robust way to traverse *all* relevant types?
2367+
expr_types = set(self.type_map().values())
2368+
symbol_types = set()
2369+
for _, sym, _ in self.tree.local_definitions():
2370+
if sym.type is not None:
2371+
symbol_types.add(sym.type)
2372+
if isinstance(sym.node, TypeInfo):
2373+
# TypeInfo symbols have some extra relevant types.
2374+
symbol_types.update(sym.node.bases)
2375+
if sym.node.metaclass_type:
2376+
symbol_types.add(sym.node.metaclass_type)
2377+
if sym.node.typeddict_type:
2378+
symbol_types.add(sym.node.typeddict_type)
2379+
if sym.node.tuple_type:
2380+
symbol_types.add(sym.node.tuple_type)
2381+
self._patch_indirect_dependencies(
2382+
self.type_checker().module_refs, expr_types | symbol_types
2383+
)
23672384

23682385
if self.options.dump_inference_stats:
23692386
dump_type_stats(
@@ -2386,10 +2403,7 @@ def free_state(self) -> None:
23862403
self._type_checker.reset()
23872404
self._type_checker = None
23882405

2389-
def _patch_indirect_dependencies(
2390-
self, module_refs: set[str], type_map: dict[Expression, Type]
2391-
) -> None:
2392-
types = set(type_map.values())
2406+
def _patch_indirect_dependencies(self, module_refs: set[str], types: set[Type]) -> None:
23932407
assert None not in types
23942408
valid = self.valid_references()
23952409

test-data/unit/check-incremental.test

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6082,3 +6082,207 @@ class Base:
60826082
[out]
60836083
[out2]
60846084
main:6: error: Call to abstract method "meth" of "Base" with trivial body via super() is unsafe
6085+
6086+
[case testNoCrashDoubleReexportFunctionEmpty]
6087+
import m
6088+
6089+
[file m.py]
6090+
import f
6091+
[file m.py.3]
6092+
import f
6093+
# modify
6094+
6095+
[file f.py]
6096+
import c
6097+
def foo(arg: c.C) -> None: pass
6098+
6099+
[file c.py]
6100+
from types import C
6101+
6102+
[file types.py]
6103+
import pb1
6104+
C = pb1.C
6105+
[file types.py.2]
6106+
import pb1, pb2
6107+
C = pb2.C
6108+
6109+
[file pb1.py]
6110+
class C: ...
6111+
[file pb2.py.2]
6112+
class C: ...
6113+
[file pb1.py.2]
6114+
[out]
6115+
[out2]
6116+
[out3]
6117+
6118+
[case testNoCrashDoubleReexportBaseEmpty]
6119+
import m
6120+
6121+
[file m.py]
6122+
import f
6123+
[file m.py.3]
6124+
import f
6125+
# modify
6126+
6127+
[file f.py]
6128+
import c
6129+
class D(c.C): pass
6130+
6131+
[file c.py]
6132+
from types import C
6133+
6134+
[file types.py]
6135+
import pb1
6136+
C = pb1.C
6137+
[file types.py.2]
6138+
import pb1, pb2
6139+
C = pb2.C
6140+
6141+
[file pb1.py]
6142+
class C: ...
6143+
[file pb2.py.2]
6144+
class C: ...
6145+
[file pb1.py.2]
6146+
[out]
6147+
[out2]
6148+
[out3]
6149+
6150+
[case testNoCrashDoubleReexportMetaEmpty]
6151+
import m
6152+
6153+
[file m.py]
6154+
import f
6155+
[file m.py.3]
6156+
import f
6157+
# modify
6158+
6159+
[file f.py]
6160+
import c
6161+
class D(metaclass=c.C): pass
6162+
6163+
[file c.py]
6164+
from types import C
6165+
6166+
[file types.py]
6167+
import pb1
6168+
C = pb1.C
6169+
[file types.py.2]
6170+
import pb1, pb2
6171+
C = pb2.C
6172+
6173+
[file pb1.py]
6174+
class C(type): ...
6175+
[file pb2.py.2]
6176+
class C(type): ...
6177+
[file pb1.py.2]
6178+
[out]
6179+
[out2]
6180+
[out3]
6181+
6182+
[case testNoCrashDoubleReexportTypedDictEmpty]
6183+
import m
6184+
6185+
[file m.py]
6186+
import f
6187+
[file m.py.3]
6188+
import f
6189+
# modify
6190+
6191+
[file f.py]
6192+
from typing_extensions import TypedDict
6193+
import c
6194+
class D(TypedDict):
6195+
x: c.C
6196+
6197+
[file c.py]
6198+
from types import C
6199+
6200+
[file types.py]
6201+
import pb1
6202+
C = pb1.C
6203+
[file types.py.2]
6204+
import pb1, pb2
6205+
C = pb2.C
6206+
6207+
[file pb1.py]
6208+
class C: ...
6209+
[file pb2.py.2]
6210+
class C: ...
6211+
[file pb1.py.2]
6212+
[builtins fixtures/dict.pyi]
6213+
[out]
6214+
[out2]
6215+
[out3]
6216+
6217+
[case testNoCrashDoubleReexportTupleEmpty]
6218+
import m
6219+
6220+
[file m.py]
6221+
import f
6222+
[file m.py.3]
6223+
import f
6224+
# modify
6225+
6226+
[file f.py]
6227+
from typing import Tuple
6228+
import c
6229+
class D(Tuple[c.C, int]): pass
6230+
6231+
[file c.py]
6232+
from types import C
6233+
6234+
[file types.py]
6235+
import pb1
6236+
C = pb1.C
6237+
[file types.py.2]
6238+
import pb1, pb2
6239+
C = pb2.C
6240+
6241+
[file pb1.py]
6242+
class C: ...
6243+
[file pb2.py.2]
6244+
class C: ...
6245+
[file pb1.py.2]
6246+
[builtins fixtures/tuple.pyi]
6247+
[out]
6248+
[out2]
6249+
[out3]
6250+
6251+
[case testNoCrashDoubleReexportOverloadEmpty]
6252+
import m
6253+
6254+
[file m.py]
6255+
import f
6256+
[file m.py.3]
6257+
import f
6258+
# modify
6259+
6260+
[file f.py]
6261+
from typing import Any, overload
6262+
import c
6263+
6264+
@overload
6265+
def foo(arg: int) -> None: ...
6266+
@overload
6267+
def foo(arg: c.C) -> None: ...
6268+
def foo(arg: Any) -> None:
6269+
pass
6270+
6271+
[file c.py]
6272+
from types import C
6273+
6274+
[file types.py]
6275+
import pb1
6276+
C = pb1.C
6277+
[file types.py.2]
6278+
import pb1, pb2
6279+
C = pb2.C
6280+
6281+
[file pb1.py]
6282+
class C: ...
6283+
[file pb2.py.2]
6284+
class C: ...
6285+
[file pb1.py.2]
6286+
[out]
6287+
[out2]
6288+
[out3]

0 commit comments

Comments
 (0)