Skip to content

False negative involving null checks on optional types #5423

Closed

Description

@person594

Here is a minimal program demonstrating the problem:

foo = None
for _ in range(2):
  if foo is not None:
    print(1[2])
  foo = 1

Expected behavior:
error: Value of type "int" is not indexable

Actual behavior:
mypy accepts the program as valid, when run with the --strict flag

This issue seems to be a problem with how mypy handles reassignment to None type variables, and static evaluation of conditionals. After line 1, foo has the type None according to mypy. Thus, mypy determines that the condition on line 3 will never hold, and thus doesn't do type checking for the body of the if statement. On line 5, foo's type changes to Optional[int]. This, however, will only be reflected in code that follows line 5.

Activity

  1. ilevkivskyi commented on Aug 6, 2018

    @ilevkivskyi
    Member

    Hm, indeed, the conditional binder still doesn't catch all the corner cases. But actually by preference would be to just show errors in unreachable code in the strict mode.

  2. msullivan commented on Aug 9, 2019

    @msullivan
    Collaborator

    This bug is actually pretty bad, I think. It comes up not infrequently and has a pretty bad knock-on effect on mypyc, where it causes bizarre type errors of the form TypeError: None object expected

  3. ilevkivskyi commented on Aug 9, 2019

    @ilevkivskyi
    Member

    @msullivan
    I left a comment in a very similar issue (a duplicate?) recently #7204 (comment). That "proposal" didn't get much traction however (although it seems to me the binder refactoring might me of smaller size than the semantic analyzer one, plus it can be split into few parts).

  4. JukkaL commented on Aug 12, 2019

    @JukkaL
    Collaborator

    This might be easy to fix without a major refactoring. Maybe we only need to modify how is None / is not None checks behave when the type is a partial type.

  5. msullivan commented on Aug 21, 2019

    @msullivan
    Collaborator

    Hm, there are cases where this breaks mypyc without is None checks:

    def foo() -> None:
        last = None
        for i in range(10):
            print(last)
            last = i
    
  6. joshinils commented on Aug 28, 2022

    @joshinils

    in case anyone comes here for help:
    i annotated a variable used in a loop under an if-statement with foo: Optional[float] = None where it previously was not.
    after that the error went away.
    I guess mypy assumed the type to be NoneType or something and that it could never be float.

    (lesson for me here is; always try to annotate more, it'll not only help me but also mypy)

  7. added a commit that references this issue on Nov 23, 2024
    c8eee51
  8. added a commit that references this issue on Dec 21, 2024
    0901689
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions