diff --git a/ChangeLog b/ChangeLog index c52bf9033c..7c630d6314 100644 --- a/ChangeLog +++ b/ChangeLog @@ -34,6 +34,11 @@ Release date: TBA Closes #5930 +* Fix a false positive for ``undefined-loop-variable`` when the ``else`` of a ``for`` + loop raises or returns. + + Closes #5971 + * Fix false positive for ``unused-variable`` for classes inside functions and where a metaclass is provided via a call. diff --git a/doc/whatsnew/2.13.rst b/doc/whatsnew/2.13.rst index 1d86a6fd91..5f3c866736 100644 --- a/doc/whatsnew/2.13.rst +++ b/doc/whatsnew/2.13.rst @@ -607,6 +607,11 @@ Other Changes Closes #5769 +* Fix a false positive for ``undefined-loop-variable`` when the ``else`` of a ``for`` + loop raises or returns. + + Closes #5971 + * Fix false positive for ``unused-variable`` for classes inside functions and where a metaclass is provided via a call. diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index 2e4f4d70fe..807e031e60 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -2207,6 +2207,7 @@ def _loopvar_name(self, node: astroid.Name) -> None: scope = node.scope() # FunctionDef subclasses Lambda due to a curious ontology. Check both. # See https://github.com/PyCQA/astroid/issues/291 + # pylint: disable-next=fixme # TODO: Revisit when astroid 3.0 includes the change if isinstance(scope, nodes.Lambda) and any( asmt.scope().parent_of(scope) for asmt in astmts @@ -2252,11 +2253,16 @@ def _loopvar_name(self, node: astroid.Name) -> None: ): return - # For functions we can do more by inferring the length of the itered object if not isinstance(assign, nodes.For): self.add_message("undefined-loop-variable", args=node.name, node=node) return + if any( + isinstance(else_stmt, (nodes.Return, nodes.Raise)) + for else_stmt in assign.orelse + ): + return + # For functions we can do more by inferring the length of the itered object try: inferred = next(assign.iter.infer()) except astroid.InferenceError: diff --git a/tests/functional/u/undefined/undefined_loop_variable.py b/tests/functional/u/undefined/undefined_loop_variable.py index 0270c6b532..6bcf6103d9 100644 --- a/tests/functional/u/undefined/undefined_loop_variable.py +++ b/tests/functional/u/undefined/undefined_loop_variable.py @@ -91,6 +91,22 @@ def handle_line(layne): handle_line(layne) +def for_else_returns(iterable): + for thing in iterable: + break + else: + return + print(thing) + + +def for_else_raises(iterable): + for thing in iterable: + break + else: + raise Exception + print(thing) + + lst = [] lst2 = [1, 2, 3]