Skip to content

Commit

Permalink
check return statements
Browse files Browse the repository at this point in the history
  • Loading branch information
Tomer Chachamu committed Dec 2, 2017
1 parent c631809 commit 026f7e7
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 5 deletions.
2 changes: 2 additions & 0 deletions docs/intro.rst
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,8 @@ This is the current list of error and warning codes:
+------------+----------------------------------------------------------------------+
| E743 | do not define functions named 'l', 'O', or 'I' |
+------------+----------------------------------------------------------------------+
| E750 | do not mix 'return' and 'return value' in the same function |
+------------+----------------------------------------------------------------------+
+------------+----------------------------------------------------------------------+
| **E9** | *Runtime* |
+------------+----------------------------------------------------------------------+
Expand Down
60 changes: 56 additions & 4 deletions pycodestyle.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ def maximum_line_length(physical_line, max_line_length, multiline, noqa):
if ((len(chunks) == 1 and multiline) or
(len(chunks) == 2 and chunks[0] == '#')) and \
len(line) - len(chunks[-1]) < max_line_length - 7:
return
return None
if hasattr(line, 'decode'): # Python 2
# The line could contain multi-byte characters
try:
Expand Down Expand Up @@ -495,6 +495,58 @@ def indentation(logical_line, previous_logical, indent_char,
yield 0, tmpl % (3 + c, "unexpected indentation")


@register_check
def returns(logical_line, indent_level, previous_logical, checker_state):
r"""Be consistent in return statements.
Either all return statements in a function should return an expression, or
none of them should. If any return statement returns an expression, any
return statements where no value is returned should explicitly state this
as return None, [and an explicit return statement should be present at the
end of the function (if reachable).]
The reachability constraint is not implemented due to its complexity.
Okay: def a():\n return 1
Okay: def a():\n return 1\n return 2
Okay: def a():\n return
Okay: def a():\n return\n return
Okay: def a():\n def b():\n return\n return b
Okay: def a():\n def b():\n return 2\n return
E750: def a():\n return\n return 2
E750: def a():\n return 4\n return
"""
functions_stack = checker_state.setdefault('functions_stack', [])
# a stack of functions, containing:
# indent_level, return_without_value, return_with_value
INDENT, RETURN_NO_VALUE, RETURN_VALUE = 0, 1, 2
if STARTSWITH_DEF_REGEX.match(previous_logical):
functions_stack.append([indent_level, False, False])

if functions_stack and indent_level < functions_stack[-1][INDENT]:
functions_stack.pop()

if logical_line.startswith('return'):
try:
last_fun_record = functions_stack[-1]
except IndexError:
# ignore return statements outside of functions (this happens in
# pycodestyle unit tests only)
return

if logical_line == 'return':
if last_fun_record[RETURN_VALUE]:
yield 0, "E750 'return' without expression used in the same " \
"method as 'return' with expression"
last_fun_record[RETURN_NO_VALUE] = True
else:
if last_fun_record[RETURN_NO_VALUE]:
yield 0, "E750 'return' with expression used in the same " \
"method as 'return' without expression"
last_fun_record[RETURN_VALUE] = True


@register_check
def continued_indentation(logical_line, tokens, indent_level, hang_closing,
indent_char, noqa, verbose):
Expand Down Expand Up @@ -1910,15 +1962,15 @@ def error(self, line_number, offset, text, check):
"""Report an error, according to options."""
code = text[:4]
if self._ignore_code(code):
return
return None
if code in self.counters:
self.counters[code] += 1
else:
self.counters[code] = 1
self.messages[code] = text[5:]
# Don't care about expected errors or warnings
if code in self.expected:
return
return None
if self.print_filename and not self.file_errors:
print(self.filename)
self.file_errors += 1
Expand Down Expand Up @@ -2030,7 +2082,7 @@ def __init__(self, options):

def error(self, line_number, offset, text, check):
if line_number not in self._selected[self.filename]:
return
return None
return super(DiffReport, self).error(line_number, offset, text, check)


Expand Down
50 changes: 50 additions & 0 deletions testsuite/E75.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#: Okay
def okay_a(x):
if x:
return 1
else:
return 2
#: Okay
def okay_b(x):
if x:
x += 1
return
z.append(x)
return
#: E750:5:9
def not_okay_a():
if True:
return None
else:
return
#: Okay
def okay_nested_a():
def f():
if 1:
return
else:
return
return f
#: Okay
def okay_nested_b():
def f():
if 1:
return 3
else:
return 4
return
#: E750:6:5
def not_okay_nested_a(x):
def f():
return
if not x:
return
return f
#: E750:6:13
def not_okay_nested_b():
def f():
if 1:
return 3
else:
return
return
2 changes: 1 addition & 1 deletion testsuite/support.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def error(self, line_number, offset, text, check):
detailed_code = '%s:%s:%s' % (code, line_number, offset + 1)
# Don't care about expected errors or warnings
if code in self.expected or detailed_code in self.expected:
return
return None
self._deferred_print.append(
(line_number, offset, detailed_code, text[5:], check.__doc__))
self.file_errors += 1
Expand Down

0 comments on commit 026f7e7

Please sign in to comment.