Skip to content

Make mypy --warn-no-return clean #2332

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 6 commits into from
Nov 1, 2016
Merged
Show file tree
Hide file tree
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
23 changes: 20 additions & 3 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ def accept(self, node: Union[Expression, Statement, FuncItem],
return typ

def accept_loop(self, body: Statement, else_body: Statement = None, *,
exit_condition: Expression = None) -> Type:
exit_condition: Expression = None) -> None:
"""Repeatedly type check a loop body until the frame doesn't change.
If exit_condition is set, assume it must be False on exit from the loop.

Expand Down Expand Up @@ -298,6 +298,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 @@ -494,10 +495,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 @@ -994,6 +996,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 @@ -1051,11 +1054,13 @@ 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:
def check_import(self, node: ImportBase) -> None:
for assign in node.assignments:
lvalue = assign.lvalues[0]
lvalue_type, _, __ = self.check_lvalue(lvalue)
Expand All @@ -1079,6 +1084,7 @@ def visit_block(self, b: Block) -> Type:
if self.binder.is_unreachable():
break
self.accept(s)
return None

def visit_assignment_stmt(self, s: AssignmentStmt) -> Type:
"""Type check an assignment statement.
Expand All @@ -1095,6 +1101,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: Lvalue, rvalue: Expression, infer_lvalue_type: bool = True,
new_syntax: bool = False) -> None:
Expand Down Expand Up @@ -1512,11 +1519,13 @@ 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."""
self.check_return_stmt(s)
self.binder.unreachable()
return None

def check_return_stmt(self, s: ReturnStmt) -> None:
if self.is_within_function():
Expand Down Expand Up @@ -1586,6 +1595,7 @@ 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,
exit_condition=s.expr)
return None

def visit_operator_assignment_stmt(self,
s: OperatorAssignmentStmt) -> Type:
Expand All @@ -1600,6 +1610,7 @@ 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)
Expand All @@ -1608,6 +1619,7 @@ def visit_assert_stmt(self, s: AssertStmt) -> Type:
true_map, _ = self.find_isinstance_check(s.expr)

self.push_type_map(true_map)
return None

def visit_raise_stmt(self, s: RaiseStmt) -> Type:
"""Type check a raise statement."""
Expand All @@ -1616,6 +1628,7 @@ def visit_raise_stmt(self, s: RaiseStmt) -> Type:
if s.from_expr:
self.type_check_raise(s.from_expr, s, True)
self.binder.unreachable()
return None

def type_check_raise(self, e: Expression, s: RaiseStmt,
optional: bool = False) -> None:
Expand Down Expand Up @@ -1765,6 +1778,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_async_iterable_item_type(self, expr: Expression) -> Type:
"""Analyse async iterable expression and return iterator item type."""
Expand Down Expand Up @@ -1878,6 +1892,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 @@ -1898,6 +1913,7 @@ def visit_with_stmt(self, s: WithStmt) -> Type:
else:
self.check_with_item(expr, target)
self.accept(s.body)
return None

def check_async_with_item(self, expr: Expression, target: Expression) -> None:
echk = self.expr_checker
Expand Down Expand Up @@ -1933,6 +1949,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 @@ -1616,6 +1616,7 @@ def analyze_super(self, e: SuperExpr, is_lvalue: bool) -> Type:
not_ready_callback=self.not_ready_callback,
msg=self.msg, override_info=base, chk=self.chk,
original_type=declared_self)
assert False, 'unreachable'
else:
# Invalid super. This has been reported by the semantic analyzer.
return AnyType()
Expand Down
39 changes: 20 additions & 19 deletions mypy/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@
from mypy import experiments


class ParseError(Exception): pass


precedence = {
'**': 16,
'-u': 15, '+u': 15, '~': 15, # unary operators (-, + and ~)
Expand Down Expand Up @@ -236,7 +239,7 @@ def parse_import_from(self) -> ImportBase:
node = None # type: ImportBase
if self.current_str() == '*':
if name == '__future__':
self.parse_error()
raise self.parse_error()
# An import all from a module node:
self.skip()
node = ImportAll(name, relative)
Expand Down Expand Up @@ -402,8 +405,7 @@ def is_no_type_check_decorator(self, expr: Expression) -> bool:
elif isinstance(expr, MemberExpr):
if isinstance(expr.expr, NameExpr):
return expr.expr.name == 'typing' and expr.name == 'no_type_check'
else:
return False
return False

def parse_function(self, no_type_checks: bool = False) -> FuncDef:
def_tok = self.expect('def')
Expand Down Expand Up @@ -628,7 +630,7 @@ def parse_arg_list(self, allow_signature: bool = True,
elif self.current_str() in ['*', '**']:
if bare_asterisk_before == len(args):
# named arguments must follow bare *
self.parse_error()
raise self.parse_error()

arg = self.parse_asterisk_arg(
allow_signature,
Expand Down Expand Up @@ -790,6 +792,8 @@ def parse_parameter_annotation(self) -> Expression:
if self.current_str() == ':':
self.skip()
return self.parse_expression(precedence[','])
else:
return None

def parse_arg_type(self, allow_signature: bool) -> Type:
if self.current_str() == ':' and allow_signature:
Expand Down Expand Up @@ -987,7 +991,7 @@ def parse_return_stmt(self) -> ReturnStmt:
expr = None
current = self.current()
if current.string == 'yield':
self.parse_error()
raise self.parse_error()
if not isinstance(current, Break):
expr = self.parse_expression()
node = ReturnStmt(expr)
Expand Down Expand Up @@ -1243,10 +1247,10 @@ def parse_print_stmt(self) -> PrintStmt:
if self.current_str() == ',':
self.skip()
if isinstance(self.current(), Break):
self.parse_error()
raise self.parse_error()
else:
if not isinstance(self.current(), Break):
self.parse_error()
raise self.parse_error()
comma = False
while not isinstance(self.current(), Break):
args.append(self.parse_expression(precedence[',']))
Expand Down Expand Up @@ -1321,7 +1325,7 @@ def parse_expression(self, prec: int = 0, star_expr_allowed: bool = False) -> Ex
expr = self.parse_ellipsis()
else:
# Invalid expression.
self.parse_error()
raise self.parse_error()

# Set the line of the expression node, if not specified. This
# simplifies recording the line number as not every node type needs to
Expand Down Expand Up @@ -1496,7 +1500,7 @@ def parse_dict_or_set_expr(self) -> Union[SetComprehension, SetExpr,
elif self.current_str() == 'for' and items == []:
return self.parse_set_comprehension(key)
elif self.current_str() != ':':
self.parse_error()
raise self.parse_error()
colon = self.expect(':')
value = self.parse_expression(precedence['<for>'])
if self.current_str() == 'for' and items == []:
Expand Down Expand Up @@ -1666,7 +1670,7 @@ def parse_arg_expr(self) -> Tuple[List[Expression], List[int], List[str]]:
kinds.append(nodes.ARG_POS)
names.append(None)
else:
self.parse_error()
raise self.parse_error()
args.append(self.parse_expression(precedence[',']))
if self.current_str() != ',':
break
Expand Down Expand Up @@ -1734,7 +1738,7 @@ def parse_bin_op_expr(self, left: Expression, prec: int) -> OpExpr:
op_str = op.string
if op_str == '~':
self.ind -= 1
self.parse_error()
raise self.parse_error()
right = self.parse_expression(prec)
node = OpExpr(op_str, left, right)
return node
Expand All @@ -1751,7 +1755,7 @@ def parse_comparison_expr(self, left: Expression, prec: int) -> ComparisonExpr:
op_str = 'not in'
self.skip()
else:
self.parse_error()
raise self.parse_error()
elif op_str == 'is' and self.current_str() == 'not':
op_str = 'is not'
self.skip()
Expand Down Expand Up @@ -1818,7 +1822,7 @@ def expect(self, string: str) -> Token:
self.ind += 1
return self.tok[self.ind - 1]
else:
self.parse_error()
raise self.parse_error()

def expect_indent(self) -> Token:
if isinstance(self.current(), Indent):
Expand All @@ -1836,7 +1840,7 @@ def expect_type(self, typ: type) -> Token:
self.ind += 1
return current
else:
self.parse_error()
raise self.parse_error()

def expect_break(self) -> Token:
return self.expect_type(Break)
Expand All @@ -1850,9 +1854,9 @@ def current_str(self) -> str:
def peek(self) -> Token:
return self.tok[self.ind + 1]

def parse_error(self) -> None:
def parse_error(self) -> ParseError:
self.parse_error_at(self.current())
raise ParseError()
return ParseError()

def parse_error_at(self, tok: Token, skip: bool = True, reason: Optional[str] = None) -> None:
msg = ''
Expand Down Expand Up @@ -1936,9 +1940,6 @@ def parse_type_comment(self, token: Token, signature: bool) -> Type:
return None


class ParseError(Exception): pass


def token_repr(tok: Token) -> str:
"""Return a representation of a token for use in parse error messages."""
if isinstance(tok, Break):
Expand Down
10 changes: 5 additions & 5 deletions mypy/parsetype.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def parse_type(self) -> Type:
raise TypeParseError(e.token, self.ind)
return result
else:
self.parse_error()
raise self.parse_error()

def parse_parens(self) -> Type:
self.expect('(')
Expand Down Expand Up @@ -166,23 +166,23 @@ def expect(self, string: str) -> Token:
self.ind += 1
return self.tok[self.ind - 1]
else:
self.parse_error()
raise self.parse_error()

def expect_type(self, typ: type) -> Token:
if isinstance(self.current_token(), typ):
self.ind += 1
return self.tok[self.ind - 1]
else:
self.parse_error()
raise self.parse_error()

def current_token(self) -> Token:
return self.tok[self.ind]

def current_token_str(self) -> str:
return self.current_token().string

def parse_error(self) -> None:
raise TypeParseError(self.tok[self.ind], self.ind)
def parse_error(self) -> TypeParseError:
return TypeParseError(self.tok[self.ind], self.ind)


def parse_str_as_type(typestr: str, line: int) -> Type:
Expand Down
2 changes: 2 additions & 0 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -2094,6 +2094,8 @@ def is_valid_del_target(self, s: Expression) -> 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
5 changes: 3 additions & 2 deletions runtests.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,9 @@ def add_basic(driver: Driver) -> None:


def add_selftypecheck(driver: Driver) -> None:
driver.add_mypy_package('package mypy', 'mypy')
driver.add_mypy_package('package mypy', 'mypy', '--config-file', 'mypy_strict_optional.ini')
driver.add_mypy_package('package mypy', 'mypy', '--fast-parser', '--warn-no-return')
driver.add_mypy_package('package mypy', 'mypy', '--fast-parser',
'--config-file', 'mypy_strict_optional.ini')


def find_files(base: str, prefix: str = '', suffix: str = '') -> List[str]:
Expand Down