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

Fix 'function never returning' computation #9591

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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions doc/whatsnew/fragments/7565.false_negative
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix computation of never-returning function: `Never` is handled in addition to `NoReturn`, and priority is given to the explicit `--never-returning-functions` option.

Closes #7565.
20 changes: 12 additions & 8 deletions pylint/checkers/refactoring/refactoring_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -2054,17 +2054,21 @@ def _is_function_def_never_returning(
Returns:
bool: True if the function never returns, False otherwise.
"""
if isinstance(node, (nodes.FunctionDef, astroid.BoundMethod)) and node.returns:
return (
try:
if node.qname() in self._never_returning_functions:
return True
except (TypeError, AttributeError):
pass
return (
isinstance(node, (nodes.FunctionDef, astroid.BoundMethod))
and node.returns
and (
isinstance(node.returns, nodes.Attribute)
and node.returns.attrname == "NoReturn"
and node.returns.attrname in {"NoReturn", "Never"}
or isinstance(node.returns, nodes.Name)
and node.returns.name == "NoReturn"
and node.returns.name in {"NoReturn", "Never"}
)
try:
return node.qname() in self._never_returning_functions
except (TypeError, AttributeError):
return False
)

def _check_return_at_the_end(self, node: nodes.FunctionDef) -> None:
"""Check for presence of a *single* return statement at the end of a
Expand Down
22 changes: 21 additions & 1 deletion tests/functional/i/inconsistent/inconsistent_returns_noreturn.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import sys
import typing
import typing_extensions

def parser_error(msg) -> typing.NoReturn: # pylint: disable=unused-argument
sys.exit(1)
Expand All @@ -11,7 +12,7 @@ def parser_error_nortype(msg): # pylint: disable=unused-argument
sys.exit(2)


from typing import NoReturn # pylint: disable=wrong-import-position
from typing import NoReturn # pylint: disable=wrong-import-position,wrong-import-order

def parser_error_name(msg) -> NoReturn: # pylint: disable=unused-argument
sys.exit(3)
Expand Down Expand Up @@ -95,3 +96,22 @@ def bug_pylint_8747_incorrect_annotation(self, s: str) -> int:
return n
except ValueError:
self._falsely_no_return_method()

# https://github.com/pylint-dev/pylint/issues/7565
def never_is_handled_like_noreturn(arg: typing.Union[int, str]) -> int:
if isinstance(arg, int):
return 1
if isinstance(arg, str):
return 2
typing_extensions.assert_never(arg)


def declared_to_not_return() -> None:
return

def config_takes_precedence_over_inference(arg: typing.Union[int, str]) -> int:
if isinstance(arg, int):
return 1
if isinstance(arg, str):
return 2
declared_to_not_return()
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
[testoptions]
min_pyver=3.12
[REFACTORING]
never-returning-functions=sys.exit,sys.getdefaultencoding
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
inconsistent-return-statements:32:0:32:25:bug_pylint_4122_wrong:Either all return statements in a function should return an expression, or none of them should.:UNDEFINED
inconsistent-return-statements:77:4:77:29:ClassUnderTest.bug_pylint_8747_wrong:Either all return statements in a function should return an expression, or none of them should.:UNDEFINED
inconsistent-return-statements:33:0:33:25:bug_pylint_4122_wrong:Either all return statements in a function should return an expression, or none of them should.:UNDEFINED
inconsistent-return-statements:78:4:78:29:ClassUnderTest.bug_pylint_8747_wrong:Either all return statements in a function should return an expression, or none of them should.:UNDEFINED