microsoft / pyright

Static Type Checker for Python
Other
13.12k stars 1.4k forks source link

Suppress unreachable code hint for assert_never #8833

Closed robian closed 2 weeks ago

robian commented 2 weeks ago

Although the python match statement does not require case arms to be exhaustive, an approximation of an exhaustiveness check is possible via typing.assert_never() which facilitates a static and a runtime check.

import typing

def fn(v: str | bool):
    match v:
        case str():
            print("str")

        case bool():
            print("bool")

        case int():
            print("int")

        case t:
            typing.assert_never(t)

For the above code, recent pyright releases issue unreachable code hints for the last two case statements.

From a type perspective v will never be an int, so the unreachable code hint is both correct and useful.

The intention of the last case statement however is to be never matched and to create an error if the type of v is ever broadened.

Would it make sense (and is it feasible) to have pyright support this pattern and not issue a hint in the never arm of the match?

erictraut commented 2 weeks ago

Pyright doesn't emit errors for this case, just tagged hints. This is very intentional, since conditions like this are not necessarily indicative of bugs. Tagged hints are generated only for a language server, and only if the client claims that it supports such tagged hints. The CLI version of pyright never emits any diagnostics for this case. These tagged hints should cause your editor to display the text with a subtle grayed-out appearance — something that indicates that the code is deemed unreachable according to static analysis but is not an error or (necessarily) a bug. Unfortunately, some clients display tagged hints in a less subtle way, making it appear that this is an error that must be fixed.

For example, see how this appears in the pyright playground.

If you don't want pyright to emit tagged hints in this case, you have a couple of options:

  1. Disable all tagged hints by setting "pyright.disableTaggedHints" to true in your language server settings.
  2. Set "enableReachabilityAnalysis" to false in your config file. This will disable reachability analysis based on type analysis but retain reachability based on non-type information like unconditional control-flow statements.