def foo(x: str | None):
if x is None:
x = "asdf"
def inner():
return x + "foo"
pyright looks for reassignments following the closure definition in the flow graph. mypy does something similar (but its implementation is a little sketchier). This heuristic seems to work fairly well in practice. Was a very common cause of user confusion and duplicate reports before mypy added support for this.