8000 Refactor semantic analysis of assignment statements (#5634) · python/mypy@4ce1c82 · GitHub
[go: up one dir, main page]

Skip to content

Commit 4ce1c82

Browse files
JukkaLilevkivskyi
authored andcommitted
Refactor semantic analysis of assignment statements (#5634)
Split a monster function into several smaller functions. Merge some almost repeated code into a helper method.
1 parent 88e49e7 commit 4ce1c82

File tree

2 files changed

+103
-85
lines changed

2 files changed

+103
-85
lines changed

mypy/semanal.py

Lines changed: 100 additions & 83 deletions
1991
Original file line numberDiff line numberDiff line change
@@ -1986,89 +1986,7 @@ def analyze_lvalue(self, lval: Lvalue, nested: bool = False,
19861986
overrides an existing name.
19871987
"""
19881988
if isinstance(lval, NameExpr):
1989-
# Top-level definitions within some statements (at least while) are
1990-
# not handled in the first pass, so they have to be added now.
1991-
nested_global = (not self.is_func_scope() and
1992-
self.block_depth[-1] > 0 and
1993-
not self.type)
1994-
if (add_global or nested_global) and lval.name not in self.globals:
1995-
# Define new global name.
1996-
v = Var(lval.name)
1997-
v.set_line(lval)
1998-
v._fullname = self.qualified_name(lval.name)
1999-
v.is_ready = False # Type not inferred yet
2000-
lval.node = v
2001-
lval.is_new_def = True
2002-
lval.is_inferred_def = True
2003-
lval.kind = GDEF
2004-
lval.fullname = v._fullname
2005-
self.globals[lval.name] = SymbolTableNode(GDEF, v)
2006-
elif isinstance(lval.node, Var) and lval.is_new_def:
2007-
if lval.kind == GDEF:
2008-
# Since the is_new_def flag is set, this must have been analyzed
2009-
# already in the first pass and added to the symbol table.
2010-
# An exception is typing module with incomplete test fixtures.
2011-
assert lval.node.name() in self.globals or self.cur_mod_id == 'typing'
2012-
elif (self.locals[-1] is not None and lval.name not in self.locals[-1] and
2013-
lval.name not in self.global_decls[-1] and
2014-
lval.name not in self.nonlocal_decls[-1]):
2015-
# Define new local name.
2016-
v = Var(lval.name)
2017-
v.set_line(lval)
2018-
lval.node = v
2019-
lval.is_new_def = True
2020-
lval.is_inferred_def = True
2021-
lval.kind = LDEF
2022-
lval.fullname = lval.name
2023-
self.add_local(v, lval)
2024-
if lval.name == '_':
2025-
# Special case for assignment to local named '_': always infer 'Any'.
2026-
typ = AnyType(TypeOfAny.special_form)
2027-
self.store_declared_types(lval, typ)
2028-
elif not self.is_func_scope() and (self.type and
2029-
lval.name not in self.type.names):
2030-
# Define a new attribute within class body.
2031-
v = Var(lval.name)
2032-
v.info = self.type
2033-
v.is_initialized_in_class = True
2034-
v.is_inferred = not explicit_type
2035-
v.set_line(lval)
2036-
v._fullname = self.qualified_name(lval.name)
2037-
lval.node = v
2038-
lval.is_new_def = True
2039-
lval.is_inferred_def = True
2040-
lval.kind = MDEF
2041-
lval.fullname = lval.name
2042-
self.type.names[lval.name] = SymbolTableNode(MDEF, v)
2043-
else:
2044-
# An existing name, try to find the original definition.
2045-
global_def = self.globals.get(lval.name)
2046-
if self.locals:
2047-
locals_last = self.locals[-1]
2048-
if locals_last:
2049-
local_def = locals_last.get(lval.name)
2050-
else:
2051-
local_def = None
2052-
else:
2053-
local_def = None
2054-
type_def = self.type.names.get(lval.name) if self.type else None
2055-
2056-
original_def = global_def or local_def or type_def
2057-
2058-
# Redefining an existing name with final is always an error.
2059-
if final_cb is not None:
2060-
# We avoid extra errors if the original definition is also final
2061-
# by keeping the final status of this assignment.
2062-
keep_final = bool(original_def and isinstance(original_def.node, Var) and
2063-
original_def.node.is_final)
2064-
final_cb(keep_final)
2065-
if explicit_type:
2066-
# Don't re-bind types
2067-
self.name_already_defined(lval.name, lval, original_def)
2068-
else:
2069-
# Bind to an existing name.
2070-
lval.accept(self)
2071-
self.check_lvalue_validity(lval.node, lval)
1989+
self.analyze_name_lvalue(lval, add_global, explicit_type, final_cb)
20721990
elif isinstance(lval, MemberExpr):
2073
if not add_global:
20741992
self.analyze_member_lvalue(lval, explicit_type, final_cb=final_cb)
@@ -2093,6 +2011,105 @@ def analyze_lvalue(self, lval: Lvalue, nested: bool = False,
20932011
else:
20942012
self.fail('Invalid assignment target', lval)
20952013

2014+
def analyze_name_lvalue(self,
2015+
lval: NameExpr,
2016+
add_global: bool,
2017+
explicit_type: bool,
2018+
final_cb: Optional[Callable[[bool], None]]) -> None:
2019+
"""Analyze an lvalue that targets a name expression.
2020+
2021+
Arguments are similar to "analyze_lvalue".
2022+
"""
2023+
# Top-level definitions within some statements (at least while) are
2024+
# not handled in the first pass, so they have to be added now.
2025+
nested_global = (not self.is_func_scope() and
2026+
self.block_depth[-1] > 0 and
2027+
not self.type)
2028+
if (add_global or nested_global) and lval.name not in self.globals:
2029+
# Define new global name.
2030+
v = self.make_name_lvalue_var(lval, GDEF)
2031+
self.globals[lval.name] = SymbolTableNode(GDEF, v)
2032+
elif isinstance(lval.node, Var) and lval.is_new_def:
2033+
if lval.kind == GDEF:
2034+
# Since the is_new_def flag is set, this must have been analyzed
2035+
# already in the first pass and added to the symbol table.
2036+
# An exception is typing module with incomplete test fixtures.
2037+
assert lval.node.name() in self.globals or self.cur_mod_id == 'typing'
2038+
elif (self.locals[-1] is not None and lval.name not in self.locals[-1] and
2039+
lval.name not in self.global_decls[-1] and
2040+
lval.name not in self.nonlocal_decls[-1]):
2041+
# Define new local name.
2042+
v = self.make_name_lvalue_var(lval, LDEF)
2043+
self.add_local(v, lval)
2044+
if lval.name == '_':
2045+
# Special case for assignment to local named '_': always infer 'Any'.
2046+
typ = AnyType(TypeOfAny.special_form)
2047+
self.store_declared_types(lval, typ)
2048+
elif not self.is_func_scope() and (self.type and
2049+
lval.name not in self.type.names):
2050+
# Define a new attribute within class body.
2051+
v = self.make_name_lvalue_var(lval, MDEF)
2052+
v.is_inferred = not explicit_type
2053+
self.type.names[lval.name] = SymbolTableNode(MDEF, v)
2054+
else:
2055+
self.make_name_lvalue_point_to_existing_def(lval, explicit_type, final_cb)
2056+
2057+
def make_name_lvalue_var(self, lvalue: NameExpr, kind: int) -> Var:
2058+
"""Return a Var node for an lvalue that is a name expression."""
2059+
v = Var(lvalue.name)
2060+
v.set_line(lvalue)
2061+
if kind == MDEF:
2062+
assert self.type is not None
2063+
v.info = self.type
2064+
v.is_initialized_in_class = True
2065+
if kind != LDEF:
2066+
v._fullname = self.qualified_name(lvalue.name)
2067+
if kind == GDEF:
2068+
v.is_ready = False # Type not inferred yet
2069+
lvalue.node = v
2070+
lvalue.is_new_def = True
2071+
lvalue.is_inferred_def = True
2072+
lvalue.kind = kind
2073+
if kind == GDEF:
2074+
lvalue.fullname = v._fullname
2075+
else:
2076+
lvalue.fullname = lvalue.name
2077+
return v
2078+
2079+
def make_name_lvalue_point_to_existing_def(
2080+
self,
2081+
lval: NameExpr,
2082+
explicit_type: bool,
2083+
final_cb: Optional[Callable[[bool], None]]) -> None:
2084+
# Assume that an existing name exists. Try to find the original definition.
2085+
global_def = self.globals.get(lval.name)
2086+
if self.locals:
2087+
locals_last = self.locals[-1]
2088+
if locals_last:
2089+
local_def = locals_last.get(lval.name)
2090+
else:
2091+
local_def = None
2092+
else:
2093+
local_def = None
2094+
type_def = self.type.names.get(lval.name) if self.type else None
2095+
2096+
original_def = global_def or local_def or type_def
2097+
2098+
# Redefining an existing name with final is always an error.
2099+
if final_cb is not None:
2100+
# We avoid extra errors if the original definition is also final
2101+
# by keeping the final status of this assignment.
2102+
keep_final = bool(original_def and isinstance(original_def.node, Var) and
2103+
original_def.node.is_final)
2104+
final_cb(keep_final)
2105+
if explicit_type:
2106+
# Don't re-bind types
2107+
self.name_already_defined(lval.name, lval, original_def)
2108+
else:
2109+
# Bind to an existing name.
2110+
lval.accept(self)
2111+
self.check_lvalue_validity(lval.node, lval)
2112+
20962113
def analyze_tuple_or_list_lvalue(self, lval: TupleExpr,
20972114
add_global: bool = False,
20982115
explicit_type: bool = False) -> None:

mypy/semanal_pass1.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
This sets up externally visible names defined in a module but doesn't
44
follow imports and mostly ignores local definitions. It helps enable
55
(some) cyclic references between modules, such as module 'a' that
6-
imports module 'b' and used names defined in b *and* vice versa. The
6+
imports module 'b' and used names defined in 'b' *and* vice versa. The
77
first pass can be performed before dependent modules have been
88
processed.
99
@@ -37,7 +37,8 @@
3737
class SemanticAnalyzerPass1(NodeVisitor[None]):
3838
"""First phase of semantic analysis.
3939
40-
See docstring of 'analyze()' below for a description of what this does.
40+
See docstring of 'visit_file()' below and the module docstring for a
41+
description of what this does.
4142
"""
4243

4344
def __init__(self, sem: SemanticAnalyzerPass2) -> None:

0 commit comments

Comments
 (0)
0