Skip to content
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

Check inconsistent return statements #711

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
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
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These can be taken out as they are tested in the more detailed tests.

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