Skip to content

Commit c2cd7c6

Browse files
Fix FP for used-before-assignment with assignment expressions in containers (#8253) (#8262)
(cherry picked from commit a0b28f9) Co-authored-by: Jacob Walls <[email protected]>
1 parent 4aedc4f commit c2cd7c6

File tree

4 files changed

+32
-24
lines changed

4 files changed

+32
-24
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fix false positive for ``used-before-assignment`` for named expressions
2+
appearing after the first element in a list, tuple, or set.
3+
4+
Closes #8252

pylint/checkers/variables.py

+19-18
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,15 @@
115115
astroid.nodes.node_classes.Dict,
116116
)
117117

118+
NODES_WITH_VALUE_ATTR = (
119+
nodes.Assign,
120+
nodes.AnnAssign,
121+
nodes.AugAssign,
122+
nodes.Expr,
123+
nodes.Return,
124+
nodes.Match,
125+
)
126+
118127

119128
class VariableVisitConsumerAction(Enum):
120129
"""Reported by _check_consumer() and its sub-methods to determine the
@@ -2128,17 +2137,7 @@ def _is_variable_violation(
21282137
# same line as the function definition
21292138
maybe_before_assign = False
21302139
elif (
2131-
isinstance(
2132-
defstmt,
2133-
(
2134-
nodes.Assign,
2135-
nodes.AnnAssign,
2136-
nodes.AugAssign,
2137-
nodes.Expr,
2138-
nodes.Return,
2139-
nodes.Match,
2140-
),
2141-
)
2140+
isinstance(defstmt, NODES_WITH_VALUE_ATTR)
21422141
and VariablesChecker._maybe_used_and_assigned_at_once(defstmt)
21432142
and frame is defframe
21442143
and defframe.parent_of(node)
@@ -2234,13 +2233,15 @@ def _maybe_used_and_assigned_at_once(defstmt: nodes.Statement) -> bool:
22342233
"""
22352234
if isinstance(defstmt, nodes.Match):
22362235
return any(case.guard for case in defstmt.cases)
2237-
if isinstance(defstmt.value, nodes.BaseContainer) and defstmt.value.elts:
2238-
# The assignment must happen as part of the first element
2239-
# e.g. "assert (x:= True), x"
2240-
# NOT "assert x, (x:= True)"
2241-
value = defstmt.value.elts[0]
2242-
else:
2243-
value = defstmt.value
2236+
if isinstance(defstmt, nodes.IfExp):
2237+
return True
2238+
if isinstance(defstmt.value, nodes.BaseContainer):
2239+
return any(
2240+
VariablesChecker._maybe_used_and_assigned_at_once(elt)
2241+
for elt in defstmt.value.elts
2242+
if isinstance(elt, NODES_WITH_VALUE_ATTR + (nodes.IfExp, nodes.Match))
2243+
)
2244+
value = defstmt.value
22442245
if isinstance(value, nodes.IfExp):
22452246
return True
22462247
if isinstance(value, nodes.Lambda) and isinstance(value.body, nodes.IfExp):

tests/functional/u/undefined/undefined_variable_py38.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,13 @@ def expression_in_ternary_operator_inside_container_tuple():
173173
return [(val3, val3) if (val3 := 'something') else 'anything']
174174

175175

176-
def expression_in_ternary_operator_inside_container_wrong_position():
177-
"""2-element list where named expression comes too late"""
178-
return [val3, val3 if (val3 := 'something') else 'anything'] # [used-before-assignment]
176+
def expression_in_ternary_operator_inside_container_later_position():
177+
"""
178+
Named expression follows unrelated item in container.
179+
180+
If 23 is replaced with `val3`, there is currently a false negative,
181+
but the false positive here is more important and likely to occur."""
182+
return [23, val3 if (val3 := 'something') else 'anything']
179183

180184

181185
# Self-referencing

tests/functional/u/undefined/undefined_variable_py38.txt

+2-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,5 @@ undefined-variable:83:6:83:19::Undefined variable 'else_assign_1':INFERENCE
66
undefined-variable:106:6:106:19::Undefined variable 'else_assign_2':INFERENCE
77
used-before-assignment:141:10:141:16:type_annotation_used_improperly_after_comprehension:Using variable 'my_int' before assignment:HIGH
88
used-before-assignment:148:10:148:16:type_annotation_used_improperly_after_comprehension_2:Using variable 'my_int' before assignment:HIGH
9-
used-before-assignment:178:12:178:16:expression_in_ternary_operator_inside_container_wrong_position:Using variable 'val3' before assignment:HIGH
10-
used-before-assignment:182:9:182:10::Using variable 'z' before assignment:HIGH
11-
used-before-assignment:189:6:189:19::Using variable 'NEVER_DEFINED' before assignment:CONTROL_FLOW
9+
used-before-assignment:186:9:186:10::Using variable 'z' before assignment:HIGH
10+
used-before-assignment:193:6:193:19::Using variable 'NEVER_DEFINED' before assignment:CONTROL_FLOW

0 commit comments

Comments
 (0)