facebook / pyre-check

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

flow control analysis appears to not handle for-else loops #192

Open jesboat opened 5 years ago

jesboat commented 5 years ago

If a variable is assigned in a for loop's body and its else clause, then it should be treated as defined at the loop's end. For example, this code is safe

#pyre-strict

from typing import Iterable

def demo(stuff: Iterable[object]) -> bool:
    for irrelevant in stuff:
        var = True
    else:
        var = True
    return var

but is rejected by Pyre with

demo2.py:10:11 Undefined name [18]: Global name `var` is not defined, or there is at least one control flow path that doesn't define `var`.

The code which inspired this looked something like

        with self._db as txn:
            for r in txn.selectrows('the_table', {'key': key}):
                previous_row = r
                break
            else:
                previous_row = None
                txn.insert('the_table', {STUFF});

        do_stuff_with(previous_row)
DarwinAwardWinner commented 4 years ago

I've encountered another instance of this:

from typing import List

# Always either returns int or throws Exception
def first_greater_than_ten(x: List[int]) -> int:
    for i in x:
        if i > 10:
            return i
    else:
        raise Exception('None of the input numbers were greater than 10.')

Pyre complains that this function can return None (implicitly by reaching the end of the function definition with no return), when in fact it cannot:

test.py:9:8 Incompatible return type [7]: Expected `int` but got implicit return value of `None`.