diff options
author | Jacob Walls <jacobtylerwalls@gmail.com> | 2023-04-16 20:50:34 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-16 20:50:34 -0400 |
commit | f45bf090a8e20c9fb09d61ed67d7f885ad354f85 (patch) | |
tree | dc3b4375c7599d252b2ac7aae7e2d7ef53748da7 /pylint/checkers/variables.py | |
parent | 4a485e28f0a5118b37550123c79f1f6d0dec42a4 (diff) | |
download | pylint-git-f45bf090a8e20c9fb09d61ed67d7f885ad354f85.tar.gz |
Fix FP `used-before-assignment` for statements guarded under same test (#8581)
Diffstat (limited to 'pylint/checkers/variables.py')
-rw-r--r-- | pylint/checkers/variables.py | 35 |
1 files changed, 35 insertions, 0 deletions
diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index d82b5a8c8..8c9f6b6ae 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -811,6 +811,9 @@ scope_type : {self._atomic.scope_type} continue outer_if = all_if[-1] + if NamesConsumer._node_guarded_by_same_test(node, outer_if): + continue + # Name defined in the if/else control flow if NamesConsumer._inferred_to_define_name_raise_or_return(name, outer_if): continue @@ -820,6 +823,38 @@ scope_type : {self._atomic.scope_type} return uncertain_nodes @staticmethod + def _node_guarded_by_same_test(node: nodes.NodeNG, other_if: nodes.If) -> bool: + """Identify if `node` is guarded by an equivalent test as `other_if`. + + Two tests are equivalent if their string representations are identical + or if their inferred values consist only of constants and those constants + are identical, and the if test guarding `node` is not a Name. + """ + other_if_test_as_string = other_if.test.as_string() + other_if_test_all_inferred = utils.infer_all(other_if.test) + for ancestor in node.node_ancestors(): + if not isinstance(ancestor, nodes.If): + continue + if ancestor.test.as_string() == other_if_test_as_string: + return True + if isinstance(ancestor.test, nodes.Name): + continue + all_inferred = utils.infer_all(ancestor.test) + if len(all_inferred) == len(other_if_test_all_inferred): + if any( + not isinstance(test, nodes.Const) + for test in (*all_inferred, *other_if_test_all_inferred) + ): + continue + if {test.value for test in all_inferred} != { + test.value for test in other_if_test_all_inferred + }: + continue + return True + + return False + + @staticmethod def _uncertain_nodes_in_except_blocks( found_nodes: list[nodes.NodeNG], node: nodes.NodeNG, |