Skip to content

refactor: add contextmanager to VariableRenameVisitor #10685

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 21, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 52 additions & 58 deletions mypy/renaming.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import Dict, List
from contextlib import contextmanager
from typing import Dict, Iterator, List
from typing_extensions import Final

from mypy.nodes import (
Expand Down Expand Up @@ -74,61 +75,47 @@ def visit_mypy_file(self, file_node: MypyFile) -> None:
This is the main entry point to this class.
"""
self.clear()
self.enter_scope(FILE)
self.enter_block()

for d in file_node.defs:
d.accept(self)

self.leave_block()
self.leave_scope()
with self.enter_scope(FILE), self.enter_block():
for d in file_node.defs:
d.accept(self)

def visit_func_def(self, fdef: FuncDef) -> None:
# Conservatively do not allow variable defined before a function to
# be redefined later, since function could refer to either definition.
self.reject_redefinition_of_vars_in_scope()

self.enter_scope(FUNCTION)
self.enter_block()

for arg in fdef.arguments:
name = arg.variable.name
# 'self' can't be redefined since it's special as it allows definition of
# attributes. 'cls' can't be used to define attributes so we can ignore it.
can_be_redefined = name != 'self' # TODO: Proper check
self.record_assignment(arg.variable.name, can_be_redefined)
self.handle_arg(name)
with self.enter_scope(FUNCTION), self.enter_block():
for arg in fdef.arguments:
name = arg.variable.name
# 'self' can't be redefined since it's special as it allows definition of
# attributes. 'cls' can't be used to define attributes so we can ignore it.
can_be_redefined = name != 'self' # TODO: Proper check
self.record_assignment(arg.variable.name, can_be_redefined)
self.handle_arg(name)

for stmt in fdef.body.body:
stmt.accept(self)

self.leave_block()
self.leave_scope()
for stmt in fdef.body.body:
stmt.accept(self)

def visit_class_def(self, cdef: ClassDef) -> None:
self.reject_redefinition_of_vars_in_scope()
self.enter_scope(CLASS)
super().visit_class_def(cdef)
self.leave_scope()
with self.enter_scope(CLASS):
super().visit_class_def(cdef)

def visit_block(self, block: Block) -> None:
self.enter_block()
super().visit_block(block)
self.leave_block()
with self.enter_block():
super().visit_block(block)

def visit_while_stmt(self, stmt: WhileStmt) -> None:
self.enter_loop()
super().visit_while_stmt(stmt)
self.leave_loop()
with self.enter_loop():
super().visit_while_stmt(stmt)

def visit_for_stmt(self, stmt: ForStmt) -> None:
stmt.expr.accept(self)
self.analyze_lvalue(stmt.index, True)
# Also analyze as non-lvalue so that every for loop index variable is assumed to be read.
stmt.index.accept(self)
self.enter_loop()
stmt.body.accept(self)
self.leave_loop()
with self.enter_loop():
stmt.body.accept(self)
if stmt.else_body:
stmt.else_body.accept(self)

Expand All @@ -142,9 +129,8 @@ def visit_try_stmt(self, stmt: TryStmt) -> None:
# Variables defined by a try statement get special treatment in the
# type checker which allows them to be always redefined, so no need to
# do renaming here.
self.enter_try()
super().visit_try_stmt(stmt)
self.leave_try()
with self.enter_try():
super().visit_try_stmt(stmt)

def visit_with_stmt(self, stmt: WithStmt) -> None:
for expr in stmt.expr:
Expand Down Expand Up @@ -275,40 +261,48 @@ def clear(self) -> None:
self.blocks = []
self.var_blocks = []

def enter_block(self) -> None:
@contextmanager
def enter_block(self) -> Iterator[None]:
self.block_id += 1
self.blocks.append(self.block_id)
self.block_loop_depth[self.block_id] = self.loop_depth
try:
yield
finally:
self.blocks.pop()

def leave_block(self) -> None:
self.blocks.pop()

def enter_try(self) -> None:
@contextmanager
def enter_try(self) -> Iterator[None]:
self.disallow_redef_depth += 1
try:
yield
finally:
self.disallow_redef_depth -= 1

def leave_try(self) -> None:
self.disallow_redef_depth -= 1

def enter_loop(self) -> None:
@contextmanager
def enter_loop(self) -> Iterator[None]:
self.loop_depth += 1

def leave_loop(self) -> None:
self.loop_depth -= 1
try:
yield
finally:
self.loop_depth -= 1

def current_block(self) -> int:
return self.blocks[-1]

def enter_scope(self, kind: int) -> None:
@contextmanager
def enter_scope(self, kind: int) -> Iterator[None]:
self.var_blocks.append({})
self.refs.append({})
self.num_reads.append({})
self.scope_kinds.append(kind)

def leave_scope(self) -> None:
self.flush_refs()
self.var_blocks.pop()
self.num_reads.pop()
self.scope_kinds.pop()
try:
yield
finally:
self.flush_refs()
self.var_blocks.pop()
self.num_reads.pop()
self.scope_kinds.pop()

def is_nested(self) -> int:
return len(self.var_blocks) > 1
Expand Down