From a83e6b9e32379220f0061574baca30098b025b44 Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Thu, 22 Feb 2024 08:57:33 -0500 Subject: [PATCH] [used-before-assignment] Fix FP under finally block (#9452) Closes #9451 --- doc/whatsnew/fragments/9451.false_positive | 4 ++++ pylint/checkers/variables.py | 12 ++++++++++-- .../u/used/used_before_assignment_issue85.py | 10 ++++++++++ .../u/used/used_before_assignment_issue85.txt | 16 ++++++++-------- 4 files changed, 32 insertions(+), 10 deletions(-) create mode 100644 doc/whatsnew/fragments/9451.false_positive diff --git a/doc/whatsnew/fragments/9451.false_positive b/doc/whatsnew/fragments/9451.false_positive new file mode 100644 index 0000000000..ea2045f91d --- /dev/null +++ b/doc/whatsnew/fragments/9451.false_positive @@ -0,0 +1,4 @@ +Fix false positive for ``used-before-assignment`` in a ``finally`` block +when assignments took place in both the ``try`` block and each exception handler. + +Closes #9451 diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index bc20d1987e..e38bec03e3 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -667,7 +667,7 @@ def get_next_to_consume(self, node: nodes.Name) -> list[nodes.NodeNG] | None: if found_nodes: uncertain_nodes = ( self._uncertain_nodes_in_try_blocks_when_evaluating_finally_blocks( - found_nodes, node_statement + found_nodes, node_statement, name ) ) self.consumed_uncertain[node.name] += uncertain_nodes @@ -1140,7 +1140,9 @@ def _uncertain_nodes_in_try_blocks_when_evaluating_except_blocks( @staticmethod def _uncertain_nodes_in_try_blocks_when_evaluating_finally_blocks( - found_nodes: list[nodes.NodeNG], node_statement: _base_nodes.Statement + found_nodes: list[nodes.NodeNG], + node_statement: _base_nodes.Statement, + name: str, ) -> list[nodes.NodeNG]: uncertain_nodes: list[nodes.NodeNG] = [] ( @@ -1187,6 +1189,12 @@ def _uncertain_nodes_in_try_blocks_when_evaluating_finally_blocks( ) ): continue + # Is the name defined in all exception clauses? + if other_node_try_finally_ancestor.handlers and all( + NamesConsumer._defines_name_raises_or_returns_recursive(name, handler) + for handler in other_node_try_finally_ancestor.handlers + ): + continue # Passed all tests for uncertain execution uncertain_nodes.append(other_node) return uncertain_nodes diff --git a/tests/functional/u/used/used_before_assignment_issue85.py b/tests/functional/u/used/used_before_assignment_issue85.py index 7ede436bb9..218dad9d00 100644 --- a/tests/functional/u/used/used_before_assignment_issue85.py +++ b/tests/functional/u/used/used_before_assignment_issue85.py @@ -34,6 +34,16 @@ def try_except_finally_assignment_in_final_block(): print(res) +def try_except_finally_assignment_in_both_try_and_except(): + """Assignment of the name in both try and except blocks is fine.""" + try: + res = 1 / 0 + except ZeroDivisionError: + res = 0 + finally: + print(res) + + def try_except_finally_nested_try_finally_in_try(): """Don't confuse assignments in different finally statements where one is nested inside a try. diff --git a/tests/functional/u/used/used_before_assignment_issue85.txt b/tests/functional/u/used/used_before_assignment_issue85.txt index 9f405c2c5c..d7d002d75e 100644 --- a/tests/functional/u/used/used_before_assignment_issue85.txt +++ b/tests/functional/u/used/used_before_assignment_issue85.txt @@ -1,10 +1,10 @@ used-before-assignment:8:14:8:17:main:Using variable 'res' before assignment:CONTROL_FLOW used-before-assignment:20:14:20:17:try_except_finally:Using variable 'res' before assignment:CONTROL_FLOW -used-before-assignment:45:18:45:21:try_except_finally_nested_try_finally_in_try:Using variable 'res' before assignment:HIGH -used-before-assignment:70:18:70:29:try_except_finally_nested_in_finally:Using variable 'outer_times' before assignment:CONTROL_FLOW -used-before-assignment:84:18:84:29:try_except_finally_nested_in_finally_2:Using variable 'inner_times' before assignment:CONTROL_FLOW -used-before-assignment:85:14:85:25:try_except_finally_nested_in_finally_2:Using variable 'outer_times' before assignment:CONTROL_FLOW -used-before-assignment:100:18:100:29:try_except_finally_nested_in_finally_3:Using variable 'inner_times' before assignment:CONTROL_FLOW -used-before-assignment:101:18:101:29:try_except_finally_nested_in_finally_3:Using variable 'outer_times' before assignment:CONTROL_FLOW -used-before-assignment:122:22:122:33:try_except_finally_nested_in_finally_4:Using variable 'inner_times' before assignment:CONTROL_FLOW -used-before-assignment:123:22:123:33:try_except_finally_nested_in_finally_4:Using variable 'outer_times' before assignment:CONTROL_FLOW +used-before-assignment:55:18:55:21:try_except_finally_nested_try_finally_in_try:Using variable 'res' before assignment:HIGH +used-before-assignment:80:18:80:29:try_except_finally_nested_in_finally:Using variable 'outer_times' before assignment:CONTROL_FLOW +used-before-assignment:94:18:94:29:try_except_finally_nested_in_finally_2:Using variable 'inner_times' before assignment:CONTROL_FLOW +used-before-assignment:95:14:95:25:try_except_finally_nested_in_finally_2:Using variable 'outer_times' before assignment:CONTROL_FLOW +used-before-assignment:110:18:110:29:try_except_finally_nested_in_finally_3:Using variable 'inner_times' before assignment:CONTROL_FLOW +used-before-assignment:111:18:111:29:try_except_finally_nested_in_finally_3:Using variable 'outer_times' before assignment:CONTROL_FLOW +used-before-assignment:132:22:132:33:try_except_finally_nested_in_finally_4:Using variable 'inner_times' before assignment:CONTROL_FLOW +used-before-assignment:133:22:133:33:try_except_finally_nested_in_finally_4:Using variable 'outer_times' before assignment:CONTROL_FLOW