microsoft / pylance-release

Documentation and issues for Pylance
Creative Commons Attribution 4.0 International
1.67k stars 770 forks source link

Bug: Code is unreachable #6028

Closed mfeyx closed 3 days ago

mfeyx commented 1 week ago

Hi there,

Sometimes Pylance states that my "Code is unreachable" even though the code works as expected. For instance, if I use an if-statement without an else. If I add the else statement the code is "reachable" and not greyed out. (Please find the examples below).

In my understanding the additional else is not neccessary and "bad" practice, if you will.

Environment data

python

Python version 3.10.9.final.0 (and others)

vs code

Version: 1.90.1 (system setup) Commit: 611f9bfce64f25108829dd295f54a6894e87339d Date: 2024-06-11T21:01:24.262Z Electron: 29.4.0 ElectronBuildId: 9593362 Chromium: 122.0.6261.156 Node.js: 20.9.0 V8: 12.2.281.27-electron.0 OS: Windows_NT x64 10.0.19045

pylance

v2024.6.1 (Client) Pylance async client (2024.6.1) started with python extension (2024.8.1) Pylance language server 2024.6.1 (pyright version 1.1.364, commit 0618acc5) starting

Published 2020-06-30, 22:05:55 Last released 2024-06-14, 02:25:45 Last updated 2024-06-07, 09:08:27 Identifier ms-python.vscode-pylance

Code Snippet

"Code is unreachable"

image

import json

def load_json(obj: str) -> list | dict:
    if isinstance(obj, str):
        obj = json.loads(obj)
        return load_json(obj)
    return obj

Code is reachable

image

import json

def load_json(obj: str) -> list | dict:
    if isinstance(obj, str):
        obj = json.loads(obj)
        return load_json(obj)
    else:
        return obj

Repro Steps

erictraut commented 1 week ago

Reachability checks are based on type analysis. The obj parameter is declared to be str. If we assume that this type declaration is not violated, the conditional expression isinstance(obj, str) will always be true.

Based on the behavior of the code, it looks like the function also accepts values of type list and dict. If my assumption is correct, then you should adjust the type annotation for the obj parameter to be obj: str | list | dict. This will eliminate the apparently-unreachable code.

def load_json(obj: str | list | dict) -> list | dict:
    if isinstance(obj, str):
        obj = json.loads(obj)
        return load_json(obj)

    return obj
mfeyx commented 1 week ago

@erictraut in my particular case I have a stringified json object that can have multiple levels of "stringification" and will, eventually, return a list or a dict. so it is a string until it's not that is why my load_json helper function only accepts a str input and should return a list or dict. So it's kind of tricky and maybe I just get rid of the input type.

Anyways, your solution works as well, thanks!

bschnurr commented 1 week ago

@mfeyx see this issue. might help with typing. https://github.com/python/typing/issues/182#issuecomment-1320974824

erictraut commented 1 week ago

@mfeyx, I took a closer look at this issue because of the inconsistency you pointed out in your bug report between the code that uses an else and the code that does not. These should be equivalent, so you should see "unreachable code" in both cases. This inconsistency was due to a bug in the reachability code in pyright. It will be fixed in the next release of pyright and in a future release of pylance.

StellaHuang95 commented 1 week ago

Thanks Eric. I will keep this issue open until Pylance releases the fix.

rchiodo commented 3 days ago

This issue has been fixed in prerelease version 2024.6.102, which we've just released. You can find the changelog here: CHANGELOG.md