Skip to content

Commit df5c775

Browse files
Fix pylint-dev#85: Emit used-before-assignment in final blocks where try statements could have failed
1 parent be149db commit df5c775

File tree

5 files changed

+42
-0
lines changed

5 files changed

+42
-0
lines changed

ChangeLog

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ Release date: TBA
1111
..
1212
Put new features here and also in 'doc/whatsnew/2.12.rst'
1313

14+
* ``used-before-assignment`` now considers that assignments in the try block
15+
of a try/finally clause may not have occurred when the final block is
16+
executed.
17+
18+
Closes #85
19+
1420
* Upgrade astroid to 2.9.0
1521

1622
Closes #4982

doc/whatsnew/2.12.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,3 +229,9 @@ Other Changes
229229
``pylint/testutil/`` are still unstable and might be modified in the near future.
230230

231231
Closes #4412 #5287
232+
233+
* ``used-before-assignment`` now considers that assignments in the try block
234+
of a try/finally clause may not have occurred when the final block is
235+
executed.
236+
237+
Closes #85

pylint/checkers/variables.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,8 @@ def mark_as_consumed(self, name, consumed_nodes):
562562
If all of the nodes for the name were consumed, delete the name from
563563
the to_consume dictionary
564564
"""
565+
if name not in self.to_consume:
566+
return
565567
unconsumed = [n for n in self.to_consume[name] if n not in set(consumed_nodes)]
566568
self.consumed[name] = consumed_nodes
567569

@@ -604,6 +606,25 @@ def get_next_to_consume(self, node):
604606
or n.statement().parent_of(node)
605607
]
606608

609+
# If this node is in a Finally block of a Try/Finally,
610+
# filter out assignments in the try portion, assuming they may fail
611+
if (
612+
found_nodes
613+
and isinstance(node.statement().parent, nodes.TryFinally)
614+
and node.statement() in node.statement().parent.finalbody
615+
):
616+
filtered_nodes = [
617+
n
618+
for n in found_nodes
619+
if not (
620+
isinstance(n.statement().parent, nodes.TryFinally)
621+
and n.statement() in n.statement().parent.body
622+
)
623+
]
624+
if not filtered_nodes:
625+
self.mark_as_consumed(name, found_nodes)
626+
return filtered_nodes
627+
607628
return found_nodes
608629

609630

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# pylint: disable=missing-module-docstring, missing-function-docstring
2+
def main():
3+
try:
4+
# throw exception here to make res undefined
5+
res = 1 / 0
6+
res = 42
7+
finally:
8+
print(res) # [used-before-assignment]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
used-before-assignment:8:14:8:17:main:Using variable 'res' before assignment:UNDEFINED

0 commit comments

Comments
 (0)