Skip to content

Commit 0741949

Browse files
Fix used-before-assignment false positive for walrus operators in ifs (#8029) (#8033)
Co-authored-by: Pierre Sassoulas <[email protected]> (cherry picked from commit 6ac908f) Co-authored-by: Zen Lee <[email protected]>
1 parent 1673aa6 commit 0741949

File tree

5 files changed

+60
-3
lines changed

5 files changed

+60
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fixes ``used-before-assignment`` false positive when the walrus operator
2+
is used in a ternary operator.
3+
4+
Closes #7779

pylint/checkers/variables.py

+10-3
Original file line numberDiff line numberDiff line change
@@ -2055,9 +2055,16 @@ def _maybe_used_and_assigned_at_once(defstmt: nodes.Statement) -> bool:
20552055
return True
20562056
if isinstance(value, nodes.Lambda) and isinstance(value.body, nodes.IfExp):
20572057
return True
2058-
return isinstance(value, nodes.Call) and (
2059-
any(isinstance(kwarg.value, nodes.IfExp) for kwarg in value.keywords)
2060-
or any(isinstance(arg, nodes.IfExp) for arg in value.args)
2058+
if not isinstance(value, nodes.Call):
2059+
return False
2060+
return any(
2061+
any(isinstance(kwarg.value, nodes.IfExp) for kwarg in call.keywords)
2062+
or any(isinstance(arg, nodes.IfExp) for arg in call.args)
2063+
or (
2064+
isinstance(call.func, nodes.Attribute)
2065+
and isinstance(call.func.expr, nodes.IfExp)
2066+
)
2067+
for call in value.nodes_of_class(klass=nodes.Call)
20612068
)
20622069

20632070
def _is_only_type_assignment(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
"""Tests for used-before-assignment false positive from ternary expression with walrus operator"""
2+
# pylint: disable=unnecessary-lambda-assignment, unused-variable, disallowed-name, invalid-name
3+
4+
def invalid():
5+
"""invalid cases that will trigger used-before-assignment"""
6+
var = foo(a, '', '') # [used-before-assignment]
7+
print(str(1 if (a:=-1) else 0))
8+
var = bar(b) # [used-before-assignment]
9+
var = c*c # [used-before-assignment]
10+
var = 1 if (b:=-1) else 0
11+
var = 1 if (c:=-1) else 0
12+
13+
def attribute_call_valid():
14+
"""assignment with attribute calls"""
15+
var = (a if (a:='a') else '').lower()
16+
var = ('' if (b:='b') else b).lower()
17+
var = (c if (c:='c') else c).upper().lower().replace('', '').strip()
18+
var = ''.strip().replace('', '' + (e if (e:='e') else '').lower())
19+
20+
def function_call_arg_valid():
21+
"""assignment as function call arguments"""
22+
var = str(a if (a:='a') else '')
23+
var = str('' if (b:='b') else b)
24+
var = foo(1, c if (c:=1) else 0, 1)
25+
print(foo('', '', foo('', str(int(d if (d:='1') else '')), '')))
26+
27+
def function_call_keyword_valid():
28+
"""assignment as function call keywords"""
29+
var = foo(x=a if (a:='1') else '', y='', z='')
30+
var = foo(x='', y=foo(x='', y='', z=b if (b:='1') else ''), z='')
31+
32+
def complex_valid():
33+
"""assignment within complex call expression"""
34+
var = str(bar(bar(a if (a:=1) else 0))).lower().upper()
35+
print(foo(x=foo(''.replace('', str(b if (b:=1) else 0).upper()), '', z=''), y='', z=''))
36+
37+
def foo(x, y, z):
38+
"""helper function for tests"""
39+
return x+y+z
40+
41+
bar = lambda x : x
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[testoptions]
2+
min_pyver=3.8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
used-before-assignment:6:14:6:15:invalid:Using variable 'a' before assignment:HIGH
2+
used-before-assignment:8:14:8:15:invalid:Using variable 'b' before assignment:HIGH
3+
used-before-assignment:9:10:9:11:invalid:Using variable 'c' before assignment:HIGH

0 commit comments

Comments
 (0)