Skip to content

Commit

Permalink
Fix "function never returning" computation
Browse files Browse the repository at this point in the history
This fixes 2 bugs in the computation:
* `Never` is handled in addition to `NoReturn`.
* Give priority to the explicit `--never-returning-functions` option.

Closes pylint-dev#7565
  • Loading branch information
svenpanne committed Apr 12, 2024
1 parent bb9e079 commit 3b957cb
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 12 deletions.
3 changes: 3 additions & 0 deletions doc/whatsnew/fragments/7565.false_positive
@@ -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
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
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()
@@ -1,2 +1,2 @@
[REFACTORING]
never-returning-functions=sys.exit,sys.getdefaultencoding
never-returning-functions=sys.exit,sys.getdefaultencoding,inconsistent_returns_noreturn.declared_to_not_return
@@ -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

0 comments on commit 3b957cb

Please sign in to comment.