facebook / pyre-check

Performant type-checking for python.
https://pyre-check.org/
MIT License
6.86k stars 437 forks source link

Pyre doesn't understand for-else statement #791

Closed WangGithubUser closed 3 months ago

WangGithubUser commented 1 year ago

Pyre Bug

Bug description Pyre doesn't understand meaning of for-else.

Reproduction steps Run pyre-check with following script:

from random import randint
def foo() -> None:
    for i in range(randint(1, 10)):
        if i % 5 == 0:
            my_none: None = None
            break
    else:
        return None
    return my_none

Expected behavior This should pass but not give a false positive: test.py:9:11 Uninitialized local [61]: Local variable `my_none` is undefined, or not always defined.

Logs

$ pyre check
ƛ Found 1 type error!
test.py:9:11 Uninitialized local [61]: Local variable `my_none` is undefined, or not always defined.

pyre_rage.log

connernilsen commented 1 year ago

Hey @WangGithubUser, thanks for reporting this! I didn't know that for-else loops were a thing until now, it's a cool feature of the language!

As a quick debugging check, I [tried doing a similar setup with an if statement](https://pyre-check.org/play?input=from%20random%20import%20randint%0A%0Adef%20foo()%20-%3E%20None%3A%0A%20%20%20%20for%20i%20in%20range(randint(1%2C%2010))%3A%0A%20%20%20%20%20%20%20%20if%20i%20%25%205%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20my_none%3A%20None%20%3D%20None%0A%20%20%20%20%20%20%20%20%20%20%20%20break%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20return%20None%0A%20%20%20%20return%20my_none%0A%0Adef%20bar()%20-%3E%20None%3A%0A%20%20%20%20if%20randint(1%2C%2010)%20%25%205%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20my_none%3A%20None%20%3D%20None%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20return%20None%0A%20%20%20%20return%20my_none), and it looks like the issue doesn't appear there. My guess is that we have an issue with our control flow graph around for-else statements. I'll add this task to our backlog and we'll take a look.

ebrahimsofi123 commented 6 months ago

The error you're seeing is because Pyre isn't sure if the variable my_none is always set before it's used. To fix this error i would suggest you to initialize my_none before the loop:

from random import randint

def foo() -> None: my_none = None # Initialize it here for i in range(randint(1, 10)): if i % 5 == 0: my_none = None break else: return None return my_none

I believe that this should resolve the issue
yangdanny97 commented 3 months ago

duplicate of #192