Skip to content

Commit

Permalink
Fix used-before-assignment false positive for walrus operators in i…
Browse files Browse the repository at this point in the history
…fs (#8029) (#8033)

Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
(cherry picked from commit 6ac908f)

Co-authored-by: Zen Lee <53538590+zenlyj@users.noreply.github.com>
  • Loading branch information
github-actions[bot] and zenlyj committed Jan 8, 2023
1 parent 1673aa6 commit 0741949
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 3 deletions.
4 changes: 4 additions & 0 deletions doc/whatsnew/fragments/7779.false_positive
@@ -0,0 +1,4 @@
Fixes ``used-before-assignment`` false positive when the walrus operator
is used in a ternary operator.

Closes #7779
13 changes: 10 additions & 3 deletions pylint/checkers/variables.py
Expand Up @@ -2055,9 +2055,16 @@ def _maybe_used_and_assigned_at_once(defstmt: nodes.Statement) -> bool:
return True
if isinstance(value, nodes.Lambda) and isinstance(value.body, nodes.IfExp):
return True
return isinstance(value, nodes.Call) and (
any(isinstance(kwarg.value, nodes.IfExp) for kwarg in value.keywords)
or any(isinstance(arg, nodes.IfExp) for arg in value.args)
if not isinstance(value, nodes.Call):
return False
return any(
any(isinstance(kwarg.value, nodes.IfExp) for kwarg in call.keywords)
or any(isinstance(arg, nodes.IfExp) for arg in call.args)
or (
isinstance(call.func, nodes.Attribute)
and isinstance(call.func.expr, nodes.IfExp)
)
for call in value.nodes_of_class(klass=nodes.Call)
)

def _is_only_type_assignment(
Expand Down
41 changes: 41 additions & 0 deletions tests/functional/u/used/used_before_assignment_ternary.py
@@ -0,0 +1,41 @@
"""Tests for used-before-assignment false positive from ternary expression with walrus operator"""
# pylint: disable=unnecessary-lambda-assignment, unused-variable, disallowed-name, invalid-name

def invalid():
"""invalid cases that will trigger used-before-assignment"""
var = foo(a, '', '') # [used-before-assignment]
print(str(1 if (a:=-1) else 0))
var = bar(b) # [used-before-assignment]
var = c*c # [used-before-assignment]
var = 1 if (b:=-1) else 0
var = 1 if (c:=-1) else 0

def attribute_call_valid():
"""assignment with attribute calls"""
var = (a if (a:='a') else '').lower()
var = ('' if (b:='b') else b).lower()
var = (c if (c:='c') else c).upper().lower().replace('', '').strip()
var = ''.strip().replace('', '' + (e if (e:='e') else '').lower())

def function_call_arg_valid():
"""assignment as function call arguments"""
var = str(a if (a:='a') else '')
var = str('' if (b:='b') else b)
var = foo(1, c if (c:=1) else 0, 1)
print(foo('', '', foo('', str(int(d if (d:='1') else '')), '')))

def function_call_keyword_valid():
"""assignment as function call keywords"""
var = foo(x=a if (a:='1') else '', y='', z='')
var = foo(x='', y=foo(x='', y='', z=b if (b:='1') else ''), z='')

def complex_valid():
"""assignment within complex call expression"""
var = str(bar(bar(a if (a:=1) else 0))).lower().upper()
print(foo(x=foo(''.replace('', str(b if (b:=1) else 0).upper()), '', z=''), y='', z=''))

def foo(x, y, z):
"""helper function for tests"""
return x+y+z

bar = lambda x : x
2 changes: 2 additions & 0 deletions tests/functional/u/used/used_before_assignment_ternary.rc
@@ -0,0 +1,2 @@
[testoptions]
min_pyver=3.8
3 changes: 3 additions & 0 deletions tests/functional/u/used/used_before_assignment_ternary.txt
@@ -0,0 +1,3 @@
used-before-assignment:6:14:6:15:invalid:Using variable 'a' before assignment:HIGH
used-before-assignment:8:14:8:15:invalid:Using variable 'b' before assignment:HIGH
used-before-assignment:9:10:9:11:invalid:Using variable 'c' before assignment:HIGH

0 comments on commit 0741949

Please sign in to comment.