From 648680875bcfcbbc81847335d6d1e3e0f8e00d8c Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 10 Oct 2017 14:32:31 +0100 Subject: [PATCH] Rename semantic analyzer classes to SemanticAnalyzerPass[123] (#4093) Work towards #4083. --- mypy/build.py | 13 +++++++------ mypy/checkexpr.py | 2 +- mypy/nodes.py | 4 ++-- mypy/semanal.py | 42 ++++++++++++++++++++++-------------------- 4 files changed, 32 insertions(+), 29 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index 47b5e38b02aa..8a94f5eee90a 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -31,7 +31,7 @@ from typing import Deque from mypy.nodes import (MypyFile, Node, ImportBase, Import, ImportFrom, ImportAll) -from mypy.semanal import FirstPass, SemanticAnalyzer, ThirdPass +from mypy.semanal import SemanticAnalyzerPass1, SemanticAnalyzerPass2, SemanticAnalyzerPass3 from mypy.checker import TypeChecker from mypy.indirection import TypeIndirectionVisitor from mypy.errors import Errors, CompileError, DecodeError, report_internal_error @@ -497,10 +497,11 @@ def __init__(self, data_dir: str, self.modules = {} # type: Dict[str, MypyFile] self.missing_modules = set() # type: Set[str] self.plugin = plugin - self.semantic_analyzer = SemanticAnalyzer(self.modules, self.missing_modules, + self.semantic_analyzer = SemanticAnalyzerPass2(self.modules, self.missing_modules, lib_path, self.errors, self.plugin) self.modules = self.semantic_analyzer.modules - self.semantic_analyzer_pass3 = ThirdPass(self.modules, self.errors, self.semantic_analyzer) + self.semantic_analyzer_pass3 = SemanticAnalyzerPass3(self.modules, self.errors, + self.semantic_analyzer) self.all_types = {} # type: Dict[Expression, Type] self.indirection_detector = TypeIndirectionVisitor() self.stale_modules = set() # type: Set[str] @@ -1605,7 +1606,7 @@ def patch_dependency_parents(self) -> None: """ In Python, if a and a.b are both modules, running `import a.b` will modify not only the current module's namespace, but a's namespace as - well -- see SemanticAnalyzer.add_submodules_to_parent_modules for more + well -- see SemanticAnalyzerPass2.add_submodules_to_parent_modules for more details. However, this patching process can occur after `a` has been parsed and @@ -1689,13 +1690,13 @@ def parse_file(self) -> None: # definitions in the file to the symbol table. We must do # this before processing imports, since this may mark some # import statements as unreachable. - first = FirstPass(manager.semantic_analyzer) + first = SemanticAnalyzerPass1(manager.semantic_analyzer) with self.wrap_context(): first.visit_file(self.tree, self.xpath, self.id, self.options) # Initialize module symbol table, which was populated by the # semantic analyzer. - # TODO: Why can't FirstPass .analyze() do this? + # TODO: Why can't SemanticAnalyzerPass1 .analyze() do this? self.tree.names = manager.semantic_analyzer.globals # Compute (direct) dependencies. diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 0d61e555b4ce..9098891dd559 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -538,7 +538,7 @@ def check_call(self, callee: Type, args: List[Expression], if (isinstance(callable_node, RefExpr) and callable_node.fullname in ('enum.Enum', 'enum.IntEnum', 'enum.Flag', 'enum.IntFlag')): - # An Enum() call that failed SemanticAnalyzer.check_enum_call(). + # An Enum() call that failed SemanticAnalyzerPass2.check_enum_call(). return callee.ret_type, callee if (callee.is_type_obj() and callee.type_object().is_abstract diff --git a/mypy/nodes.py b/mypy/nodes.py index b1dd7482c3dc..761f93620de1 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -255,7 +255,7 @@ def deserialize(cls, data: JsonDict) -> 'MypyFile': class ImportBase(Statement): """Base class for all import statements.""" - is_unreachable = False # Set by semanal.FirstPass if inside `if False` etc. + is_unreachable = False # Set by semanal.SemanticAnalyzerPass1 if inside `if False` etc. is_top_level = False # Ditto if outside any class or def is_mypy_only = False # Ditto if inside `if TYPE_CHECKING` or `if MYPY` @@ -2433,7 +2433,7 @@ def serialize(self, fullname: str) -> JsonDict: for key, value in self.items(): # Skip __builtins__: it's a reference to the builtins # module that gets added to every module by - # SemanticAnalyzer.visit_file(), but it shouldn't be + # SemanticAnalyzerPass2.visit_file(), but it shouldn't be # accessed by users of the module. if key == '__builtins__': continue diff --git a/mypy/semanal.py b/mypy/semanal.py index 37a7194c3741..7cc0ac6716b8 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -10,26 +10,27 @@ defines a new variable, the type of which is to be inferred (in a later pass; type inference or type checking is not part of semantic analysis). Also, it would bind both references to 'x' to the same -module-level variable node. The second assignment would also be -analyzed, and the type of 'y' marked as being inferred. +module-level variable (Var) node. The second assignment would also +be analyzed, and the type of 'y' marked as being inferred. Semantic analysis is the first analysis pass after parsing, and it is subdivided into three passes: - * FirstPass looks up externally visible names defined in a module but - ignores imports and local definitions. It helps enable (some) - cyclic references between modules, such as module 'a' that imports - module 'b' and used names defined in b *and* vice versa. The first - pass can be performed before dependent modules have been processed. + * SemanticAnalyzerPass1 looks up externally visible names defined in a + module but ignores imports and local definitions. It helps enable + (some) cyclic references between modules, such as module 'a' that + imports module 'b' and used names defined in b *and* vice versa. The + first pass can be performed before dependent modules have been + processed. - * SemanticAnalyzer is the second pass. It does the bulk of the work. + * SemanticAnalyzerPass2 is the second pass. It does the bulk of the work. It assumes that dependent modules have been semantically analyzed, up to the second pass, unless there is a import cycle. - * ThirdPass checks that type argument counts are valid; for example, - it will reject Dict[int]. We don't do this in the second pass, - since we infer the type argument counts of classes during this - pass, and it is possible to refer to classes defined later in a + * SemanticAnalyzerPass3 checks that type argument counts are valid; + for example, it will reject Dict[int]. We don't do this in the + second pass, since we infer the type argument counts of classes during + this pass, and it is possible to refer to classes defined later in a file, which would not have the type argument count set yet. This pass also recomputes the method resolution order of each class, in case one of its bases belongs to a module involved in an import @@ -183,7 +184,7 @@ } -class SemanticAnalyzer(NodeVisitor[None]): +class SemanticAnalyzerPass2(NodeVisitor[None]): """Semantically analyze parsed mypy files. The analyzer binds names and does various consistency checks for a @@ -1078,7 +1079,7 @@ def analyze_base_classes(self, defn: ClassDef) -> None: # Calculate the MRO. It might be incomplete at this point if # the bases of defn include classes imported from other - # modules in an import loop. We'll recompute it in ThirdPass. + # modules in an import loop. We'll recompute it in SemanticAnalyzerPass3. if not self.verify_base_classes(defn): # Give it an MRO consisting of just the class itself and object. defn.info.mro = [defn.info, self.object_type().type] @@ -3807,13 +3808,13 @@ def accept(self, node: Node) -> None: report_internal_error(err, self.errors.file, node.line, self.errors, self.options) -class FirstPass(NodeVisitor[None]): +class SemanticAnalyzerPass1(NodeVisitor[None]): """First phase of semantic analysis. See docstring of 'analyze()' below for a description of what this does. """ - def __init__(self, sem: SemanticAnalyzer) -> None: + def __init__(self, sem: SemanticAnalyzerPass2) -> None: self.sem = sem def visit_file(self, file: MypyFile, fnam: str, mod_id: str, options: Options) -> None: @@ -4076,7 +4077,7 @@ def kind_by_scope(self) -> int: assert False, "Couldn't determine scope" -class ThirdPass(TraverserVisitor): +class SemanticAnalyzerPass3(TraverserVisitor): """The third and final pass of semantic analysis. Check type argument counts and values of generic types, and perform some @@ -4084,7 +4085,7 @@ class ThirdPass(TraverserVisitor): """ def __init__(self, modules: Dict[str, MypyFile], errors: Errors, - sem: SemanticAnalyzer) -> None: + sem: SemanticAnalyzerPass2) -> None: self.modules = modules self.errors = errors self.sem = sem @@ -4845,7 +4846,8 @@ def visit_forwardref_type(self, t: ForwardRef) -> Type: """This visitor method tracks situations like this: x: A # This type is not yet known and therefore wrapped in ForwardRef, - # its content is updated in ThirdPass, now we need to unwrap this type. + # its content is updated in SemanticAnalyzerPass3, now we need to unwrap + # this type. A = NewType('A', int) """ assert t.resolved, 'Internal error: Unresolved forward reference: {}'.format( @@ -4855,7 +4857,7 @@ def visit_forwardref_type(self, t: ForwardRef) -> Type: def visit_instance(self, t: Instance, from_fallback: bool = False) -> Type: """This visitor method tracks situations like this: - x: A # When analyzing this type we will get an Instance from FirstPass. + x: A # When analyzing this type we will get an Instance from SemanticAnalyzerPass1. # Now we need to update this to actual analyzed TupleType. class A(NamedTuple): attr: str