|
19 | 19 | from typing import TYPE_CHECKING, Any, NamedTuple
|
20 | 20 |
|
21 | 21 | import astroid
|
22 |
| -from astroid import extract_node, nodes |
| 22 | +from astroid import bases, extract_node, nodes |
23 | 23 | from astroid.typing import InferenceResult
|
24 | 24 |
|
25 | 25 | from pylint.checkers import BaseChecker, utils
|
26 | 26 | from pylint.checkers.utils import (
|
27 | 27 | in_type_checking_block,
|
28 | 28 | is_postponed_evaluation_enabled,
|
29 | 29 | )
|
30 |
| -from pylint.constants import PY39_PLUS, TYPING_TYPE_CHECKS_GUARDS |
| 30 | +from pylint.constants import ( |
| 31 | + PY39_PLUS, |
| 32 | + TYPING_NEVER, |
| 33 | + TYPING_NORETURN, |
| 34 | + TYPING_TYPE_CHECKS_GUARDS, |
| 35 | +) |
31 | 36 | from pylint.interfaces import CONTROL_FLOW, HIGH, INFERENCE, INFERENCE_FAILURE
|
32 | 37 | from pylint.typing import MessageDefinitionTuple
|
33 | 38 |
|
@@ -2245,13 +2250,35 @@ def _loopvar_name(self, node: astroid.Name) -> None:
|
2245 | 2250 | if not isinstance(assign, nodes.For):
|
2246 | 2251 | self.add_message("undefined-loop-variable", args=node.name, node=node)
|
2247 | 2252 | return
|
2248 |
| - if any( |
2249 |
| - isinstance( |
| 2253 | + for else_stmt in assign.orelse: |
| 2254 | + if isinstance( |
2250 | 2255 | else_stmt, (nodes.Return, nodes.Raise, nodes.Break, nodes.Continue)
|
2251 |
| - ) |
2252 |
| - for else_stmt in assign.orelse |
2253 |
| - ): |
2254 |
| - return |
| 2256 | + ): |
| 2257 | + return |
| 2258 | + # TODO: 2.16: Consider using RefactoringChecker._is_function_def_never_returning |
| 2259 | + if isinstance(else_stmt, nodes.Expr) and isinstance( |
| 2260 | + else_stmt.value, nodes.Call |
| 2261 | + ): |
| 2262 | + inferred_func = utils.safe_infer(else_stmt.value.func) |
| 2263 | + if ( |
| 2264 | + isinstance(inferred_func, nodes.FunctionDef) |
| 2265 | + and inferred_func.returns |
| 2266 | + ): |
| 2267 | + inferred_return = utils.safe_infer(inferred_func.returns) |
| 2268 | + if isinstance( |
| 2269 | + inferred_return, nodes.FunctionDef |
| 2270 | + ) and inferred_return.qname() in { |
| 2271 | + *TYPING_NORETURN, |
| 2272 | + *TYPING_NEVER, |
| 2273 | + "typing._SpecialForm", |
| 2274 | + }: |
| 2275 | + return |
| 2276 | + # typing_extensions.NoReturn returns a _SpecialForm |
| 2277 | + if ( |
| 2278 | + isinstance(inferred_return, bases.Instance) |
| 2279 | + and inferred_return.qname() == "typing._SpecialForm" |
| 2280 | + ): |
| 2281 | + return |
2255 | 2282 |
|
2256 | 2283 | maybe_walrus = utils.get_node_first_ancestor_of_type(node, nodes.NamedExpr)
|
2257 | 2284 | if maybe_walrus:
|
|
0 commit comments