Skip to content

Commit

Permalink
Fix a false positive for undefined-loop-variable when a loop else r…
Browse files Browse the repository at this point in the history
…aises or returns (#6480)

Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
  • Loading branch information
jacobtylerwalls and Pierre-Sassoulas committed May 2, 2022
1 parent 8450b0c commit deff919
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 1 deletion.
5 changes: 5 additions & 0 deletions ChangeLog
Expand Up @@ -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.

Expand Down
5 changes: 5 additions & 0 deletions doc/whatsnew/2.13.rst
Expand Up @@ -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.

Expand Down
8 changes: 7 additions & 1 deletion pylint/checkers/variables.py
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down
16 changes: 16 additions & 0 deletions tests/functional/u/undefined/undefined_loop_variable.py
Expand Up @@ -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]

Expand Down

0 comments on commit deff919

Please sign in to comment.