pylint-dev/pylint

With Pylint 3.2.4 and Python 3.8 getting incorrect error W0101: Unreachable code (unreachable)

Closed this issue · 5 comments

Bug description

When using pylint 3.2.4 I'm getting "W0101: Unreachable code (unreachable" errors but only for Python 3.8. They do not happen for Python 3.9 to 3.12.

See https://github.com/BittyTax/BittyTax/actions/runs/9677156397/job/26698206145.

Worked fine for pylint 3.2.3, see https://github.com/BittyTax/BittyTax/actions/runs/9667255227/job/26668643763.

Command used

pylint $(git ls-files '*.py')

Pylint output

src/bittytax/price/datasource.py:245:8: W0101: Unreachable code (unreachable)
43
src/bittytax/price/datasource.py:253:8: W0101: Unreachable code (unreachable)
44
src/bittytax/price/datasource.py:268:8: W0101: Unreachable code (unreachable)
45
src/bittytax/price/datasource.py:342:8: W0101: Unreachable code (unreachable)
46
src/bittytax/price/datasource.py:357:8: W0101: Unreachable code (unreachable)
47
src/bittytax/price/datasource.py:384:8: W0101: Unreachable code (unreachable)
48
src/bittytax/price/datasource.py:402:8: W0101: Unreachable code (unreachable)
49
src/bittytax/price/datasource.py:423:8: W0101: Unreachable code (unreachable)
50
src/bittytax/price/datasource.py:438:8: W0101: Unreachable code (unreachable)
51
src/bittytax/price/datasource.py:455:8: W0101: Unreachable code (unreachable)
52
src/bittytax/price/datasource.py:475:8: W0101: Unreachable code (unreachable)
53
src/bittytax/price/datasource.py:495:8: W0101: Unreachable code (unreachable)
54
src/bittytax/price/datasource.py:518:8: W0101: Unreachable code (unreachable)
55
src/bittytax/price/datasource.py:539:8: W0101: Unreachable code (unreachable)
56
src/bittytax/price/datasource.py:558:8: W0101: Unreachable code (unreachable)
57
src/bittytax/price/datasource.py:584:8: W0101: Unreachable code (unreachable)

Expected behavior

No errors and per pylint 3.2.3.

Pylint version

astroid-3.2.2 pylint-3.2.4

OS / Environment

Ubuntu 22.04.4 LTS

I don't have access to laptop right now, but can somebody bisect if this is due to #9714?

Seeing the same in QEMU's tests: under Python 3.9 through 3.12 inclusive, there's no issue. Python 3.8 with 3.1.1, 3.2.0, 3.2.1, 3.2.2, 3.2.3 all work correctly. This only seems to surface directly under pylint 3.2.4 and python 3.8. Python 3.7 and earlier untested.

Bisecting confirms commit c41c35a

here's a somewhat minimized reproducer: I tried testing it one function at a time, but could only trigger it in the caller to qemu_img_json().

import json
import subprocess
from typing import Any

def qemu_tool(
    *args: str,
    check: bool = True,
    combine_stdio: bool = True
) -> 'subprocess.CompletedProcess[str]':
    subp = subprocess.run(
        args,
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT if combine_stdio else subprocess.PIPE,
        universal_newlines=True,
        check=False
    )

    if check and subp.returncode or (subp.returncode < 0):
        raise subprocess.CalledProcessError(
            subp.returncode, args,
            output=subp.stdout,
            stderr=subp.stderr,
        )

    return subp

def qemu_img(
    *args: str,
    check: bool = True,
    combine_stdio: bool = True
) -> 'subprocess.CompletedProcess[str]':
    full_args = ['qemu-img'] + list(args)
    return qemu_tool(*full_args, check=check, combine_stdio=combine_stdio)

def qemu_img_json(*args: str) -> Any:
    try:
        res = qemu_img(*args, combine_stdio=False)
    except subprocess.CalledProcessError as exc:
        if exc.returncode < 0:
            raise
        try:
            return json.loads(exc.stdout)
        except json.JSONDecodeError:
            pass
        raise
    return json.loads(res.stdout)

def qemu_img_info(*args: str) -> Any:
    ret = qemu_img_json('info', "--output", "json", *args)
    print("hello?")  # repro.py:50:4: W0101: Unreachable code (unreachable)
    return ret

... If I had to guess, it's because _SpecialForm is being checked in 3.8, but I believe that typing.Any also inherits _SpecialForm, so I think that might be what's going on here.

Yup. Here's a much shorter reproducer:

from typing import Any

def repro() -> Any:
    return 5

def main():
    x = repro() + 5
    print(x)

Thank you for the reproducer @jnsnow ! I don't have python 3.8 locally, but I've opened #9753 to reproduce in the continuous integration.

Big thankyou to everyone for getting this resolved so quickly.