diff --git a/doc/whatsnew/fragments/10508.bugfix b/doc/whatsnew/fragments/10508.bugfix new file mode 100644 index 0000000000..3195b4d441 --- /dev/null +++ b/doc/whatsnew/fragments/10508.bugfix @@ -0,0 +1,3 @@ +Fix false positive ``inconsistent-return-statements`` when using ``quit()`` or ``exit()`` functions. + +Closes #10508 diff --git a/pylint/checkers/refactoring/refactoring_checker.py b/pylint/checkers/refactoring/refactoring_checker.py index 5b41c4dde3..e3441541e5 100644 --- a/pylint/checkers/refactoring/refactoring_checker.py +++ b/pylint/checkers/refactoring/refactoring_checker.py @@ -2021,11 +2021,11 @@ def _is_node_return_ended(self, node: nodes.NodeNG) -> bool: # Recursion base case return True case nodes.Call(): + if utils.is_terminating_func(node): + return True return any( - ( - isinstance(maybe_func, (nodes.FunctionDef, bases.BoundMethod)) - and self._is_function_def_never_returning(maybe_func) - ) + isinstance(maybe_func, (nodes.FunctionDef, bases.BoundMethod)) + and self._is_function_def_never_returning(maybe_func) for maybe_func in utils.infer_all(node.func) ) case nodes.While(): diff --git a/tests/functional/i/inconsistent/inconsistent_returns_quit_exit.py b/tests/functional/i/inconsistent/inconsistent_returns_quit_exit.py new file mode 100644 index 0000000000..c78e4701db --- /dev/null +++ b/tests/functional/i/inconsistent/inconsistent_returns_quit_exit.py @@ -0,0 +1,61 @@ +# pylint: disable=missing-docstring, invalid-name, unused-argument, consider-using-sys-exit, no-else-return +"""Test that quit() and exit() are handled consistently with sys.exit()""" + +import sys + +# These functions should not trigger inconsistent-return-statements +# because quit, exit, and sys.exit are never-returning functions (#10508) + +def func_with_quit_1(i): + """quit() in else branch should be treated like sys.exit()""" + if i == 1: + return i + quit(1) + +def func_with_quit_2(i): + """quit() in if branch should be treated like sys.exit()""" + if i == 1: + quit(1) + return 1 + +def func_with_exit_1(i): + """exit() in else branch should be treated like sys.exit()""" + if i == 1: + return i + exit(1) + +def func_with_exit_2(i): + """exit() in if branch should be treated like sys.exit()""" + if i == 1: + exit(1) + return 1 + +def func_with_sys_exit_1(i): + """sys.exit() in else branch - baseline test""" + if i == 1: + return i + sys.exit(1) + +def func_with_sys_exit_2(i): + """sys.exit() in if branch - baseline test""" + if i == 1: + sys.exit(1) + return 1 + +# Test mixed usage +def func_mixed_exit_methods(i): + """Using different exit methods should all work consistently""" + if i == 1: + return "one" + if i == 2: + quit() + if i == 3: + exit() + sys.exit() + +# This should trigger inconsistent-return-statements +def func_inconsistent_example(i): # [inconsistent-return-statements] + """This should trigger the warning as a negative test case""" + if i == 1: + return i + print("Not exiting, just printing") diff --git a/tests/functional/i/inconsistent/inconsistent_returns_quit_exit.txt b/tests/functional/i/inconsistent/inconsistent_returns_quit_exit.txt new file mode 100644 index 0000000000..99dff5ccdc --- /dev/null +++ b/tests/functional/i/inconsistent/inconsistent_returns_quit_exit.txt @@ -0,0 +1 @@ +inconsistent-return-statements:57:0:57:29:func_inconsistent_example:Either all return statements in a function should return an expression, or none of them should.:UNDEFINED