pylint
pylint copied to clipboard
`possibly-used-before-assignment` does not handle `match` in nested `if`
Bug description
# pylint_used_before_assignment_nested_match.py
from enum import Enum
from typing import assert_never
class Example(Enum):
FOO = 1
BAR = 2
def check_value_only_match(example: Example) -> str | None:
match example:
case Example.FOO:
result = "foo"
case Example.BAR:
result = "bar"
case _:
assert_never(example)
return result
def check_value_if_then_if(example: Example, should_check: bool) -> str | None:
if should_check:
result = None
else:
if example == Example.FOO:
result = "foo"
elif example == Example.BAR:
result = "bar"
else:
assert_never(example)
return result
def check_value_if_then_match_return(example: Example, should_check: bool) -> str | None:
if should_check:
result = None
else:
match example:
case Example.FOO:
result = "foo"
case Example.BAR:
result = "bar"
case _:
return None
return result
def check_value_if_then_match_raise(example: Example, should_check: bool) -> str | None:
if should_check:
result = None
else:
match example:
case Example.FOO:
result = "foo"
case Example.BAR:
result = "bar"
case _:
raise ValueError("Not a valid enum")
return result
def check_value_if_then_match_assert_never(
example: Example, should_check: bool
) -> str | None:
if should_check:
result = None
else:
match example:
case Example.FOO:
result = "foo"
case Example.BAR:
result = "bar"
case _:
assert_never(example)
return result
Configuration
No response
Command used
% ./venv/bin/pylint --disable=all --enable=possibly-used-before-assignment pylint_used_before_assignment_nested_match.py
Pylint output
pylint_used_before_assignment_nested_match.py:50:11: E0606: Possibly using variable 'result' before assignment (possibly-used-before-assignment)
pylint_used_before_assignment_nested_match.py:65:11: E0606: Possibly using variable 'result' before assignment (possibly-used-before-assignment)
pylint_used_before_assignment_nested_match.py:82:11: E0606: Possibly using variable 'result' before assignment (possibly-used-before-assignment)
------------------------------------------------------------------
Your code has been rated at 6.59/10 (previous run: 7.22/10, -0.63)
Expected behavior
The first two functions are correctly handled by pylint, i.e.
- a regular
matchworks - two nested
ifs work
I though this was a variant of https://github.com/pylint-dev/pylint/issues/9643 at first, with match not properly supported, but it's apparently a bit more complicated: it seems pylint has trouble understanding that the match nested under the first if is also "exhaustive". Note that this is triggered whatever "exit" type is used in the case _: fallthrough (return, raise or assert_never)
Pylint version
pylint 3.2.2
astroid 3.2.2
Python 3.11.2 (main, Feb 28 2023, 10:36:52) [GCC 12.2.0]
OS / Environment
% lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description: Debian GNU/Linux trixie/sid
Release: n/a
Codename: trixie
Additional dependencies
No response
I ran into the same issue after upgrading to pylint: in pylint 3.1.x the problem doesn't occur and since 3.2.0 it does.
Here is a more minimal test case: (although it's actually pretty similar to the fourth test case above)
def f(x):
if x is None:
y = 0
else:
match x:
case int():
y = x
case _:
raise TypeError(type(x))
return y
Output:
testcase.py:10:11: E0606: Possibly using variable 'y' before assignment (possibly-used-before-assignment)
Versions used:
pylint 3.2.3
astroid 3.2.2
Python 3.11.9