mypy icon indicating copy to clipboard operation
mypy copied to clipboard

`--strict-equality-for-none` false positive on a global in a loop with `--allow-redefinition-new`

Open gschaffner opened this issue 1 month ago • 0 comments

Bug Report

--strict-equality-for-none gives a false-positive comparison-overlap error when checking if a global is None in a loop.

To Reproduce

# No playground link since playground doesn't support --strict-equality-for-none yet (https://github.com/ymyzk/mypy-playground/issues/1058).

# flags: --strict-equality --strict-equality-for-none --local-partial-types --allow-redefinition-new
x: int | None = None


def f() -> None:
    for _ in range(1):
        reveal_type(x)  # note: Revealed type is "builtins.int | None"
        if x is None:  # error: Non-overlapping identity check (left operand type: "int", right operand type: "None")  [comparison-overlap]
            raise RuntimeError()

Expected Behavior

No error.

Actual Behavior

$ mypy --strict-equality --strict-equality-for-none --local-partial-types --allow-redefinition-new eg.py
eg.py:9: note: Revealed type is "builtins.int | None"
eg.py:10: error: Non-overlapping identity check (left operand type: "int", right operand type: "None")  [comparison-overlap]
Found 1 error in 1 file (checked 1 source file)

Your Environment

  • Mypy version used: 1.18.2, compiled
  • Mypy command-line flags: --strict-equality --strict-equality-for-none --local-partial-types --allow-redefinition-new
  • Mypy configuration options from mypy.ini (and other config files): none
  • Python version used: CPython 3.13.9

Notes

This seems similar to #19328. Note that the error goes away if you remove the line for _ in range(1):.

But it's different from #19328 in some ways:

  • It seems to be specific to --strict-equality-for-none; the error goes away if you change None to str:

    # flags: --strict-equality --strict-equality-for-none --local-partial-types --allow-redefinition-new
    x: int | str = ""
    
    
    def f() -> None:
        for _ in range(1):
            reveal_type(x)  # note: Revealed type is "builtins.int | builtins.str"
            if x == "":  # NO ERROR
                raise RuntimeError()
    
  • It seems to require multiple variable scopes: the error goes away if you remove the line def f() -> None:.

  • Unlike the snippet in #19328, the variable is never mutated.

gschaffner avatar Nov 24 '25 05:11 gschaffner