From 35254c29eb3a0f1c7847cfcb3451501a4180373d Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Mon, 13 Dec 2021 12:02:46 -0500 Subject: Fix false-positive 'used-before-assignment' for assignments in except blocks following try blocks that return (#5506) --- ChangeLog | 12 +++++++++--- doc/whatsnew/2.13.rst | 12 +++++++++--- pylint/checkers/variables.py | 22 +++++++++++++++++++++- ...ssignment_except_handler_for_try_with_return.py | 15 +++++++++++++++ ...signment_except_handler_for_try_with_return.txt | 1 + 5 files changed, 55 insertions(+), 7 deletions(-) create mode 100644 tests/functional/u/use/used_before_assignment_except_handler_for_try_with_return.py create mode 100644 tests/functional/u/use/used_before_assignment_except_handler_for_try_with_return.txt diff --git a/ChangeLog b/ChangeLog index a90ade1cc..ae0c1d6e2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -11,15 +11,21 @@ Release date: TBA .. Put new features here and also in 'doc/whatsnew/2.13.rst' +* ``used-before-assignment`` now considers that assignments in a try block + may not have occurred when the except or finally blocks are executed. + + Closes #85, #2615 + * ``used-before-assignment`` now assumes that assignments in except blocks may not have occurred and warns accordingly. Closes #4761 -* ``used-before-assignment`` now considers that assignments in a try block - may not have occurred when the except or finally blocks are executed. +* When evaluating statements after an except block, ``used-before-assignment`` + assumes that assignments in the except blocks took place if the + corresponding try block contained a return statement. - Closes #85, #2615 + Closes #5500 * ``used-before-assignment`` now checks names in try blocks. diff --git a/doc/whatsnew/2.13.rst b/doc/whatsnew/2.13.rst index b3372278d..8555108c1 100644 --- a/doc/whatsnew/2.13.rst +++ b/doc/whatsnew/2.13.rst @@ -43,15 +43,21 @@ Other Changes Closes #5323 +* ``used-before-assignment`` now considers that assignments in a try block + may not have occurred when the except or finally blocks are executed. + + Closes #85, #2615 + * ``used-before-assignment`` now assumes that assignments in except blocks may not have occurred and warns accordingly. Closes #4761 -* ``used-before-assignment`` now considers that assignments in a try block - may not have occurred when the except or finally blocks are executed. +* When evaluating statements after an except block, ``used-before-assignment`` + assumes that assignments in the except blocks took place if the + corresponding try block contained a return statement. - Closes #85, #2615 + Closes #5500 * ``used-before-assignment`` now checks names in try blocks. diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index 0a4a21c18..86b7eb669 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -408,7 +408,8 @@ MSGS = { "Emitted when a local variable is accessed before its assignment took place. " "Assignments in try blocks are assumed not to have occurred when evaluating " "associated except/finally blocks. Assignments in except blocks are assumed " - "not to have occurred when evaluating statements outside the block.", + "not to have occurred when evaluating statements outside the block, except " + "when the associated try block contains a return statement.", ), "E0602": ( "Undefined variable %r", @@ -656,6 +657,25 @@ scope_type : {self._atomic.scope_type} and isinstance( n.statement(future=True).parent.parent, nodes.TryExcept ) + # If the try block returns we assume that assignments in the except + # handlers could have happened. + and ( + not any( + isinstance(try_statement, nodes.Return) + for try_statement in n.statement( + future=True + ).parent.parent.body + ) + # But not if this node is in the final block, which will + # execute before the return. + or ( + isinstance(node_statement.parent, nodes.TryFinally) + and node_statement in node_statement.parent.finalbody + and n.statement(future=True).parent.parent.parent.parent_of( + node_statement + ) + ) + ) ) or n.statement(future=True).parent.parent_of(node) ] diff --git a/tests/functional/u/use/used_before_assignment_except_handler_for_try_with_return.py b/tests/functional/u/use/used_before_assignment_except_handler_for_try_with_return.py new file mode 100644 index 000000000..d976a46b2 --- /dev/null +++ b/tests/functional/u/use/used_before_assignment_except_handler_for_try_with_return.py @@ -0,0 +1,15 @@ +"""Tests for used-before-assignment with assignments in except handlers after +try blocks with return statements. +See: https://github.com/PyCQA/pylint/issues/5500. +""" +def function(): + """Assume except blocks execute if the try block returns.""" + try: + success_message = "success message" + return success_message + except ValueError: + failure_message = "failure message" + finally: + print(failure_message) # [used-before-assignment] + + return failure_message diff --git a/tests/functional/u/use/used_before_assignment_except_handler_for_try_with_return.txt b/tests/functional/u/use/used_before_assignment_except_handler_for_try_with_return.txt new file mode 100644 index 000000000..e5d8d3de8 --- /dev/null +++ b/tests/functional/u/use/used_before_assignment_except_handler_for_try_with_return.txt @@ -0,0 +1 @@ +used-before-assignment:13:14:13:29:function:Using variable 'failure_message' before assignment:UNDEFINED -- cgit v1.2.1