diff --git a/mypy/binder.py b/mypy/binder.py index 23be259e82cf..55f02179cf65 100644 --- a/mypy/binder.py +++ b/mypy/binder.py @@ -8,6 +8,12 @@ from mypy.join import join_simple from mypy.sametypes import is_same_type +from mypy.nodes import IndexExpr, MemberExpr, NameExpr + + +BindableTypes = (IndexExpr, MemberExpr, NameExpr) +BindableExpression = Union[IndexExpr, MemberExpr, NameExpr] + class Frame(Dict[Key, Type]): """A Frame represents a specific point in the execution of a program. @@ -92,7 +98,7 @@ def push_frame(self) -> Frame: self.options_on_return.append([]) return f - def _push(self, key: Key, type: Type, index: int=-1) -> None: + def _put(self, key: Key, type: Type, index: int=-1) -> None: self.frames[index][key] = type def _get(self, key: Key, index: int=-1) -> Type: @@ -103,19 +109,23 @@ def _get(self, key: Key, index: int=-1) -> Type: return self.frames[i][key] return None - def push(self, node: Node, typ: Type) -> None: - if not node.literal: + def put(self, expr: Expression, typ: Type) -> None: + # TODO: replace with isinstance(expr, BindableTypes) + if not isinstance(expr, (IndexExpr, MemberExpr, NameExpr)): return - key = node.literal_hash + if not expr.literal: + return + key = expr.literal_hash if key not in self.declarations: - self.declarations[key] = self.get_declaration(node) + assert isinstance(expr, BindableTypes) + self.declarations[key] = get_declaration(expr) self._add_dependencies(key) - self._push(key, typ) + self._put(key, typ) def unreachable(self) -> None: self.frames[-1].unreachable = True - def get(self, expr: Union[Expression, Var]) -> Type: + def get(self, expr: Expression) -> Type: return self._get(expr.literal_hash) def is_unreachable(self) -> bool: @@ -163,7 +173,7 @@ def update_from_options(self, frames: List[Frame]) -> bool: for other in resulting_values[1:]: type = join_simple(self.declarations[key], type, other) if not is_same_type(type, current_value): - self._push(key, type) + self._put(key, type) changed = True self.frames[-1].unreachable = not frames @@ -189,19 +199,13 @@ def pop_frame(self, can_skip: bool, fall_through: int) -> Frame: return result - def get_declaration(self, expr: Node) -> Type: - if isinstance(expr, RefExpr) and isinstance(expr.node, Var): - type = expr.node.type - if isinstance(type, PartialType): - return None - return type - else: - return None - def assign_type(self, expr: Expression, type: Type, declared_type: Type, restrict_any: bool = False) -> None: + # TODO: replace with isinstance(expr, BindableTypes) + if not isinstance(expr, (IndexExpr, MemberExpr, NameExpr)): + return None if not expr.literal: return self.invalidate_dependencies(expr) @@ -226,16 +230,16 @@ def assign_type(self, expr: Expression, and not restrict_any): pass elif isinstance(type, AnyType): - self.push(expr, declared_type) + self.put(expr, declared_type) else: - self.push(expr, type) + self.put(expr, type) for i in self.try_frames: # XXX This should probably not copy the entire frame, but # just copy this variable into a single stored frame. self.allow_jump(i) - def invalidate_dependencies(self, expr: Expression) -> None: + def invalidate_dependencies(self, expr: BindableExpression) -> None: """Invalidate knowledge of types that include expr, but not expr itself. For example, when expr is foo.bar, invalidate foo.bar.baz. @@ -246,11 +250,11 @@ def invalidate_dependencies(self, expr: Expression) -> None: for dep in self.dependencies.get(expr.literal_hash, set()): self._cleanse_key(dep) - def most_recent_enclosing_type(self, expr: Expression, type: Type) -> Type: + def most_recent_enclosing_type(self, expr: BindableExpression, type: Type) -> Type: if isinstance(type, AnyType): - return self.get_declaration(expr) + return get_declaration(expr) key = expr.literal_hash - enclosers = ([self.get_declaration(expr)] + + enclosers = ([get_declaration(expr)] + [f[key] for f in self.frames if key in f and is_subtype(type, f[key])]) return enclosers[-1] @@ -334,3 +338,11 @@ def top_frame_context(self) -> Iterator[Frame]: assert len(self.frames) == 1 yield self.push_frame() self.pop_frame(True, 0) + + +def get_declaration(expr: BindableExpression) -> Type: + if isinstance(expr, RefExpr) and isinstance(expr.node, Var): + type = expr.node.type + if not isinstance(type, PartialType): + return type + return None diff --git a/mypy/checker.py b/mypy/checker.py index 15e9e53c9429..47cf4a3462cb 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -48,8 +48,8 @@ from mypy.visitor import StatementVisitor from mypy.join import join_types from mypy.treetransform import TransformVisitor +from mypy.binder import ConditionalTypeBinder, get_declaration from mypy.meet import is_overlapping_types -from mypy.binder import ConditionalTypeBinder from mypy.options import Options from mypy import experiments @@ -1221,10 +1221,7 @@ def check_assignment(self, lvalue: Lvalue, rvalue: Expression, infer_lvalue_type " to a variable of type '{}'".format(lvalue_type), rvalue) return if rvalue_type and infer_lvalue_type: - self.binder.assign_type(lvalue, - rvalue_type, - lvalue_type, - False) + self.binder.assign_type(lvalue, rvalue_type, lvalue_type, False) elif index_lvalue: self.check_indexed_assignment(index_lvalue, rvalue, lvalue) @@ -1927,7 +1924,6 @@ def visit_assert_stmt(self, s: AssertStmt) -> None: # If this is asserting some isinstance check, bind that type in the following code true_map, _ = self.find_isinstance_check(s.expr) - self.push_type_map(true_map) def visit_raise_stmt(self, s: RaiseStmt) -> None: @@ -2171,10 +2167,8 @@ def visit_del_stmt(self, s: DelStmt) -> None: s.expr.accept(self.expr_checker) for elt in flatten(s.expr): if isinstance(elt, NameExpr): - self.binder.assign_type(elt, - DeletedType(source=elt.name), - self.binder.get_declaration(elt), - False) + self.binder.assign_type(elt, DeletedType(source=elt.name), + get_declaration(elt), False) def visit_decorator(self, e: Decorator) -> None: for d in e.decorators: @@ -2463,7 +2457,7 @@ def push_type_map(self, type_map: Optional[Dict[Expression, Type]]) -> None: self.binder.unreachable() else: for expr, type in type_map.items(): - self.binder.push(expr, type) + self.binder.put(expr, type) # Data structure returned by find_isinstance_check representing # information learned from the truth or falsehood of a condition. The diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 29e2f0832e12..51a6f92e9ce6 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -160,18 +160,13 @@ def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type: return result def analyze_var_ref(self, var: Var, context: Context) -> Type: - if not var.type: + if var.type: + return var.type + else: if not var.is_ready and self.chk.in_checked_function(): self.chk.handle_cannot_determine_type(var.name(), context) # Implicit 'Any' type. return AnyType() - else: - # Look up local type of variable with type (inferred or explicit). - val = self.chk.binder.get(var) - if val is None: - return var.type - else: - return val def visit_call_expr(self, e: CallExpr, allow_none_return: bool = False) -> Type: """Type check a call expression.""" @@ -1954,7 +1949,7 @@ def check_for_comp(self, e: Union[GeneratorExpr, DictionaryComprehension]) -> No if true_map: for var, type in true_map.items(): - self.chk.binder.push(var, type) + self.chk.binder.put(var, type) def visit_conditional_expr(self, e: ConditionalExpr) -> Type: cond_type = self.accept(e.cond) diff --git a/mypy/nodes.py b/mypy/nodes.py index 9ff592235c51..e5a26d638518 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -116,11 +116,6 @@ class Node(Context): line = -1 column = -1 - # TODO: Move to Expression - # See [Note Literals and literal_hash] below - literal = LITERAL_NO - literal_hash = None # type: Key - def __str__(self) -> str: ans = self.accept(mypy.strconv.StrConv()) if ans is None: @@ -161,6 +156,9 @@ def accept(self, visitor: StatementVisitor[T]) -> T: class Expression(Node): """An expression node.""" + literal = LITERAL_NO + literal_hash = None # type: Key + def accept(self, visitor: ExpressionVisitor[T]) -> T: raise RuntimeError('Not implemented')