pyrefly icon indicating copy to clipboard operation
pyrefly copied to clipboard

Pyrefly Fails to Report Non-Exhaustive match Statements and Incorrect Enum Case Patterns

Open davisuga opened this issue 9 months ago • 1 comments

Describe the Bug

Observed Behavior (with Pyrefly):

When analyzing Python match statements, Pyrefly currently reports no errors under the following conditions where issues are present:

  • Non-exhaustive matching: When case statements within a match block do not collectively handle all possible members of an Enum or all types within a Union that the matched variable could be.
  • Incorrect syntax for Enum member case patterns: When Enum members used in case patterns are incorrectly written with parentheses as if they were being called (e.g., MyEnum.MEMBER() instead of the correct syntax MyEnum.MEMBER).

This behavior implies Pyrefly currently considers these potentially problematic patterns as valid.

Expected Behavior:

Pyrefly should identify and report errors in the scenarios described above to help developers catch potential bugs and ensure code correctness:

  • For non-exhaustive matching: Pyrefly should issue an error or warning, ideally specifying which Enum members or Union types are not handled by any case statement.
  • For incorrect Enum syntax: Pyrefly should report a syntax or type error when Enum members are incorrectly used with parentheses in case patterns (e.g., similar to Pyright's "Literal[...] is not a class" report).

This would align Pyrefly's strictness with other type checkers and provide more robust static analysis for match statements.

Pyright's Output (for reference, from the original user report):

/Users/davi/gits/chief-app/test_types.py
  /Users/davi/gits/chief-app/test_types.py:21:11 - error: Cases within match statement do not exhaustively handle all values
    Unhandled type: "OrderStatus"
    If exhaustive handling is not intended, add "case _: pass" (reportMatchNotExhaustive)
  /Users/davi/gits/chief-app/test_types.py:22:14 - error: "Literal[OrderStatus.Ready]" is not a class (reportGeneralTypeIssues)
  /Users/davi/gits/chief-app/test_types.py:24:14 - error: "Literal[OrderStatus.Shipped]" is not a class (reportGeneralTypeIssues)
  /Users/davi/gits/chief-app/test_types.py:30:11 - error: Cases within match statement do not exhaustively handle all values
    Unhandled type: "Literal[OrderStatus.Scheduled]"
    If exhaustive handling is not intended, add "case _: pass" (reportMatchNotExhaustive)
  /Users/davi/gits/chief-app/test_types.py:52:11 - error: Cases within match statement do not exhaustively handle all values
    Unhandled type: "int"
    If exhaustive handling is not intended, add "case _: pass" (reportMatchNotExhaustive)
5 errors, 0 warnings, 0 information 

Steps to Reproduce:

  1. Prepare the code: Save the Python code snippet provided in the "Sandbox Link" section of this GitHub issue (which contains functions handle_order_1, handle_order_2, and handle) into a local file (e.g., test_match_issues.py). This code demonstrates the non-exhaustive matches and incorrect Enum syntax.
  2. Analyze with Pyrefly: Run pyrefly on the created file:
    pyrefly check test_match_issues.py
    
  3. Observe Pyrefly's output: Notice that Pyrefly does not report any errors for the problematic match statements within the handle_order_1, handle_order_2, or handle functions.
  4. (Optional Comparison) Analyze with Pyright: Run pyright on the same file:
    pyright test_match_issues.py
    
  5. Observe Pyright's output: Notice that Pyright correctly identifies and reports multiple errors related to non-exhaustiveness and incorrect enum case patterns for the same functions, as detailed in the "Pyright's Output" section of this issue. This highlights the behavior expected from Pyrefly.

Sandbox Link

https://pyrefly.org/sandbox/?code=GYJw9gtgBApgdgV2gSwgBzCALlAooiAKEIGIoB5EZAc2TgEMAbKAZTARAGMYAuKACyxY0AZx4B6cf3oBrZACN4yLPQB0nSOLQBPLPzBwAtBG07DMAB7SEIrMgBuMQ534xOcuNVWkoAFX7IIlAaACYwsBZojJgwQZbWtg7wsUEubh7UUHRQEPRYLlC2eTAQ8Fgi3mQAYphQAAq6+nBQAMyqAIztANQANFBYpnRe9CIiMNgA+nAwjiBZQfT29MiM9PKMvAJCohLiA2hDqiAw9CF6MCFgnBXIYOLw4qtYsVjiIuxcMOIIcMf0LmsNqpBBBGD5-OFLPR0BsgopogB3KD0Y5QGwXfpgfovCIJOyOaajYKudxDHwI5T8eqNAytDoABgA5EFcvkqUVnqU4FhVNSqNRBMyoIppPZbnMABQaEDHThYRjaLJhbnIYDaIawGWYEQASnmUDgYGeIUqfgCQWOGGwAho-EYtvK-WkOB0x2ACqgEoRAQKlswxo9dBCyE4xSCACIwDJw1BaobNeAQEFgLVzmNgiNYjqfCJ9AhGCElSdGB6mO8iyq1U7YuEMM8VUx5iIELEoCm5uB5DYcPtwmlSZ5vIROKsiZQwiAWCosDYJfgkDqeIQoCuoAAlE4hRUAXig4b+W-Dy9XLDSIXzGN34ZEZ4vISPq9YATQaEve9zyBfFyPPgabo9ubsAWRZMB6frYEuZDtLyAByBjmFY9DdkkOR5AUErjuMU55DYqinq454bIWECBCIQw6qaABMvIAJJwNKsounkzwgM0AAGmGTtOuEbqc2gSjqbGeoB+aFooUAcSAE7YTOFS8VubEUT4bRQHRDFuExQjjOxnEybhLDPq+IQCUJEoicB4mSdJ3EVAZn5GYp3hhMAAj0HAIQbBMmAThM7RmTZfC6TZeqGAAfFAcHTEuj6suhHI2IuUBkH+DpQPa0xQJRAAcWTNJgNB0I2UIwjAx6PiuobpkFOFyZu-GJcl2j8oIaV0OElEAJxleVj5oFQ3IStez6xlJ4zhtmPUVZmFCjVxNV4YZFwCXwjXNTg6XhC0lHdZNUB9XQWCDQCnjhN5Y3Zr+TUwO6irmYWyBhKBirgVgkFQNBEXwfESGJI4qFsp61WyXht5ETkpHkU512ue5nlnSAEyUf5NWBbNem6lAYWfVF3WxVSyOyQ1fKpRtUAACz0rlI0FQwzDFVEpW7ZV4RAzxdXRbtq77QNQ2fiNE7jTtq7MzN1nzXZX4hBznN7f1h3hsd1CnbNguPiAyzpgAakwLa4FqIASsA4bo1AADe8UiAAvvqho4Es9r3hdZD+IE+rAG1fSutdAF5sBL0Gli4yJhUhDOTDHkwF5s0TC0BM2KjYuE9L-1xcFyePiLrO1Xx6eTdz8sfmg-PnULU1VWjNkgwRd65z1+eDYXxcgKrk2ZxX4uLVLpflfXCvSCdTeC4QdFYJQU5zLuB1QAAPoUWAgMQq3e7dvv3Y9JbPTAVqvT4H2RQheIoXjnpsQdQm9uDoyQ6H0P9xHEoWFAfAj2P88heFkW8LjaFUhYK3EwKdabUoAAHZKJU3yrQWmERoQM27iLWwBtFzd16nLQaAAJNyEdHZAA

(Only applicable for extension issues) IDE Information

No response

davisuga avatar May 29 '25 14:05 davisuga

Thanks for reporting, there are some known gaps in how we analyze match statements. I'm not sure if its feasible to be 100% accurate for exhaustiveness checks when matching on gradual types, but we can certainly do better than we're doing now.

yangdanny97 avatar May 29 '25 14:05 yangdanny97

This issue has been automatically marked as stale because it is assigned, but has not had recent activity for more than 2 weeks.

If you are still working on this issue, please add a comment to keep it active. Otherwise, please unassign yourself and allow someone else to take over.

Thank you for your contributions!

github-actions[bot] avatar Jul 04 '25 00:07 github-actions[bot]

It appears that additionally, for match statements with enumerations, pyrefly doesn't mark all branches as being handled when the enum is exhausted. From my perspective, it's probably the same issue, but if you have a return type in a function and return from inside the match, then pyrefly will complain unless you have a case _ or similar, even though you've covered the whole enum. Example:

class MyEnum(Enum):
    VALUE1: "my_val"
    VALUE2: "not_my_val"

def func(value: MyEnum) -> str: # marked with does not return <str> on all paths
    match value:
        case VALUE1:
            return "matched"
        case VALUE2:
            return VALUE2.value

alschade avatar Aug 04 '25 15:08 alschade

Presumably the same as what @alschade wrote, but I just wanted to add that the problem also occurs with StrEnums:

class Type(StrEnum):
    FIRST = "FIRST"
    SECOND = "SECOND"


def format_type(type_: Type) -> str:
    match type_:
        case Type.FIRST:
            return "First Type"
        case Type.SECOND:
            return "Second Type"

This yields:

ERROR Function declared to return `str`, but one or more paths are missing an explicit `return` [bad-return]
 --> /home/coder2k/dev/generics-test/match.py:9:33
  |
9 | def format_type(type_: Type) -> str:
  |                                 ^^^
  |
 INFO 1 error

mgerhold avatar Aug 11 '25 06:08 mgerhold