Skip to content

UnboundLocalError not detected when variable not declared as global or nonlocal is del'd #769

@AlexWaygood

Description

@AlexWaygood

Summary

Ty doesn't emit any error on this code, but it should:

X = 42

def f():
    print(X)

    if False:
        del X

At runtime, calling f() will always fail:

>>> X = 42
>>> def f():
...     print(X)
...     if False:
...         del X
...         
>>> f()
Traceback (most recent call last):
  File "<python-input-3>", line 1, in <module>
    f()
    ~^^
  File "<python-input-2>", line 2, in f
    print(X)
          ^
UnboundLocalError: cannot access local variable 'X' where it is not associated with a value

The fact that there is a del statement affecting the X variable later on in the function means that X must be a local variable for the duration of the entire function scope. I.e., the print() call on the first line of the function that attempts to load the symbol X cannot reach the definition of X in the global scope. X can only ever be a local variable inside this function due to the later del statement, even though that del statement is unreachable.

Practically speaking, we need to edit this check so that it also checks whether there were any deletions in this scope as well as any bindings in this scope, and returns Place::Unbound if so: https://github.com/astral-sh/ruff/blob/44f2f77748f5ed36b78243075c849a091adfaaf8/crates/ty_python_semantic/src/types/infer.rs#L5708-L5719

@oconnor663, I wonder if you might be interested in this? It's sort-of adjacent to your work on the nonlocal statement :-)

Version

No response

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingruntime semanticsAccurate modeling of how Python's semantics work at runtime

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions