Skip to content

Commit 2195915

Browse files
authored
Initial work towards new semantic analyzer (#6240)
Implement basic deferred nodes functionality and remove most of old pass 1 functionality. Also fork `mypy.semanal_shared` since I needed to tweak the signature of anal_type in an incompatible way (there are not other changes in that module). A lot of things still don't work, including functions and more than two passes. There are a small number of tests for the new semantic analyzer, but I made no attempt at achieving good test coverage yet. Work towards #6204.
1 parent 18e9687 commit 2195915

16 files changed

+604
-468
lines changed

mypy/build.py

Lines changed: 61 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,11 @@
3232

3333
from mypy.nodes import (MypyFile, ImportBase, Import, ImportFrom, ImportAll)
3434
from mypy.semanal_pass1 import SemanticAnalyzerPass1
35+
from mypy.newsemanal.semanal_pass1 import ReachabilityAnalyzer
3536
from mypy.semanal import SemanticAnalyzerPass2, apply_semantic_analyzer_patches
3637
from mypy.semanal_pass3 import SemanticAnalyzerPass3
38+
from mypy.newsemanal.semanal import NewSemanticAnalyzer
39+
from mypy.newsemanal.semanal_main import semantic_analysis_for_scc
3740
from mypy.checker import TypeChecker
3841
from mypy.indirection import TypeIndirectionVisitor
3942
from mypy.errors import Errors, CompileError, report_internal_error
@@ -502,10 +505,21 @@ def __init__(self, data_dir: str,
502505
self.modules = {} # type: Dict[str, MypyFile]
503506
self.missing_modules = set() # type: Set[str]
504507
self.plugin = plugin
505-
self.semantic_analyzer = SemanticAnalyzerPass2(self.modules, self.missing_modules,
506-
self.errors, self.plugin)
507-
self.semantic_analyzer_pass3 = SemanticAnalyzerPass3(self.modules, self.errors,
508-
self.semantic_analyzer)
508+
if options.new_semantic_analyzer:
509+
# Set of namespaces (module or class) that are being populated during semantic
510+
# analysis and may have missing definitions.
511+
self.incomplete_namespaces = set() # type: Set[str]
512+
self.new_semantic_analyzer = NewSemanticAnalyzer(
513+
self.modules,
514+
self.missing_modules,
515+
self.incomplete_namespaces,
516+
self.errors,
517+
self.plugin)
518+
else:
519+
self.semantic_analyzer = SemanticAnalyzerPass2(self.modules, self.missing_modules,
520+
self.errors, self.plugin)
521+
self.semantic_analyzer_pass3 = SemanticAnalyzerPass3(self.modules, self.errors,
522+
self.semantic_analyzer)
509523
self.all_types = {} # type: Dict[Expression, Type] # Enabled by export_types
510524
self.indirection_detector = TypeIndirectionVisitor()
511525
self.stale_modules = set() # type: Set[str]
@@ -1721,21 +1735,44 @@ def parse_file(self) -> None:
17211735

17221736
modules[self.id] = self.tree
17231737

1724-
# Do the first pass of semantic analysis: add top-level
1725-
# definitions in the file to the symbol table. We must do
1726-
# this before processing imports, since this may mark some
1727-
# import statements as unreachable.
1728-
first = SemanticAnalyzerPass1(manager.semantic_analyzer)
1729-
with self.wrap_context():
1730-
first.visit_file(self.tree, self.xpath, self.id, self.options)
1738+
self.semantic_analysis_pass1()
17311739

1732-
# Initialize module symbol table, which was populated by the
1733-
# semantic analyzer.
1734-
# TODO: Why can't SemanticAnalyzerPass1 .analyze() do this?
1735-
self.tree.names = manager.semantic_analyzer.globals
1740+
if not self.options.new_semantic_analyzer:
1741+
# Initialize module symbol table, which was populated by the
1742+
# semantic analyzer.
1743+
# TODO: Why can't SemanticAnalyzerPass1 .analyze() do this?
1744+
self.tree.names = manager.semantic_analyzer.globals
17361745

17371746
self.check_blockers()
17381747

1748+
def semantic_analysis_pass1(self) -> None:
1749+
"""Perform pass 1 of semantic analysis, which happens immediately after parsing.
1750+
1751+
This pass can't assume that any other modules have been processed yet.
1752+
"""
1753+
options = self.options
1754+
assert self.tree is not None
1755+
if options.new_semantic_analyzer:
1756+
# Do the first pass of semantic analysis: analyze the reachability
1757+
# of blocks and import statements. We must do this before
1758+
# processing imports, since this may mark some import statements as
1759+
# unreachable.
1760+
#
1761+
# TODO: Once we remove the old semantic analyzer, this no longer should
1762+
# be considered as a semantic analysis pass -- it's an independent
1763+
# pass.
1764+
analyzer = ReachabilityAnalyzer()
1765+
with self.wrap_context():
1766+
analyzer.visit_file(self.tree, self.xpath, self.id, options)
1767+
else:
1768+
# Do the first pass of semantic analysis: add top-level
1769+
# definitions in the file to the symbol table. We must do
1770+
# this before processing imports, since this may mark some
1771+
# import statements as unreachable.
1772+
first = SemanticAnalyzerPass1(self.manager.semantic_analyzer)
1773+
with self.wrap_context():
1774+
first.visit_file(self.tree, self.xpath, self.id, options)
1775+
17391776
def compute_dependencies(self) -> None:
17401777
"""Compute a module's dependencies after parsing it.
17411778
@@ -2649,12 +2686,15 @@ def process_stale_scc(graph: Graph, scc: List[str], manager: BuildManager) -> No
26492686
typing_mod = graph['typing'].tree
26502687
assert typing_mod, "The typing module was not parsed"
26512688
manager.semantic_analyzer.add_builtin_aliases(typing_mod)
2652-
for id in stale:
2653-
graph[id].semantic_analysis()
2654-
for id in stale:
2655-
graph[id].semantic_analysis_pass_three()
2656-
for id in stale:
2657-
graph[id].semantic_analysis_apply_patches()
2689+
if manager.options.new_semantic_analyzer:
2690+
semantic_analysis_for_scc(graph, scc)
2691+
else:
2692+
for id in stale:
2693+
graph[id].semantic_analysis()
2694+
for id in stale:
2695+
graph[id].semantic_analysis_pass_three()
2696+
for id in stale:
2697+
graph[id].semantic_analysis_apply_patches()
26582698
for id in stale:
26592699
graph[id].type_check_first_pass()
26602700
more = True

mypy/main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,8 @@ def add_invertible_flag(flag: str,
582582
"the contents of SHADOW_FILE instead.")
583583
add_invertible_flag('--fast-exit', default=False, help=argparse.SUPPRESS,
584584
group=internals_group)
585+
add_invertible_flag('--new-semantic-analyzer', default=False, help=argparse.SUPPRESS,
586+
group=internals_group)
585587

586588
error_group = parser.add_argument_group(
587589
title='Error reporting',

0 commit comments

Comments
 (0)