summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhippo91 <guillaume.peillex@gmail.com>2017-09-26 17:03:42 +0200
committerClaudiu Popa <pcmanticore@gmail.com>2017-09-26 17:03:42 +0200
commit56a6723f96dddd1e70e668d808e8446fd53b841f (patch)
treecbead24fb86338441114e4cdc9245ac540a8d10c
parent8dd70cb76439d72ea02ade02ae0dfa028a46619c (diff)
downloadpylint-git-56a6723f96dddd1e70e668d808e8446fd53b841f.tar.gz
Adding a check for ``inconsistent-return-statements`` inside function or methods. (#1641)
Close #1267
-rw-r--r--ChangeLog3
-rw-r--r--doc/whatsnew/1.8.rst28
-rw-r--r--pylint/checkers/classes.py8
-rw-r--r--pylint/checkers/format.py1
-rw-r--r--pylint/checkers/imports.py5
-rw-r--r--pylint/checkers/python3.py2
-rw-r--r--pylint/checkers/refactoring.py82
-rw-r--r--pylint/checkers/typecheck.py27
-rw-r--r--pylint/checkers/utils.py40
-rw-r--r--pylint/checkers/variables.py4
-rwxr-xr-xpylint/epylint.py1
-rw-r--r--pylint/extensions/_check_docs_utils.py1
-rw-r--r--pylint/extensions/mccabe.py2
-rw-r--r--pylint/lint.py3
-rw-r--r--pylint/pyreverse/utils.py3
-rw-r--r--pylint/test/functional/inconsistent_returns.py140
-rw-r--r--pylint/test/functional/inconsistent_returns.txt7
-rw-r--r--pylint/test/functional/lost_exception.py1
-rw-r--r--pylint/test/functional/nested_blocks_issue1088.py2
-rw-r--r--pylint/test/functional/nested_blocks_issue1088.txt2
-rw-r--r--pylint/test/functional/no_else_return.py2
-rw-r--r--pylint/test/functional/too_many_nested_blocks.py2
-rw-r--r--pylint/test/functional/too_many_nested_blocks.txt2
-rw-r--r--pylint/test/functional/unpacking_non_sequence.py1
-rw-r--r--pylint/test/functional/useless_else_on_loop.py2
-rw-r--r--pylint/test/functional/useless_else_on_loop.txt8
-rw-r--r--pylint/test/functional/using_constant_test.py1
-rw-r--r--pylint/test/input/func_return_yield_mix_py_33.py4
-rw-r--r--pylint/utils.py1
29 files changed, 342 insertions, 43 deletions
diff --git a/ChangeLog b/ChangeLog
index cadfea3bf..62c0610fb 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -102,6 +102,9 @@ What's New in Pylint 1.8?
Close #1336
+ * Added a new Python check for inconsistent return statements inside method or function.
+ Close #1267
+
* Fix ``superfluous-parens`` false positive related to handling logical statements
involving ``in`` operator.
diff --git a/doc/whatsnew/1.8.rst b/doc/whatsnew/1.8.rst
index ab058b9ed..0e3b8af83 100644
--- a/doc/whatsnew/1.8.rst
+++ b/doc/whatsnew/1.8.rst
@@ -219,6 +219,34 @@ New checkers
var = "123"
logging.log(logging.INFO, "Var: %s", var)
+* A new Python checker was added to warn about ``inconsistent-return-statements``. A function or a method
+ has inconsistent return statements if it returns both explicit and implicit values :
+
+ .. code-block:: python
+
+ def mix_implicit_explicit_returns(arg):
+ if arg < 10:
+ return True
+ elif arg < 20:
+ return
+
+ According to PEP8_, 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).
+ Thus, the previous function should be written:
+
+ .. code-block:: python
+
+ def mix_implicit_explicit_returns(arg):
+ if arg < 10:
+ return True
+ elif arg < 20:
+ return None
+
+ Close #1267
+
+ .. _PEP8: https://www.python.org/dev/peps/pep-0008
+
Other Changes
=============
diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py
index 985e4e4ee..5d71b98f3 100644
--- a/pylint/checkers/classes.py
+++ b/pylint/checkers/classes.py
@@ -298,14 +298,14 @@ def _safe_infer_call_result(node, caller, context=None):
inferit = node.infer_call_result(caller, context=context)
value = next(inferit)
except astroid.InferenceError:
- return # inference failed
+ return None # inference failed
except StopIteration:
- return # no values infered
+ return None # no values infered
try:
next(inferit)
- return # there is ambiguity on the inferred node
+ return None # there is ambiguity on the inferred node
except astroid.InferenceError:
- return # there is some kind of ambiguity
+ return None # there is some kind of ambiguity
except StopIteration:
return value
diff --git a/pylint/checkers/format.py b/pylint/checkers/format.py
index eed5f9fba..1266f6fe0 100644
--- a/pylint/checkers/format.py
+++ b/pylint/checkers/format.py
@@ -1064,6 +1064,7 @@ class FormatChecker(BaseTokenChecker):
self.add_message('bad-indentation', line=line_num,
args=(level * unit_size + len(suppl), i_type,
expected * unit_size))
+ return None
def register(linter):
diff --git a/pylint/checkers/imports.py b/pylint/checkers/imports.py
index 31373e67a..af7cb1b2b 100644
--- a/pylint/checkers/imports.py
+++ b/pylint/checkers/imports.py
@@ -97,6 +97,7 @@ def _get_first_import(node, context, name, base, level, alias):
break
if found and not are_exclusive(first, node):
return first
+ return None
def _ignore_import_failure(node, modname, ignored_modules):
@@ -629,7 +630,7 @@ class ImportsChecker(BaseChecker):
the imported module name.
"""
if not self.linter.is_message_enabled('relative-import'):
- return
+ return None
if importedmodnode.file is None:
return False # built-in module
if modnode is importedmodnode:
@@ -641,6 +642,8 @@ class ImportsChecker(BaseChecker):
self.add_message('relative-import',
args=(importedasname, importedmodnode.name),
node=importnode)
+ return None
+ return None
def _add_imported_module(self, node, importedmodname):
"""notify an imported module, used to analyze dependencies"""
diff --git a/pylint/checkers/python3.py b/pylint/checkers/python3.py
index dfa67a79a..fabcb7e48 100644
--- a/pylint/checkers/python3.py
+++ b/pylint/checkers/python3.py
@@ -36,6 +36,7 @@ def _is_old_octal(literal):
except ValueError:
return False
return True
+ return None
def _check_dict_node(node):
@@ -892,6 +893,7 @@ class Python3Checker(checkers.BaseChecker):
if isinstance(value, str):
self.add_message('raising-string', node=node)
return True
+ return None
class Python3TokenChecker(checkers.BaseTokenChecker):
diff --git a/pylint/checkers/refactoring.py b/pylint/checkers/refactoring.py
index fd79e1f43..cb456bac4 100644
--- a/pylint/checkers/refactoring.py
+++ b/pylint/checkers/refactoring.py
@@ -27,6 +27,45 @@ def _all_elements_are_true(gen):
return values and all(values)
+def _is_node_return_ended(node):
+ """Check if the node ends with an explicit return statement.
+
+ Args:
+ node (astroid.NodeNG): node to be checked.
+
+ Returns:
+ bool: True if the node ends with an explicit statement, False otherwise.
+
+ """
+ # Recursion base case
+ if isinstance(node, astroid.Return):
+ return True
+ if isinstance(node, astroid.Raise):
+ # a Raise statement doesn't need to end with a return statement
+ # but if the exception raised is handled, then the handler has to
+ # ends with a return statement
+ exc = utils.safe_infer(node.exc)
+ if exc is None or exc is astroid.Uninferable:
+ return False
+ exc_name = exc.pytype().split('.')[-1]
+ handlers = utils.get_exception_handlers(node, exc_name)
+ if handlers:
+ # among all the handlers handling the exception at least one
+ # must end with a return statement
+ return any(_is_node_return_ended(_handler) for _handler in handlers)
+ # if no handlers handle the exception then it's ok
+ return True
+ if isinstance(node, astroid.If):
+ # if statement is returning if there are exactly two return statements in its
+ # children : one for the body part, the other for the orelse part
+ return_stmts = [_is_node_return_ended(_child) for _child in node.get_children()]
+ return sum(return_stmts) == 2
+ # recurses on the children of the node except for those which are except handler
+ # because one cannot be sure that the handler will really be used
+ return any(_is_node_return_ended(_child) for _child in node.get_children()
+ if not isinstance(_child, astroid.ExceptHandler))
+
+
def _if_statement_is_always_returning(if_node):
def _has_return_node(elems, scope):
for node in elems:
@@ -111,6 +150,14 @@ class RefactoringChecker(checkers.BaseTokenChecker):
'a generator may lead to hard to find bugs. This PEP specify that '
'raise StopIteration has to be replaced by a simple return statement',
{'minversion': (3, 0)}),
+ 'R1710': ('Either all return statements in a function should return an expression, '
+ 'or none of them should.',
+ 'inconsistent-return-statements',
+ 'According to PEP8, 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)'
+ ),
}
options = (('max-nested-blocks',
{'default': 5, 'type': 'int', 'metavar': '<int>',
@@ -122,6 +169,7 @@ class RefactoringChecker(checkers.BaseTokenChecker):
def __init__(self, linter=None):
checkers.BaseTokenChecker.__init__(self, linter)
+ self._return_nodes = {}
self._init()
def _init(self):
@@ -309,12 +357,15 @@ class RefactoringChecker(checkers.BaseTokenChecker):
self._check_nested_blocks(node)
self._check_superfluous_else_return(node)
- @utils.check_messages('too-many-nested-blocks')
- def leave_functiondef(self, _):
+ @utils.check_messages('too-many-nested-blocks', 'inconsistent-return-statements')
+ def leave_functiondef(self, node):
# check left-over nested blocks stack
self._emit_nested_blocks_message_if_needed(self._nested_blocks)
# new scope = reinitialize the stack of nested blocks
self._nested_blocks = []
+ # check consistent return statements
+ self._check_consistent_returns(node)
+ self._return_nodes[node.name] = []
def visit_raise(self, node):
self._check_stop_iteration_inside_generator(node)
@@ -491,6 +542,33 @@ class RefactoringChecker(checkers.BaseTokenChecker):
condition = node.slice.value
return condition, true_value, false_value
+ def visit_functiondef(self, node):
+ self._return_nodes[node.name] = []
+ return_nodes = node.nodes_of_class(astroid.Return)
+ self._return_nodes[node.name] = [_rnode for _rnode in return_nodes
+ if _rnode.frame() == node.frame()]
+
+ def _check_consistent_returns(self, node):
+ """Check that all return statements inside a function are consistent.
+
+ Return statements are consistent if:
+ - all returns are explicit and if there is no implicit return;
+ - all returns are empty and if there is, possibly, an implicit return.
+
+ Args:
+ node (astroid.FunctionDef): the function holding the return statements.
+
+ """
+ # explicit return statements are those with a not None value
+ explicit_returns = [_node for _node in self._return_nodes[node.name]
+ if _node.value is not None]
+ if not explicit_returns:
+ return
+ if (len(explicit_returns) == len(self._return_nodes[node.name])
+ and _is_node_return_ended(node)):
+ return
+ self.add_message('inconsistent-return-statements', node=node)
+
class RecommandationChecker(checkers.BaseChecker):
__implements__ = (interfaces.IAstroidChecker,)
diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py
index fe3f5571e..57f8e0c88 100644
--- a/pylint/checkers/typecheck.py
+++ b/pylint/checkers/typecheck.py
@@ -1002,15 +1002,15 @@ accessed. Python regular expressions are accepted.'}
@check_messages('invalid-sequence-index')
def visit_index(self, node):
if not node.parent or not hasattr(node.parent, "value"):
- return
+ return None
# Look for index operations where the parent is a sequence type.
# If the types can be determined, only allow indices to be int,
# slice or instances with __index__.
parent_type = safe_infer(node.parent.value)
if not isinstance(parent_type, (astroid.ClassDef, astroid.Instance)):
- return
+ return None
if not has_known_bases(parent_type):
- return
+ return None
# Determine what method on the parent this index will use
# The parent of this node will be a Subscript, and the parent of that
@@ -1029,20 +1029,20 @@ accessed. Python regular expressions are accepted.'}
try:
methods = dunder_lookup.lookup(parent_type, methodname)
if methods is astroid.YES:
- return
+ return None
itemmethod = methods[0]
except (exceptions.NotFoundError,
exceptions.AttributeInferenceError,
IndexError):
- return
+ return None
if not isinstance(itemmethod, astroid.FunctionDef):
- return
+ return None
if itemmethod.root().name != BUILTINS:
- return
+ return None
if not itemmethod.parent:
- return
+ return None
if itemmethod.parent.name not in SEQUENCE_TYPES:
- return
+ return None
# For ExtSlice objects coming from visit_extslice, no further
# inference is necessary, since if we got this far the ExtSlice
@@ -1052,18 +1052,18 @@ accessed. Python regular expressions are accepted.'}
else:
index_type = safe_infer(node)
if index_type is None or index_type is astroid.YES:
- return
+ return None
# Constants must be of type int
if isinstance(index_type, astroid.Const):
if isinstance(index_type.value, int):
- return
+ return None
# Instance values must be int, slice, or have an __index__ method
elif isinstance(index_type, astroid.Instance):
if index_type.pytype() in (BUILTINS + '.int', BUILTINS + '.slice'):
- return
+ return None
try:
index_type.getattr('__index__')
- return
+ return None
except exceptions.NotFoundError:
pass
elif isinstance(index_type, astroid.Slice):
@@ -1074,6 +1074,7 @@ accessed. Python regular expressions are accepted.'}
# Anything else is an error
self.add_message('invalid-sequence-index', node=node)
+ return None
@check_messages('invalid-slice-index')
def visit_slice(self, node):
diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py
index 7af35f873..2dd41b69b 100644
--- a/pylint/checkers/utils.py
+++ b/pylint/checkers/utils.py
@@ -505,6 +505,7 @@ def _is_property_decorator(decorator):
for ancestor in infered.ancestors():
if ancestor.name == 'property' and ancestor.root().name == BUILTINS_NAME:
return True
+ return None
def decorated_with(func, qnames):
@@ -610,21 +611,38 @@ def is_from_fallback_block(node):
def _except_handlers_ignores_exception(handlers, exception):
- func = functools.partial(error_of_type,
- error_type=(exception, ))
+ func = functools.partial(error_of_type, error_type=(exception, ))
return any(map(func, handlers))
-def node_ignores_exception(node, exception):
- """Check if the node is in a TryExcept which handles the given exception."""
+def get_exception_handlers(node, exception):
+ """Return the collections of handlers handling the exception in arguments.
+
+ Args:
+ node (astroid.Raise): the node raising the exception.
+ exception (builtin.Exception or str): exception or name of the exception.
+
+ Returns:
+ generator: the collection of handlers that are handling the exception or None.
+
+ """
current = node
ignores = (astroid.ExceptHandler, astroid.TryExcept)
while current and not isinstance(current.parent, ignores):
current = current.parent
if current and isinstance(current.parent, astroid.TryExcept):
- return _except_handlers_ignores_exception(current.parent.handlers, exception)
- return False
+ return (_handler for _handler in current.parent.handlers
+ if error_of_type(_handler, exception))
+ return None
+
+
+def node_ignores_exception(node, exception):
+ """Check if the node is in a TryExcept which handles the given exception."""
+ managing_handlers = get_exception_handlers(node, exception)
+ if not managing_handlers:
+ return False
+ return any(managing_handlers)
def class_is_abstract(node):
@@ -774,12 +792,12 @@ def safe_infer(node, context=None):
inferit = node.infer(context=context)
value = next(inferit)
except astroid.InferenceError:
- return
+ return None
try:
next(inferit)
- return # None if there is ambiguity on the inferred node
+ return None # None if there is ambiguity on the inferred node
except astroid.InferenceError:
- return # there is some kind of ambiguity
+ return None # there is some kind of ambiguity
except StopIteration:
return value
@@ -824,9 +842,9 @@ def node_type(node):
continue
types.add(var_type)
if len(types) > 1:
- return
+ return None
except astroid.InferenceError:
- return
+ return None
return types.pop() if types else None
diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py
index 604708991..cd0691c56 100644
--- a/pylint/checkers/variables.py
+++ b/pylint/checkers/variables.py
@@ -44,12 +44,13 @@ def _is_from_future_import(stmt, name):
try:
module = stmt.do_import_module(stmt.modname)
except astroid.AstroidBuildingException:
- return
+ return None
for local_node in module.locals.get(name, []):
if (isinstance(local_node, astroid.ImportFrom)
and local_node.modname == FUTURE):
return True
+ return None
def in_for_else_branch(parent, stmt):
@@ -203,6 +204,7 @@ def _find_frame_imports(name, frame):
return True
elif import_name and import_name == name:
return True
+ return None
def _import_name_is_global(stmt, global_names):
diff --git a/pylint/epylint.py b/pylint/epylint.py
index 0b714fb4e..fc28aaaf6 100755
--- a/pylint/epylint.py
+++ b/pylint/epylint.py
@@ -158,6 +158,7 @@ def py_run(command_options='', return_std=False, stdout=None, stderr=None):
# Return standard output and error
if return_std:
return six.moves.StringIO(proc_stdout), six.moves.StringIO(proc_stderr)
+ return None
def Run():
diff --git a/pylint/extensions/_check_docs_utils.py b/pylint/extensions/_check_docs_utils.py
index ffa7de357..a626bfddd 100644
--- a/pylint/extensions/_check_docs_utils.py
+++ b/pylint/extensions/_check_docs_utils.py
@@ -45,6 +45,7 @@ def get_setters_property_name(node):
decorator.attrname == "setter" and
isinstance(decorator.expr, astroid.Name)):
return decorator.expr.name
+ return None
def get_setters_property(node):
diff --git a/pylint/extensions/mccabe.py b/pylint/extensions/mccabe.py
index 8e231ccd7..ae1ef0a8c 100644
--- a/pylint/extensions/mccabe.py
+++ b/pylint/extensions/mccabe.py
@@ -75,7 +75,7 @@ class PathGraphingAstVisitor(Mccabe_PathGraphingAstVisitor):
def _append_node(self, node):
if not self.tail:
- return
+ return None
self.graph.connect(self.tail, node)
self.tail = node
return node
diff --git a/pylint/lint.py b/pylint/lint.py
index 59b901a8f..c64211f2e 100644
--- a/pylint/lint.py
+++ b/pylint/lint.py
@@ -84,6 +84,7 @@ def _get_python_path(filepath):
dirname = os.path.dirname(dirname)
if old_dirname == dirname:
return os.getcwd()
+ return None
def _merge_stats(stats):
@@ -928,7 +929,7 @@ class PyLinter(config.OptionsManagerMixIn,
tokens = utils.tokenize_module(ast_node)
except tokenize.TokenError as ex:
self.add_message('syntax-error', line=ex.args[1][0], args=ex.args[0])
- return
+ return None
if not ast_node.pure_python:
self.add_message('raw-checker-failed', args=ast_node.name)
diff --git a/pylint/pyreverse/utils.py b/pylint/pyreverse/utils.py
index 5ab384164..1878cbc2b 100644
--- a/pylint/pyreverse/utils.py
+++ b/pylint/pyreverse/utils.py
@@ -189,7 +189,7 @@ class LocalsVisitor(ASTWalker):
def visit(self, node):
"""launch the visit starting from the given node"""
if node in self._visited:
- return
+ return None
self._visited[node] = 1 # FIXME: use set ?
methods = self.get_callbacks(node)
if methods[0] is not None:
@@ -199,3 +199,4 @@ class LocalsVisitor(ASTWalker):
self.visit(local_node)
if methods[1] is not None:
return methods[1](node)
+ return None
diff --git a/pylint/test/functional/inconsistent_returns.py b/pylint/test/functional/inconsistent_returns.py
new file mode 100644
index 000000000..1b9bd447d
--- /dev/null
+++ b/pylint/test/functional/inconsistent_returns.py
@@ -0,0 +1,140 @@
+#pylint: disable=missing-docstring, no-else-return, invalid-name, unused-variable, superfluous-parens
+"""Testing inconsistent returns"""
+import math
+
+# These ones are consistent
+def explicit_returns(var):
+ if var >= 0:
+ return math.sqrt(var)
+ else:
+ return None
+
+def explicit_returns2(var):
+ if var < 0:
+ return None
+ return math.sqrt(var)
+
+def empty_implicit_returns(var):
+ if var < 0:
+ return
+
+def returns_in_exceptions():
+ try:
+ raise ValueError('test')
+ except ValueError:
+ return 1
+ except (OSError, TypeError):
+ return 2
+
+def returns_and_exceptions(var):
+ if var < 10:
+ return var**2
+ else:
+ raise ValueError("Incorrect value")
+
+def explicit_returns3(arg):
+ if arg:
+ return False
+ else:
+ if arg < 3:
+ print('arg < 3')
+ return True
+
+def explicit_returns4(arg):
+ if arg:
+ if arg > 2:
+ print('arg > 2')
+ return False
+ else:
+ if arg < 3:
+ print('arg < 3')
+ return True
+
+def explicit_returns5(arg):
+ if arg:
+ if arg > 2:
+ print('arg > 2')
+ return False
+ else:
+ return True
+
+def nested_function():
+ def dummy_return():
+ return
+ return dummy_return
+
+def explicit_returns6(x, y, z):
+ if x: # pylint: disable=no-else-return
+ a = 1
+ if y: # pylint: disable=no-else-return
+ b = 2
+ return y
+ else:
+ c = 3
+ return x
+ else:
+ d = 4
+ return z
+
+def explicit_returns7(arg):
+ if arg < 0:
+ arg = 2 * arg
+ return 'below 0'
+ elif arg == 0:
+ print("Null arg")
+ return '0'
+ else:
+ arg = 3 * arg
+ return 'above 0'
+
+# Next ones are not consistent
+def explicit_implicit_returns(var): # [inconsistent-return-statements]
+ if var >= 0:
+ return math.sqrt(var)
+
+def empty_explicit_returns(var): # [inconsistent-return-statements]
+ if var < 0:
+ return
+ return math.sqrt(var)
+
+def explicit_implicit_returns2(arg): # [inconsistent-return-statements]
+ if arg:
+ if arg > 2:
+ print('arg > 2')
+ return False
+ else:
+ return True
+
+def explicit_implicit_returns3(arg): # [inconsistent-return-statements]
+ if arg:
+ if arg > 2:
+ print('arg > 2')
+ return False
+ else:
+ return True
+
+def returns_missing_in_catched_exceptions(arg): # [inconsistent-return-statements]
+ try:
+ arg = arg**2
+ raise ValueError('test')
+ except ValueError:
+ print('ValueError')
+ arg = 0
+ except (OSError, TypeError):
+ return 2
+
+def complex_func(arg): # [inconsistent-return-statements]
+ for i in range(arg):
+ if i > arg / 2:
+ break
+ else:
+ return arg
+
+def inconsistent_returns_in_nested_function():
+ def not_consistent_returns_inner(arg): # [inconsistent-return-statements]
+ for i in range(arg):
+ if i > arg / 2:
+ break
+ else:
+ return arg
+ return not_consistent_returns_inner
diff --git a/pylint/test/functional/inconsistent_returns.txt b/pylint/test/functional/inconsistent_returns.txt
new file mode 100644
index 000000000..84f2af47a
--- /dev/null
+++ b/pylint/test/functional/inconsistent_returns.txt
@@ -0,0 +1,7 @@
+inconsistent-return-statements:91:explicit_implicit_returns:Either all return statements in a function should return an expression, or none of them should.
+inconsistent-return-statements:95:empty_explicit_returns:Either all return statements in a function should return an expression, or none of them should.
+inconsistent-return-statements:100:explicit_implicit_returns2:Either all return statements in a function should return an expression, or none of them should.
+inconsistent-return-statements:108:explicit_implicit_returns3:Either all return statements in a function should return an expression, or none of them should.
+inconsistent-return-statements:116:returns_missing_in_catched_exceptions:Either all return statements in a function should return an expression, or none of them should.
+inconsistent-return-statements:126:complex_func:Either all return statements in a function should return an expression, or none of them should.
+inconsistent-return-statements:134:inconsistent_returns_in_nested_function.not_consistent_returns_inner:Either all return statements in a function should return an expression, or none of them should.
diff --git a/pylint/test/functional/lost_exception.py b/pylint/test/functional/lost_exception.py
index 685b6cd8c..fe7deddb7 100644
--- a/pylint/test/functional/lost_exception.py
+++ b/pylint/test/functional/lost_exception.py
@@ -33,6 +33,7 @@ def break_and_return():
def strange():
if True:
return my_var
+ return None
strange()
if i:
break
diff --git a/pylint/test/functional/nested_blocks_issue1088.py b/pylint/test/functional/nested_blocks_issue1088.py
index 4ff2d5984..d44a9f71f 100644
--- a/pylint/test/functional/nested_blocks_issue1088.py
+++ b/pylint/test/functional/nested_blocks_issue1088.py
@@ -7,6 +7,7 @@ def had_bug(num):
if num > 5:
if num > 6:
return True
+ return None
def was_correct(num):
@@ -19,3 +20,4 @@ def was_correct(num):
return True
if num == 0:
return False
+ return None
diff --git a/pylint/test/functional/nested_blocks_issue1088.txt b/pylint/test/functional/nested_blocks_issue1088.txt
index 5d95aab44..cf226de78 100644
--- a/pylint/test/functional/nested_blocks_issue1088.txt
+++ b/pylint/test/functional/nested_blocks_issue1088.txt
@@ -1,2 +1,2 @@
too-many-nested-blocks:3:had_bug:Too many nested blocks (6/5):HIGH
-too-many-nested-blocks:13:was_correct:Too many nested blocks (6/5):HIGH
+too-many-nested-blocks:14:was_correct:Too many nested blocks (6/5):HIGH
diff --git a/pylint/test/functional/no_else_return.py b/pylint/test/functional/no_else_return.py
index 0ed2e0720..bee354bea 100644
--- a/pylint/test/functional/no_else_return.py
+++ b/pylint/test/functional/no_else_return.py
@@ -50,6 +50,7 @@ def bar2(w, x, y, z):
a = 1
else:
return w
+ return None
def bar3(x, y, z):
@@ -58,3 +59,4 @@ def bar3(x, y, z):
return y
else:
return z
+ return None
diff --git a/pylint/test/functional/too_many_nested_blocks.py b/pylint/test/functional/too_many_nested_blocks.py
index da20c5512..e7c007957 100644
--- a/pylint/test/functional/too_many_nested_blocks.py
+++ b/pylint/test/functional/too_many_nested_blocks.py
@@ -70,6 +70,7 @@ def elif_function():
return 6
elif arg == 7:
return 7
+ return None
def else_if_function():
arg = None
@@ -93,3 +94,4 @@ def else_if_function():
else:
if arg == 7:
return 7
+ return None
diff --git a/pylint/test/functional/too_many_nested_blocks.txt b/pylint/test/functional/too_many_nested_blocks.txt
index dcee9d2a1..b2a30fd25 100644
--- a/pylint/test/functional/too_many_nested_blocks.txt
+++ b/pylint/test/functional/too_many_nested_blocks.txt
@@ -1,2 +1,2 @@
too-many-nested-blocks:6:my_function:Too many nested blocks (6/5)
-too-many-nested-blocks:76:else_if_function:Too many nested blocks (7/5)
+too-many-nested-blocks:77:else_if_function:Too many nested blocks (7/5)
diff --git a/pylint/test/functional/unpacking_non_sequence.py b/pylint/test/functional/unpacking_non_sequence.py
index a674a3796..7c1439b9a 100644
--- a/pylint/test/functional/unpacking_non_sequence.py
+++ b/pylint/test/functional/unpacking_non_sequence.py
@@ -137,3 +137,4 @@ def flow_control_unpacking(var=None):
if var is not None:
var0, var1 = var
return var0, var1
+ return None
diff --git a/pylint/test/functional/useless_else_on_loop.py b/pylint/test/functional/useless_else_on_loop.py
index bd8534f53..c6ee2b77d 100644
--- a/pylint/test/functional/useless_else_on_loop.py
+++ b/pylint/test/functional/useless_else_on_loop.py
@@ -9,6 +9,7 @@ def test_return_for():
return i
else: # [useless-else-on-loop]
print('math is broken')
+ return None
def test_return_while():
"""else + return is not accetable."""
@@ -16,6 +17,7 @@ def test_return_while():
return 1
else: # [useless-else-on-loop]
print('math is broken')
+ return None
while True:
diff --git a/pylint/test/functional/useless_else_on_loop.txt b/pylint/test/functional/useless_else_on_loop.txt
index 93309b6ef..c6a8c66e5 100644
--- a/pylint/test/functional/useless_else_on_loop.txt
+++ b/pylint/test/functional/useless_else_on_loop.txt
@@ -1,5 +1,5 @@
useless-else-on-loop:10:test_return_for:Else clause on loop without a break statement
-useless-else-on-loop:17:test_return_while:Else clause on loop without a break statement
-useless-else-on-loop:26::Else clause on loop without a break statement
-useless-else-on-loop:33::Else clause on loop without a break statement
-useless-else-on-loop:38::Else clause on loop without a break statement
+useless-else-on-loop:18:test_return_while:Else clause on loop without a break statement
+useless-else-on-loop:28::Else clause on loop without a break statement
+useless-else-on-loop:35::Else clause on loop without a break statement
+useless-else-on-loop:40::Else clause on loop without a break statement
diff --git a/pylint/test/functional/using_constant_test.py b/pylint/test/functional/using_constant_test.py
index fcaae722a..2c0f58d54 100644
--- a/pylint/test/functional/using_constant_test.py
+++ b/pylint/test/functional/using_constant_test.py
@@ -127,6 +127,7 @@ if [1, 2, 3][:1]:
def test(*args):
if args:
return 42
+ return None
def test_good_comprehension_checks():
[data for data in range(100)]
diff --git a/pylint/test/input/func_return_yield_mix_py_33.py b/pylint/test/input/func_return_yield_mix_py_33.py
index 473c9daaf..b120b80ac 100644
--- a/pylint/test/input/func_return_yield_mix_py_33.py
+++ b/pylint/test/input/func_return_yield_mix_py_33.py
@@ -1,5 +1,5 @@
-"""pylint should detect yield and return mix inside genrators"""
-# pylint: disable=using-constant-test
+"""pylint should detect yield and return mix inside generators"""
+# pylint: disable=using-constant-test, inconsistent-return-statements
def somegen():
"""this is a bad generator"""
if True:
diff --git a/pylint/utils.py b/pylint/utils.py
index 4ad04d6cd..dc796a883 100644
--- a/pylint/utils.py
+++ b/pylint/utils.py
@@ -329,6 +329,7 @@ class MessagesHandlerMixIn(object):
return MSG_STATE_SCOPE_MODULE
except (KeyError, TypeError):
return MSG_STATE_SCOPE_CONFIG
+ return None
def is_message_enabled(self, msg_descr, line=None, confidence=None):
"""return true if the message associated to the given message id is