Skip to content

Commit

Permalink
Fix #85: Emit used-before-assignment in final blocks where try stat…
Browse files Browse the repository at this point in the history
…ements could have failed
  • Loading branch information
jacobtylerwalls committed Nov 24, 2021
1 parent be149db commit df5c775
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 0 deletions.
6 changes: 6 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ Release date: TBA
..
Put new features here and also in 'doc/whatsnew/2.12.rst'

* ``used-before-assignment`` now considers that assignments in the try block
of a try/finally clause may not have occurred when the final block is
executed.

Closes #85

* Upgrade astroid to 2.9.0

Closes #4982
Expand Down
6 changes: 6 additions & 0 deletions doc/whatsnew/2.12.rst
Original file line number Diff line number Diff line change
Expand Up @@ -229,3 +229,9 @@ Other Changes
``pylint/testutil/`` are still unstable and might be modified in the near future.

Closes #4412 #5287

* ``used-before-assignment`` now considers that assignments in the try block
of a try/finally clause may not have occurred when the final block is
executed.

Closes #85
21 changes: 21 additions & 0 deletions pylint/checkers/variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,8 @@ def mark_as_consumed(self, name, consumed_nodes):
If all of the nodes for the name were consumed, delete the name from
the to_consume dictionary
"""
if name not in self.to_consume:
return
unconsumed = [n for n in self.to_consume[name] if n not in set(consumed_nodes)]
self.consumed[name] = consumed_nodes

Expand Down Expand Up @@ -604,6 +606,25 @@ def get_next_to_consume(self, node):
or n.statement().parent_of(node)
]

# If this node is in a Finally block of a Try/Finally,
# filter out assignments in the try portion, assuming they may fail
if (
found_nodes
and isinstance(node.statement().parent, nodes.TryFinally)
and node.statement() in node.statement().parent.finalbody
):
filtered_nodes = [
n
for n in found_nodes
if not (
isinstance(n.statement().parent, nodes.TryFinally)
and n.statement() in n.statement().parent.body
)
]
if not filtered_nodes:
self.mark_as_consumed(name, found_nodes)
return filtered_nodes

return found_nodes


Expand Down
8 changes: 8 additions & 0 deletions tests/functional/u/use/used_before_assignment_issue85.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# pylint: disable=missing-module-docstring, missing-function-docstring
def main():
try:
# throw exception here to make res undefined
res = 1 / 0
res = 42
finally:
print(res) # [used-before-assignment]
1 change: 1 addition & 0 deletions tests/functional/u/use/used_before_assignment_issue85.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
used-before-assignment:8:14:8:17:main:Using variable 'res' before assignment:UNDEFINED

0 comments on commit df5c775

Please sign in to comment.