Skip to content

Commit

Permalink
Fix type information for functions that previously fell off the end.
Browse files Browse the repository at this point in the history
See python#1229.  In most cases, `return None`; in some others, `assert
False` in codepaths that don't occur, but that type information is
insufficient to prove it.

The only thing that doesn't work is self.parse_error().
  • Loading branch information
ecprice committed Jun 23, 2016
1 parent 969740e commit 842faab
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 14 deletions.
27 changes: 25 additions & 2 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ def accept_loop(self, body: Node, else_body: Node = None) -> Type:
self.binder.pop_loop_frame()
if else_body:
self.accept(else_body)
return None

#
# Definitions
Expand All @@ -272,6 +273,7 @@ def visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> Type:
self.check_method_override(defn)
self.check_inplace_operator_method(defn)
self.check_overlapping_overloads(defn)
return None

def check_overlapping_overloads(self, defn: OverloadedFuncDef) -> None:
for i, item in enumerate(defn.items):
Expand Down Expand Up @@ -379,10 +381,11 @@ def visit_func_def(self, defn: FuncDef) -> Type:
messages.INCOMPATIBLE_REDEFINITION,
'redefinition with type',
'original type')
return None

def check_func_item(self, defn: FuncItem,
type_override: CallableType = None,
name: str = None) -> Type:
name: str = None) -> None:
"""Type check a function.
If type_override is provided, use it as the function type.
Expand Down Expand Up @@ -840,6 +843,7 @@ def visit_class_def(self, defn: ClassDef) -> Type:
self.check_multiple_inheritance(typ)
self.leave_partial_types()
self.errors.pop_type()
return None

def check_multiple_inheritance(self, typ: TypeInfo) -> None:
"""Check for multiple inheritance related errors."""
Expand Down Expand Up @@ -905,9 +909,11 @@ def check_compatibility(self, name: str, base1: TypeInfo,

def visit_import_from(self, node: ImportFrom) -> Type:
self.check_import(node)
return None

def visit_import_all(self, node: ImportAll) -> Type:
self.check_import(node)
return None

def check_import(self, node: ImportBase) -> Type:
for assign in node.assignments:
Expand All @@ -921,6 +927,7 @@ def check_import(self, node: ImportBase) -> Type:
self.check_simple_assignment(lvalue_type, assign.rvalue, node,
msg=message, lvalue_name='local name',
rvalue_name='imported name')
return None

#
# Statements
Expand All @@ -933,6 +940,7 @@ def visit_block(self, b: Block) -> Type:
self.accept(s)
if self.binder.breaking_out:
break
return None

def visit_assignment_stmt(self, s: AssignmentStmt) -> Type:
"""Type check an assignment statement.
Expand All @@ -949,6 +957,7 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> Type:
rvalue = self.temp_node(self.type_map[s.rvalue], s)
for lv in s.lvalues[:-1]:
self.check_assignment(lv, rvalue, s.type is None)
return None

def check_assignment(self, lvalue: Node, rvalue: Node, infer_lvalue_type: bool = True) -> None:
"""Type check a single assignment: lvalue = rvalue."""
Expand Down Expand Up @@ -1370,6 +1379,7 @@ def try_infer_partial_type_from_indexed_assignment(

def visit_expression_stmt(self, s: ExpressionStmt) -> Type:
self.accept(s.expr)
return None

def visit_return_stmt(self, s: ReturnStmt) -> Type:
"""Type check a return statement."""
Expand Down Expand Up @@ -1414,6 +1424,7 @@ def visit_return_stmt(self, s: ReturnStmt) -> Type:

if self.typing_mode_full():
self.fail(messages.RETURN_VALUE_EXPECTED, s)
return None

def wrap_generic_type(self, typ: Instance, rtyp: Instance, check_type:
str, context: Context) -> Type:
Expand Down Expand Up @@ -1486,12 +1497,14 @@ def visit_if_stmt(self, s: IfStmt) -> Type:
if broken:
self.binder.breaking_out = True
return None
return None

def visit_while_stmt(self, s: WhileStmt) -> Type:
"""Type check a while statement."""
self.accept_loop(IfStmt([s.expr], [s.body], None), s.else_body)
if isinstance(s.expr, NameExpr) and s.expr.name == 'True':
self.binder.breaking_out = True
return None

def visit_operator_assignment_stmt(self,
s: OperatorAssignmentStmt) -> Type:
Expand All @@ -1506,10 +1519,12 @@ def visit_operator_assignment_stmt(self,
else:
if not is_subtype(rvalue_type, lvalue_type):
self.msg.incompatible_operator_assignment(s.op, s)
return None

def visit_assert_stmt(self, s: AssertStmt) -> Type:
self.accept(s.expr)
if ((isinstance(s.expr, TupleExpr) and isinstance(s.expr.items[0], NameExpr) and s.expr.items[0].name == 'False') or
if ((isinstance(s.expr, TupleExpr) and
isinstance(s.expr.items[0], NameExpr) and s.expr.items[0].name == 'False') or
(isinstance(s.expr, NameExpr) and s.expr.name == 'False')):
self.binder.breaking_out = True

Expand All @@ -1522,6 +1537,7 @@ def visit_assert_stmt(self, s: AssertStmt) -> Type:
if true_map:
for var, type in true_map.items():
self.binder.push(var, type)
return None

def visit_raise_stmt(self, s: RaiseStmt) -> Type:
"""Type check a raise statement."""
Expand All @@ -1530,6 +1546,7 @@ def visit_raise_stmt(self, s: RaiseStmt) -> Type:
self.type_check_raise(s.expr, s)
if s.from_expr:
self.type_check_raise(s.from_expr, s)
return None

def type_check_raise(self, e: Node, s: RaiseStmt) -> None:
typ = self.accept(e)
Expand All @@ -1554,6 +1571,7 @@ def type_check_raise(self, e: Node, s: RaiseStmt) -> None:
self.check_subtype(typ,
self.named_type('builtins.BaseException'), s,
messages.INVALID_EXCEPTION)
return None

def visit_try_stmt(self, s: TryStmt) -> Type:
"""Type check a try statement."""
Expand Down Expand Up @@ -1654,6 +1672,7 @@ def visit_for_stmt(self, s: ForStmt) -> Type:
item_type = self.analyze_iterable_item_type(s.expr)
self.analyze_index_variables(s.index, item_type, s)
self.accept_loop(s.body, s.else_body)
return None

def analyze_iterable_item_type(self, expr: Node) -> Type:
"""Analyse iterable expression and return iterator item type."""
Expand Down Expand Up @@ -1689,6 +1708,7 @@ def analyze_iterable_item_type(self, expr: Node) -> Type:
method = echk.analyze_external_member_access(nextmethod, iterator,
expr)
return echk.check_call(method, [], [], expr)[0]
return None

def analyze_index_variables(self, index: Node, item_type: Type,
context: Context) -> None:
Expand Down Expand Up @@ -1748,6 +1768,7 @@ def visit_decorator(self, e: Decorator) -> Type:
e.var.is_ready = True
if e.func.is_property:
self.check_incompatible_property_override(e)
return None

def check_incompatible_property_override(self, e: Decorator) -> None:
if not e.var.is_settable_property and e.func.info is not None:
Expand All @@ -1773,6 +1794,7 @@ def visit_with_stmt(self, s: WithStmt) -> Type:
arg = self.temp_node(AnyType(), expr)
echk.check_call(exit, [arg] * 3, [nodes.ARG_POS] * 3, expr)
self.accept(s.body)
return None

def visit_print_stmt(self, s: PrintStmt) -> Type:
for arg in s.args:
Expand All @@ -1782,6 +1804,7 @@ def visit_print_stmt(self, s: PrintStmt) -> Type:
if not isinstance(target_type, NoneTyp):
# TODO: Also verify the type of 'write'.
self.expr_checker.analyze_external_member_access('write', target_type, s.target)
return None

#
# Expressions
Expand Down
1 change: 1 addition & 0 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -1418,6 +1418,7 @@ def analyze_super(self, e: SuperExpr, is_lvalue: bool) -> Type:
is_lvalue, True,
self.named_type, self.not_ready_callback,
self.msg, base)
assert False
else:
# Invalid super. This has been reported by the semantic analyzer.
return AnyType()
Expand Down
1 change: 1 addition & 0 deletions mypy/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ def set_line(self, target: Union[Token, Node, int]) -> Node:
if self.initialization_statement:
self.initialization_statement.set_line(self.line)
self.initialization_statement.lvalues[0].set_line(self.line)
return self

def serialize(self) -> JsonDict:
data = {'.class': 'Argument',
Expand Down
3 changes: 3 additions & 0 deletions mypy/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,8 @@ def is_no_type_check_decorator(self, expr: Node) -> bool:
elif isinstance(expr, MemberExpr):
if isinstance(expr.expr, NameExpr):
return expr.expr.name == 'typing' and expr.name == 'no_type_check'
else:
return False
else:
return False

Expand Down Expand Up @@ -803,6 +805,7 @@ def parse_parameter_annotation(self) -> Node:
if self.current_str() == ':':
self.skip()
return self.parse_expression(precedence[','])
assert False

def parse_arg_type(self, allow_signature: bool) -> Type:
if self.current_str() == ':' and allow_signature:
Expand Down
2 changes: 2 additions & 0 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -1698,6 +1698,8 @@ def is_valid_del_target(self, s: Node) -> bool:
return True
elif isinstance(s, TupleExpr):
return all(self.is_valid_del_target(item) for item in s.items)
else:
return False

def visit_global_decl(self, g: GlobalDecl) -> None:
for name in g.names:
Expand Down
Loading

0 comments on commit 842faab

Please sign in to comment.