Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix used-before-assignment false positive for walrus operators in ifs #8029

Merged
merged 8 commits into from
Jan 8, 2023
16 changes: 12 additions & 4 deletions pylint/checkers/variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -2246,10 +2246,18 @@ 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
Pierre-Sassoulas marked this conversation as resolved.
Show resolved Hide resolved
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 isinstance(value, nodes.Call):
for call in value.nodes_of_class(klass=nodes.Call):
if (
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)
)
):
return True
return False
Pierre-Sassoulas marked this conversation as resolved.
Show resolved Hide resolved

def _is_only_type_assignment(
self, node: nodes.Name, defstmt: nodes.Statement
Expand Down
41 changes: 41 additions & 0 deletions tests/functional/u/used/used_before_assignment_ternary.py
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions tests/functional/u/used/used_before_assignment_ternary.txt
Original file line number Diff line number Diff line change
@@ -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