summaryrefslogtreecommitdiff
path: root/pylint/checkers/variables.py
diff options
context:
space:
mode:
authorDaniƫl van Noord <13665637+DanielNoord@users.noreply.github.com>2022-09-19 14:12:45 +0200
committerGitHub <noreply@github.com>2022-09-19 14:12:45 +0200
commita47fbbf610d178f003c4c7c2cc31ce69f3fffd10 (patch)
tree8753dc97e819990fb3f45d5b4824afe41efcbf6f /pylint/checkers/variables.py
parenta5c8e23904e417ccf24c8f7feb5531e6f6a6251a (diff)
downloadpylint-git-a47fbbf610d178f003c4c7c2cc31ce69f3fffd10.tar.gz
Fix `undefined-loop-variable` with `NoReturn` and `Never` (#7476)
Co-authored-by: detachhead <detachhead@users.noreply.github.com> Co-authored-by: Jacob Walls <jacobtylerwalls@gmail.com>
Diffstat (limited to 'pylint/checkers/variables.py')
-rw-r--r--pylint/checkers/variables.py43
1 files changed, 35 insertions, 8 deletions
diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py
index dc0b6304b..38a3a7cc2 100644
--- a/pylint/checkers/variables.py
+++ b/pylint/checkers/variables.py
@@ -19,7 +19,7 @@ from functools import lru_cache
from typing import TYPE_CHECKING, Any, NamedTuple
import astroid
-from astroid import extract_node, nodes
+from astroid import bases, extract_node, nodes
from astroid.nodes import _base_nodes
from astroid.typing import InferenceResult
@@ -28,7 +28,12 @@ from pylint.checkers.utils import (
in_type_checking_block,
is_postponed_evaluation_enabled,
)
-from pylint.constants import PY39_PLUS, TYPING_TYPE_CHECKS_GUARDS
+from pylint.constants import (
+ PY39_PLUS,
+ TYPING_NEVER,
+ TYPING_NORETURN,
+ TYPING_TYPE_CHECKS_GUARDS,
+)
from pylint.interfaces import CONTROL_FLOW, HIGH, INFERENCE, INFERENCE_FAILURE
from pylint.typing import MessageDefinitionTuple
@@ -2255,13 +2260,35 @@ class VariablesChecker(BaseChecker):
if not isinstance(assign, nodes.For):
self.add_message("undefined-loop-variable", args=node.name, node=node)
return
- if any(
- isinstance(
+ for else_stmt in assign.orelse:
+ if isinstance(
else_stmt, (nodes.Return, nodes.Raise, nodes.Break, nodes.Continue)
- )
- for else_stmt in assign.orelse
- ):
- return
+ ):
+ return
+ # TODO: 2.16: Consider using RefactoringChecker._is_function_def_never_returning
+ if isinstance(else_stmt, nodes.Expr) and isinstance(
+ else_stmt.value, nodes.Call
+ ):
+ inferred_func = utils.safe_infer(else_stmt.value.func)
+ if (
+ isinstance(inferred_func, nodes.FunctionDef)
+ and inferred_func.returns
+ ):
+ inferred_return = utils.safe_infer(inferred_func.returns)
+ if isinstance(
+ inferred_return, nodes.FunctionDef
+ ) and inferred_return.qname() in {
+ *TYPING_NORETURN,
+ *TYPING_NEVER,
+ "typing._SpecialForm",
+ }:
+ return
+ # typing_extensions.NoReturn returns a _SpecialForm
+ if (
+ isinstance(inferred_return, bases.Instance)
+ and inferred_return.qname() == "typing._SpecialForm"
+ ):
+ return
maybe_walrus = utils.get_node_first_ancestor_of_type(node, nodes.NamedExpr)
if maybe_walrus: