diff options
author | hippo91 <guillaume.peillex@gmail.com> | 2017-09-26 17:03:42 +0200 |
---|---|---|
committer | Claudiu Popa <pcmanticore@gmail.com> | 2017-09-26 17:03:42 +0200 |
commit | 56a6723f96dddd1e70e668d808e8446fd53b841f (patch) | |
tree | cbead24fb86338441114e4cdc9245ac540a8d10c | |
parent | 8dd70cb76439d72ea02ade02ae0dfa028a46619c (diff) | |
download | pylint-git-56a6723f96dddd1e70e668d808e8446fd53b841f.tar.gz |
Adding a check for ``inconsistent-return-statements`` inside function or methods. (#1641)
Close #1267
29 files changed, 342 insertions, 43 deletions
@@ -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 |