Repository: python/mypy

Issue:
Crash with star imports and new file in the build
This test case:
```
[case testAddedSubStar]
# cmd: mypy -m a pack pack.mod b
# cmd2: mypy -m other
[file a.py]
from pack import *
[file pack/__init__.py]
[file pack/mod.py]
[file b.py]
import pack.mod
[file other.py]
import a
[out]
[out2]
```
crashes with
```
/Users/ilevkivskyi/src/mypy/mypy/build.py:157: in build
    result = _build(sources, options, alt_lib_path, flush_errors, fscache)
/Users/ilevkivskyi/src/mypy/mypy/build.py:207: in _build
    graph = dispatch(sources, manager)
/Users/ilevkivskyi/src/mypy/mypy/build.py:2214: in dispatch
    process_graph(graph, manager)
/Users/ilevkivskyi/src/mypy/mypy/build.py:2505: in process_graph
    process_fresh_modules(graph, prev_scc, manager)
/Users/ilevkivskyi/src/mypy/mypy/build.py:2591: in process_fresh_modules
    graph[id].fix_cross_refs()
/Users/ilevkivskyi/src/mypy/mypy/build.py:1598: in fix_cross_refs
    self.options.use_fine_grained_cache)
/Users/ilevkivskyi/src/mypy/mypy/fixup.py:25: in fixup_module
    node_fixer.visit_symbol_table(tree.names)
/Users/ilevkivskyi/src/mypy/mypy/fixup.py:77: in visit_symbol_table
    self.quick_and_dirty)
/Users/ilevkivskyi/src/mypy/mypy/fixup.py:261: in lookup_qualified_stnode
    return lookup_fully_qualified(name, modules, raise_on_missing=not quick_and_dirty)
/Users/ilevkivskyi/src/mypy/mypy/lookup.py:47: in lookup_fully_qualified
    assert key in names, "Cannot find %s for %s" % (key, name)
AssertionError: Cannot find mod for pack.mod
```
I think this is high priority because the crash affects users who adds new files to typed builds in existing code bases. There must be another way to reproduce this where all files from first command are also present in the second, but I didn't find a simple version of it.

Anyway, the crux seems to be the interference between lack of dependencies from star import (that can also import modules), and `add_submodules_to_parent_modules()`.


Discussion:

Resolving patch:
diff --git a/mypy/build.py b/mypy/build.py
--- a/mypy/build.py
+++ b/mypy/build.py
@@ -1775,9 +1775,13 @@ def compute_dependencies(self) -> None:
     def semantic_analysis(self) -> None:
         assert self.tree is not None, "Internal error: method must be called on parsed file only"
         patches = []  # type: List[Tuple[int, Callable[[], None]]]
+        self.manager.semantic_analyzer.imports.clear()
         with self.wrap_context():
             self.manager.semantic_analyzer.visit_file(self.tree, self.xpath, self.options, patches)
         self.patches = patches
+        for dep in self.manager.semantic_analyzer.imports:
+            self.dependencies.append(dep)
+            self.priorities[dep] = PRI_LOW
 
     def semantic_analysis_pass_three(self) -> None:
         assert self.tree is not None, "Internal error: method must be called on parsed file only"
diff --git a/mypy/semanal.py b/mypy/semanal.py
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -212,6 +212,8 @@ class SemanticAnalyzerPass2(NodeVisitor[None],
     is_stub_file = False   # Are we analyzing a stub file?
     _is_typeshed_stub_file = False  # Are we analyzing a typeshed stub file?
     imports = None  # type: Set[str]  # Imported modules (during phase 2 analysis)
+    # Note: some imports (and therefore dependencies) might
+    # not be found in phase 1, for example due to * imports.
     errors = None  # type: Errors     # Keeps track of generated errors
     plugin = None  # type: Plugin     # Mypy plugin for special casing of library features
 
@@ -1630,7 +1632,10 @@ def visit_import_all(self, i: ImportAll) -> None:
                     continue
                 # if '__all__' exists, all nodes not included have had module_public set to
                 # False, and we can skip checking '_' because it's been explicitly included.
-                if (node.module_public and (not name.startswith('_') or '__all__' in m.names)):
+                if node.module_public and (not name.startswith('_') or '__all__' in m.names):
+                    if isinstance(node.node, MypyFile):
+                        # Star import of submodule from a package, add it as a dependency.
+                        self.imports.add(node.node.fullname())
                     existing_symbol = self.lookup_current_scope(name)
                     if existing_symbol:
                         # Import can redefine a variable. They get special treatment.


Test patch:
diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test
--- a/test-data/unit/check-incremental.test
+++ b/test-data/unit/check-incremental.test
@@ -4904,3 +4904,17 @@ a: Literal[2] = 2
 main:2: error: Revealed type is 'builtins.int'
 [out2]
 main:2: error: Revealed type is 'Literal[2]'
+
+[case testAddedSubStarImport]
+# cmd: mypy -m a pack pack.mod b
+# cmd2: mypy -m other
+[file a.py]
+from pack import *
+[file pack/__init__.py]
+[file pack/mod.py]
+[file b.py]
+import pack.mod
+[file other.py]
+import a
+[out]
+[out2]