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

possibly-used-before-assignment does not handle match in nested if #9668

Open
noirbee opened this issue May 22, 2024 · 1 comment
Open

possibly-used-before-assignment does not handle match in nested if #9668

noirbee opened this issue May 22, 2024 · 1 comment
Labels
C: used-before-assignment Issues related to 'used-before-assignment' check False Positive 🦟 A message is emitted but nothing is wrong with the code Needs PR This issue is accepted, sufficiently specified and now needs an implementation

Comments

@noirbee
Copy link

noirbee commented May 22, 2024

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 match works
  • two nested ifs work

I though this was a variant of #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

@noirbee noirbee added the Needs triage 📥 Just created, needs acknowledgment, triage, and proper labelling label May 22, 2024
@jacobtylerwalls jacobtylerwalls added False Positive 🦟 A message is emitted but nothing is wrong with the code C: used-before-assignment Issues related to 'used-before-assignment' check Needs PR This issue is accepted, sufficiently specified and now needs an implementation and removed Needs triage 📥 Just created, needs acknowledgment, triage, and proper labelling labels May 22, 2024
@mthuurne
Copy link
Contributor

mthuurne commented Jun 16, 2024

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

nickdrozd added a commit to nickdrozd/pylint that referenced this issue Sep 28, 2024
@nickdrozd nickdrozd mentioned this issue Sep 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C: used-before-assignment Issues related to 'used-before-assignment' check False Positive 🦟 A message is emitted but nothing is wrong with the code Needs PR This issue is accepted, sufficiently specified and now needs an implementation
Projects
None yet
Development

No branches or pull requests

3 participants