Skip to content

Commit 4383461

Browse files
author
Tomer Chachamu
committed
check return statements
1 parent c631809 commit 4383461

File tree

4 files changed

+104
-5
lines changed

4 files changed

+104
-5
lines changed

docs/intro.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,8 @@ This is the current list of error and warning codes:
373373
+------------+----------------------------------------------------------------------+
374374
| E743 | do not define functions named 'l', 'O', or 'I' |
375375
+------------+----------------------------------------------------------------------+
376+
| E750 | do not mix 'return' and 'return value' in the same function |
377+
+------------+----------------------------------------------------------------------+
376378
+------------+----------------------------------------------------------------------+
377379
| **E9** | *Runtime* |
378380
+------------+----------------------------------------------------------------------+

pycodestyle.py

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ def maximum_line_length(physical_line, max_line_length, multiline, noqa):
281281
if ((len(chunks) == 1 and multiline) or
282282
(len(chunks) == 2 and chunks[0] == '#')) and \
283283
len(line) - len(chunks[-1]) < max_line_length - 7:
284-
return
284+
return None
285285
if hasattr(line, 'decode'): # Python 2
286286
# The line could contain multi-byte characters
287287
try:
@@ -495,6 +495,53 @@ def indentation(logical_line, previous_logical, indent_char,
495495
yield 0, tmpl % (3 + c, "unexpected indentation")
496496

497497

498+
@register_check
499+
def returns(logical_line, indent_level, previous_logical, checker_state):
500+
r"""Be consistent in return statements.
501+
502+
Either all return statements in a function should return an expression, or
503+
none of them should. If any return statement returns an expression, any
504+
return statements where no value is returned should explicitly state this
505+
as return None, [and an explicit return statement should be present at the
506+
end of the function (if reachable).]
507+
508+
The reachability constraint is not implemented due to its complexity.
509+
510+
Okay: def a():\n return 1
511+
Okay: def a():\n return 1\n return 2
512+
Okay: def a():\n return
513+
Okay: def a():\n return\n return
514+
Okay: def a():\n def b():\n return\n return b
515+
Okay: def a():\n def b():\n return 2\n return
516+
517+
E750: def a():\n return\n return 2
518+
E750: def a():\n return 4\n return
519+
"""
520+
functions_stack = checker_state.setdefault('functions_stack', [])
521+
# a stack of functions, containing:
522+
# indent_level, return_without_value, return_with_value
523+
INDENT, RETURN_NO_VALUE, RETURN_VALUE = 0, 1, 2
524+
if STARTSWITH_DEF_REGEX.match(previous_logical):
525+
functions_stack.append([indent_level, False, False])
526+
527+
if functions_stack and indent_level < functions_stack[-1][INDENT]:
528+
functions_stack.pop()
529+
530+
if functions_stack:
531+
if logical_line == 'return':
532+
last_fun_record = functions_stack[-1]
533+
if last_fun_record[RETURN_VALUE]:
534+
yield 0, "E750 'return' without expression used in the same " \
535+
"method as 'return' with expression"
536+
last_fun_record[RETURN_NO_VALUE] = True
537+
elif logical_line.startswith('return'):
538+
last_fun_record = functions_stack[-1]
539+
if last_fun_record[RETURN_NO_VALUE]:
540+
yield 0, "E750 'return' with expression used in the same " \
541+
"method as 'return' without expression"
542+
last_fun_record[RETURN_VALUE] = True
543+
544+
498545
@register_check
499546
def continued_indentation(logical_line, tokens, indent_level, hang_closing,
500547
indent_char, noqa, verbose):
@@ -1910,15 +1957,15 @@ def error(self, line_number, offset, text, check):
19101957
"""Report an error, according to options."""
19111958
code = text[:4]
19121959
if self._ignore_code(code):
1913-
return
1960+
return None
19141961
if code in self.counters:
19151962
self.counters[code] += 1
19161963
else:
19171964
self.counters[code] = 1
19181965
self.messages[code] = text[5:]
19191966
# Don't care about expected errors or warnings
19201967
if code in self.expected:
1921-
return
1968+
return None
19221969
if self.print_filename and not self.file_errors:
19231970
print(self.filename)
19241971
self.file_errors += 1
@@ -2030,7 +2077,7 @@ def __init__(self, options):
20302077

20312078
def error(self, line_number, offset, text, check):
20322079
if line_number not in self._selected[self.filename]:
2033-
return
2080+
return None
20342081
return super(DiffReport, self).error(line_number, offset, text, check)
20352082

20362083

testsuite/E75.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#: Okay
2+
def okay_a(x):
3+
if x:
4+
return 1
5+
else:
6+
return 2
7+
#: Okay
8+
def okay_b(x):
9+
if x:
10+
x += 1
11+
return
12+
z.append(x)
13+
return
14+
#: E750:5:9
15+
def not_okay_a():
16+
if True:
17+
return None
18+
else:
19+
return
20+
#: Okay
21+
def okay_nested_a():
22+
def f():
23+
if 1:
24+
return
25+
else:
26+
return
27+
return f
28+
#: Okay
29+
def okay_nested_b():
30+
def f():
31+
if 1:
32+
return 3
33+
else:
34+
return 4
35+
return
36+
#: E750:6:5
37+
def not_okay_nested_a(x):
38+
def f():
39+
return
40+
if not x:
41+
return
42+
return f
43+
#: E750:6:13
44+
def not_okay_nested_b():
45+
def f():
46+
if 1:
47+
return 3
48+
else:
49+
return
50+
return

testsuite/support.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def error(self, line_number, offset, text, check):
3838
detailed_code = '%s:%s:%s' % (code, line_number, offset + 1)
3939
# Don't care about expected errors or warnings
4040
if code in self.expected or detailed_code in self.expected:
41-
return
41+
return None
4242
self._deferred_print.append(
4343
(line_number, offset, detailed_code, text[5:], check.__doc__))
4444
self.file_errors += 1

0 commit comments

Comments
 (0)