diff options
author | github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> | 2023-02-11 17:34:41 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-11 17:34:41 +0100 |
commit | c2cd7c6afe989f301c7f2a469770933018eaeb92 (patch) | |
tree | efa72a5abed976ea2a044e0249d23256c0125fc7 | |
parent | 4aedc4f9589b49fbb0a598a53d04ad600129cba6 (diff) | |
download | pylint-git-c2cd7c6afe989f301c7f2a469770933018eaeb92.tar.gz |
Fix FP for used-before-assignment with assignment expressions in containers (#8253) (#8262)
(cherry picked from commit a0b28f9019fabddd8ef428be75659082377abb4c)
Co-authored-by: Jacob Walls <jacobtylerwalls@gmail.com>
-rw-r--r-- | doc/whatsnew/fragments/8252.false_positive | 4 | ||||
-rw-r--r-- | pylint/checkers/variables.py | 37 | ||||
-rw-r--r-- | tests/functional/u/undefined/undefined_variable_py38.py | 10 | ||||
-rw-r--r-- | tests/functional/u/undefined/undefined_variable_py38.txt | 5 |
4 files changed, 32 insertions, 24 deletions
diff --git a/doc/whatsnew/fragments/8252.false_positive b/doc/whatsnew/fragments/8252.false_positive new file mode 100644 index 000000000..2c449ac36 --- /dev/null +++ b/doc/whatsnew/fragments/8252.false_positive @@ -0,0 +1,4 @@ +Fix false positive for ``used-before-assignment`` for named expressions +appearing after the first element in a list, tuple, or set. + +Closes #8252 diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index 6df00d01a..76faf5cc8 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -115,6 +115,15 @@ DICT_TYPES = ( astroid.nodes.node_classes.Dict, ) +NODES_WITH_VALUE_ATTR = ( + nodes.Assign, + nodes.AnnAssign, + nodes.AugAssign, + nodes.Expr, + nodes.Return, + nodes.Match, +) + class VariableVisitConsumerAction(Enum): """Reported by _check_consumer() and its sub-methods to determine the @@ -2128,17 +2137,7 @@ class VariablesChecker(BaseChecker): # same line as the function definition maybe_before_assign = False elif ( - isinstance( - defstmt, - ( - nodes.Assign, - nodes.AnnAssign, - nodes.AugAssign, - nodes.Expr, - nodes.Return, - nodes.Match, - ), - ) + isinstance(defstmt, NODES_WITH_VALUE_ATTR) and VariablesChecker._maybe_used_and_assigned_at_once(defstmt) and frame is defframe and defframe.parent_of(node) @@ -2234,13 +2233,15 @@ class VariablesChecker(BaseChecker): """ if isinstance(defstmt, nodes.Match): return any(case.guard for case in defstmt.cases) - if isinstance(defstmt.value, nodes.BaseContainer) and defstmt.value.elts: - # The assignment must happen as part of the first element - # e.g. "assert (x:= True), x" - # NOT "assert x, (x:= True)" - value = defstmt.value.elts[0] - else: - value = defstmt.value + if isinstance(defstmt, nodes.IfExp): + return True + if isinstance(defstmt.value, nodes.BaseContainer): + return any( + VariablesChecker._maybe_used_and_assigned_at_once(elt) + for elt in defstmt.value.elts + if isinstance(elt, NODES_WITH_VALUE_ATTR + (nodes.IfExp, nodes.Match)) + ) + value = defstmt.value if isinstance(value, nodes.IfExp): return True if isinstance(value, nodes.Lambda) and isinstance(value.body, nodes.IfExp): diff --git a/tests/functional/u/undefined/undefined_variable_py38.py b/tests/functional/u/undefined/undefined_variable_py38.py index ef774e53a..6fb543e80 100644 --- a/tests/functional/u/undefined/undefined_variable_py38.py +++ b/tests/functional/u/undefined/undefined_variable_py38.py @@ -173,9 +173,13 @@ def expression_in_ternary_operator_inside_container_tuple(): return [(val3, val3) if (val3 := 'something') else 'anything'] -def expression_in_ternary_operator_inside_container_wrong_position(): - """2-element list where named expression comes too late""" - return [val3, val3 if (val3 := 'something') else 'anything'] # [used-before-assignment] +def expression_in_ternary_operator_inside_container_later_position(): + """ + Named expression follows unrelated item in container. + + If 23 is replaced with `val3`, there is currently a false negative, + but the false positive here is more important and likely to occur.""" + return [23, val3 if (val3 := 'something') else 'anything'] # Self-referencing diff --git a/tests/functional/u/undefined/undefined_variable_py38.txt b/tests/functional/u/undefined/undefined_variable_py38.txt index 832d8dd11..1674707a5 100644 --- a/tests/functional/u/undefined/undefined_variable_py38.txt +++ b/tests/functional/u/undefined/undefined_variable_py38.txt @@ -6,6 +6,5 @@ undefined-variable:83:6:83:19::Undefined variable 'else_assign_1':INFERENCE undefined-variable:106:6:106:19::Undefined variable 'else_assign_2':INFERENCE used-before-assignment:141:10:141:16:type_annotation_used_improperly_after_comprehension:Using variable 'my_int' before assignment:HIGH used-before-assignment:148:10:148:16:type_annotation_used_improperly_after_comprehension_2:Using variable 'my_int' before assignment:HIGH -used-before-assignment:178:12:178:16:expression_in_ternary_operator_inside_container_wrong_position:Using variable 'val3' before assignment:HIGH -used-before-assignment:182:9:182:10::Using variable 'z' before assignment:HIGH -used-before-assignment:189:6:189:19::Using variable 'NEVER_DEFINED' before assignment:CONTROL_FLOW +used-before-assignment:186:9:186:10::Using variable 'z' before assignment:HIGH +used-before-assignment:193:6:193:19::Using variable 'NEVER_DEFINED' before assignment:CONTROL_FLOW |