Skip to content

Commit 1dc7c1d

Browse files
committed
Allow del to clear type info for local inferred variables (fixes #10005)
1 parent 557d964 commit 1dc7c1d

File tree

3 files changed

+96
-4
lines changed

3 files changed

+96
-4
lines changed

mypy/checker.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5225,9 +5225,22 @@ def visit_del_stmt(self, s: DelStmt) -> None:
52255225
s.expr.accept(self.expr_checker)
52265226
for elt in flatten(s.expr):
52275227
if isinstance(elt, NameExpr):
5228-
self.binder.assign_type(
5229-
elt, DeletedType(source=elt.name), get_declaration(elt)
5230-
)
5228+
# For local variables, completely remove type binding to allow reuse
5229+
if (isinstance(elt.node, Var) and
5230+
elt.node.is_inferred and
5231+
not elt.node.is_property and
5232+
not elt.node.is_classvar and
5233+
elt.node.is_local):
5234+
# Completely remove the variable from type tracking
5235+
self.binder.cleanse(elt)
5236+
# Also remove from type map if present
5237+
if hasattr(self, 'type_map') and elt in self.type_map:
5238+
del self.type_map[elt]
5239+
else:
5240+
# For non-local variables, use the existing DeletedType behavior
5241+
self.binder.assign_type(
5242+
elt, DeletedType(source=elt.name), get_declaration(elt)
5243+
)
52315244

52325245
def visit_decorator(self, e: Decorator) -> None:
52335246
for d in e.decorators:
@@ -5306,7 +5319,7 @@ def visit_decorator_inner(
53065319
self.fail(message_registry.BAD_CONSTRUCTOR_TYPE, e)
53075320

53085321
if e.func.original_def and isinstance(sig, FunctionLike):
5309-
# Function definition overrides function definition.
5322+
# Function signature processing continues herection definition overrides function definition.
53105323
self.check_func_def_override(e.func, sig)
53115324

53125325
def check_for_untyped_decorator(
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
[case testDelVariableReuse]
2+
def test_del_reuse() -> None:
3+
x = 1
4+
reveal_type(x) # N: Revealed type is "builtins.int"
5+
del x
6+
x = "hello"
7+
reveal_type(x) # N: Revealed type is "builtins.str"
8+
9+
[case testDelVariableReuseConditional]
10+
def test_del_conditional() -> None:
11+
x = 1
12+
if True:
13+
del x
14+
x = "hello"
15+
reveal_type(x) # N: Revealed type is "builtins.str"
16+
17+
[case testDelVariableNotLocal]
18+
x = 1
19+
def test_del_global() -> None:
20+
global x
21+
del x
22+
x = "hello" # E: Incompatible types in assignment (expression has type "str", variable has type "int")
23+
24+
[case testDelIndexExpr]
25+
def test_del_index() -> None:
26+
d = {"key": 1}
27+
del d["key"]
28+
d["key"] = "hello" # OK - this should work normally
29+
30+
[case testDelAttribute]
31+
class C:
32+
attr: int = 1
33+
34+
def test_del_attr() -> None:
35+
c = C()
36+
del c.attr
37+
c.attr = "hello" # E: Incompatible types in assignment (expression has type "str", target has type "int")
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
[case testDelVariableResetsType]
2+
def test_del_resets_type() -> None:
3+
x = 42 # x: int
4+
del x
5+
x = "hello" # Should not error - x can now be str
6+
reveal_type(x) # N: Revealed type is "builtins.str"
7+
8+
[case testDelVariableInBranch]
9+
def test_del_in_branch(cond: bool) -> None:
10+
x = 42 # x: int
11+
if cond:
12+
del x
13+
x = "hello" # Should not error in this branch
14+
else:
15+
reveal_type(x) # N: Revealed type is "builtins.int"
16+
17+
[case testDelVariableNotLocal]
18+
class C:
19+
attr: int = 42
20+
21+
def test_del_not_local() -> None:
22+
c = C()
23+
del c.attr # Should use DeletedType behavior, not reset
24+
# This should still be treated as deleted, not reset
25+
26+
[case testDelVariableExplicitType]
27+
def test_del_explicit_type() -> None:
28+
x: int = 42
29+
del x
30+
x = "hello" # Should still error - explicit type annotation
31+
[out]
32+
main:4: error: Incompatible types in assignment (expression has type "str", variable has type "int")
33+
34+
[case testDelVariableMultiple]
35+
def test_del_multiple() -> None:
36+
x = 42
37+
y = "hello"
38+
del x, y
39+
x = "world" # Should not error
40+
y = 123 # Should not error
41+
reveal_type(x) # N: Revealed type is "builtins.str"
42+
reveal_type(y) # N: Revealed type is "builtins.int"

0 commit comments

Comments
 (0)