diff --git a/mypy/fixup.py b/mypy/fixup.py index 5854797dfc0d..91d8dcdfb783 100644 --- a/mypy/fixup.py +++ b/mypy/fixup.py @@ -5,7 +5,7 @@ from mypy.nodes import ( MypyFile, SymbolNode, SymbolTable, SymbolTableNode, TypeInfo, FuncDef, OverloadedFuncDef, Decorator, Var, - TypeVarExpr, ClassDef, + TypeVarExpr, ClassDef, Block, LDEF, MDEF, GDEF ) from mypy.types import ( @@ -90,6 +90,11 @@ def visit_symbol_table(self, symtab: SymbolTable) -> None: value.type_override = stnode.type_override elif not self.quick_and_dirty: assert stnode is not None, "Could not find cross-ref %s" % (cross_ref,) + else: + # We have a missing crossref in quick mode, need to put something + value.node = stale_info() + if value.type_override is not None: + value.type_override.accept(self.type_fixer) else: if isinstance(value.node, TypeInfo): # TypeInfo has no accept(). TODO: Add it? @@ -162,6 +167,10 @@ def visit_instance(self, inst: Instance) -> None: for base in inst.type.bases: if base.type is NOT_READY: base.accept(self) + else: + # Looks like a missing TypeInfo in quick mode, put something there + assert self.quick_and_dirty, "Should never get here in normal mode" + inst.type = stale_info() for a in inst.args: a.accept(self) @@ -267,12 +276,23 @@ def lookup_qualified_stnode(modules: Dict[str, MypyFile], name: str, return None key = rest.pop() if key not in names: + if not quick_and_dirty: + assert key in names, "Cannot find %s for %s" % (key, name) return None - elif not quick_and_dirty: - assert key in names, "Cannot find %s for %s" % (key, name) stnode = names[key] if not rest: return stnode node = stnode.node assert isinstance(node, TypeInfo) names = node.names + + +def stale_info() -> TypeInfo: + suggestion = "" + dummy_def = ClassDef(suggestion, Block([])) + dummy_def.fullname = suggestion + + info = TypeInfo(SymbolTable(), dummy_def, "") + info.mro = [info] + info.bases = [] + return info diff --git a/mypy/nodes.py b/mypy/nodes.py index 0088808c8c58..8e0d23ffe3a9 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -2228,7 +2228,10 @@ class FakeInfo(TypeInfo): # pass cleanly. # 2. If NOT_READY value is accidentally used somewhere, it will be obvious where the value # is from, whereas a 'None' value could come from anywhere. - def __getattr__(self, attr: str) -> None: + def __init__(self, *args: Any, **kwargs: Any) -> None: + pass + + def __getattribute__(self, attr: str) -> None: raise AssertionError('De-serialization failure: TypeInfo not fixed') diff --git a/mypy/types.py b/mypy/types.py index 8de44cc69da7..c17bb063f5fb 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -401,7 +401,7 @@ class Instance(Type): def __init__(self, typ: mypy.nodes.TypeInfo, args: List[Type], line: int = -1, column: int = -1, erased: bool = False) -> None: - assert(typ is None or typ.fullname() not in ["builtins.Any", "typing.Any"]) + assert(typ is NOT_READY or typ.fullname() not in ["builtins.Any", "typing.Any"]) self.type = typ self.args = args self.erased = erased @@ -1796,8 +1796,10 @@ def union_items(typ: Type) -> List[Type]: return [typ] +names = globals().copy() +names.pop('NOT_READY', None) deserialize_map = { key: obj.deserialize # type: ignore - for key, obj in globals().items() + for key, obj in names.items() if isinstance(obj, type) and issubclass(obj, Type) and obj is not Type }