diff --git a/mypy/fixup.py b/mypy/fixup.py index 5ffc47120734..02c6ab93f29e 100644 --- a/mypy/fixup.py +++ b/mypy/fixup.py @@ -128,8 +128,23 @@ def visit_symbol_table(self, symtab: SymbolTable, table_fullname: str) -> None: cross_ref, self.modules, raise_on_missing=not self.allow_missing ) if stnode is not None: - assert stnode.node is not None, (table_fullname + "." + key, cross_ref) - value.node = stnode.node + if stnode is value: + # The node seems to refer to itself, which can mean that + # the target is a deleted submodule of the current module, + # and thus lookup falls back to the symbol table of the parent + # package. Here's how this may happen: + # + # pkg/__init__.py: + # from pkg import sub + # + # Now if pkg.sub is deleted, the pkg.sub symbol table entry + # appears to refer to itself. Replace the entry with a + # placeholder to avoid a crash. We can't delete the entry, + # as it would stop dependency propagation. + value.node = Var(key + "@deleted") + else: + assert stnode.node is not None, (table_fullname + "." + key, cross_ref) + value.node = stnode.node elif not self.allow_missing: assert False, f"Could not find cross-ref {cross_ref}" else: diff --git a/mypy/nodes.py b/mypy/nodes.py index 1d7b3e3be84b..d65a23a6b7fe 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -3824,6 +3824,8 @@ def __str__(self) -> str: # Include declared type of variables and functions. if self.type is not None: s += f" : {self.type}" + if self.cross_ref: + s += f" cross_ref:{self.cross_ref}" return s def serialize(self, prefix: str, name: str) -> JsonDict: diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 5dc42bd62d9b..165a2089b466 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -10486,3 +10486,22 @@ reveal_type(s) == == b.py:2: note: Revealed type is "builtins.str" + +[case testRenameSubModule] +import a + +[file a.py] +import pkg.sub + +[file pkg/__init__.py] +[file pkg/sub/__init__.py] +from pkg.sub import mod +[file pkg/sub/mod.py] + +[file pkg/sub/__init__.py.2] +from pkg.sub import modb +[delete pkg/sub/mod.py.2] +[file pkg/sub/modb.py.2] + +[out] +==