mypy icon indicating copy to clipboard operation
mypy copied to clipboard

`possibly-undefined` false positive when assigning variable for every case in match statement for all possible values

Open DetachHead opened this issue 1 year ago • 1 comments

# mypy: enable-error-code=possibly-undefined

from typing import Literal

value: Literal[1, 2]

match value:
    case 1:
        result = 1
    case 2:
        result = 2

result # error: possibly-undefined

playground

similar issue: #15958 (seems it was only fixed when using assert_never at the end)

DetachHead avatar Mar 06 '24 05:03 DetachHead

Interesting that adding case _ kind of resolves the issue though mypy does detect that case _ code is unreachable.

# mypy: enable-error-code=possibly-undefined

from typing import Literal

value: Literal[1, 2]

match value:
    case 1:
        result = 1
    case 2:
        result = 2
    case _:
        # error: Statement is unreachable  [unreachable]
        result = 3

result

I've also met the similar issue with if isinstance checks. In the last case adding redudant else block makes the case false negative.

# mypy: enable-error-code="possibly-undefined"

class A: ...
class B: ...

# False positive: Name "new_name" may be undefined  [possibly-undefined]
def test_1(obj: A | B) -> None:
    if isinstance(obj, A):
        new_name = "test"
    elif isinstance(obj, B):
        new_name = "test"
    a = new_name

# + adding "else: reveal_type(obj)" somehow resolves it
def test_1b(obj: A | B) -> None:
    if isinstance(obj, A):
        new_name = "test"
    elif isinstance(obj, B):
        new_name = "test"
    else:
        reveal_type(obj)
    a = new_name

# False positive: Name "new_name" may be undefined  [possibly-undefined]
def get_obj() -> A | B: ...
def test_2() -> None:
    obj = get_obj()
    if isinstance(obj, A):
        new_name = "test"
    elif isinstance(obj, B):
        new_name = "test"
    a = new_name
# but adding "else: reveal_type(obj)" somehow resolves it
def test_2b() -> None:
    obj = get_obj()
    if isinstance(obj, A):
        new_name = "test"
    elif isinstance(obj, B):
        new_name = "test"
    else:
        reveal_type(obj)
    a = new_name

# Name "new_name" may be undefined  [possibly-undefined]
# and it's correct
def test_3(obj: str | float) -> None:
    if isinstance(obj, str):
        new_name = "test"
    elif isinstance(obj, float):
        new_name = "test"
    a = new_name

# but adding "else: reveal_type(obj)" makes it false negative
def test_3b(obj: str | float) -> None:
    if isinstance(obj, str):
        new_name = "test"
    elif isinstance(obj, float):
        new_name = "test"
    else:
        reveal_type(obj)
    a = new_name

Andrej730 avatar Jun 27 '24 07:06 Andrej730