Skip to content

Commit

Permalink
Rename semantic analyzer classes to SemanticAnalyzerPass[123] (#4093)
Browse files Browse the repository at this point in the history
Work towards #4083.
  • Loading branch information
JukkaL authored and ilevkivskyi committed Oct 10, 2017
1 parent 3e49ef9 commit 6486808
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 29 deletions.
13 changes: 7 additions & 6 deletions mypy/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions mypy/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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`

Expand Down Expand Up @@ -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
Expand Down
42 changes: 22 additions & 20 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -4076,15 +4077,15 @@ 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
straightforward type inference.
"""

def __init__(self, modules: Dict[str, MypyFile], errors: Errors,
sem: SemanticAnalyzer) -> None:
sem: SemanticAnalyzerPass2) -> None:
self.modules = modules
self.errors = errors
self.sem = sem
Expand Down Expand Up @@ -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(
Expand All @@ -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
Expand Down

0 comments on commit 6486808

Please sign in to comment.