Skip to content

Commit 88a0c6e

Browse files
BCSharpslozier
andauthored
Prevent nonlocal binding to global variables (#1150)
* Prevent nonlocal binding to global variables * Fix remaining f->foo Co-authored-by: Stéphane Lozier <slozier@users.noreply.github.com>
1 parent 2e5e3bc commit 88a0c6e

2 files changed

Lines changed: 27 additions & 11 deletions

File tree

Src/IronPython/Compiler/Ast/ScopeStatement.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ internal virtual void Bind(PythonNameBinder binder) {
340340
if (_references != null) {
341341
foreach (var reference in _references.Values) {
342342
reference.PythonVariable = BindReference(binder, reference);
343-
if (reference.PythonVariable is null && TryGetNonlocalStatement(reference.Name, out NonlocalStatement node)) {
343+
if ((reference.PythonVariable is null || reference.PythonVariable.Kind is VariableKind.Global) && TryGetNonlocalStatement(reference.Name, out NonlocalStatement node)) {
344344
binder.ReportSyntaxError($"no binding for nonlocal '{reference.Name}' found", node);
345345
}
346346
}

Tests/test_nonlocal.py

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def test_no_binding_func(self):
2121
def foo():
2222
nonlocal x
2323
return x
24-
f()
24+
foo()
2525
"""
2626
self.check_compile_error(source, "no binding for nonlocal 'x' found", 3)
2727

@@ -31,7 +31,7 @@ def test_no_binding_func_global(self):
3131
def foo():
3232
nonlocal x
3333
return x
34-
f()
34+
foo()
3535
"""
3636
self.check_compile_error(source, "no binding for nonlocal 'x' found", 4)
3737

@@ -52,17 +52,33 @@ def foo():
5252
global x # CPython's error location
5353
nonlocal x # IronPython's error location
5454
return x
55-
f()
55+
foo()
5656
"""
5757
self.check_compile_error(source, "name 'x' is nonlocal and global", 4 if is_cli else 3)
5858

59+
def test_local_global_nonlocal(self):
60+
source = """if True:
61+
def foo():
62+
x = 2 # x local in foo
63+
def bar():
64+
def gek():
65+
nonlocal x # sees global x in bar before x from foo
66+
return x
67+
global x # masks local x in outer scope foo
68+
x = 3 # global x
69+
return x, gek()
70+
return x, bar()
71+
foo()
72+
"""
73+
self.check_compile_error(source, "no binding for nonlocal 'x' found", 6)
74+
5975
def test_nonlocal_global(self):
6076
source = """if True:
6177
def foo():
6278
nonlocal x # CPython's error location
6379
global x # IronPython's error location
6480
return x
65-
f()
81+
foo()
6682
"""
6783
self.check_compile_error(source, "name 'x' is nonlocal and global", 4 if is_cli else 3)
6884

@@ -73,7 +89,7 @@ def bar():
7389
nonlocal y
7490
return x
7591
return bar()
76-
f(1)
92+
foo(1)
7793
"""
7894
self.check_compile_error(source, "no binding for nonlocal 'y' found", 4)
7995

@@ -86,7 +102,7 @@ def bar():
86102
nonlocal x
87103
return x
88104
return bar()
89-
f(1)
105+
foo(1)
90106
"""
91107
self.check_compile_error(source, "name 'x' is assigned to before nonlocal declaration", 5)
92108

@@ -99,7 +115,7 @@ def bar():
99115
nonlocal x
100116
return x
101117
return bar()
102-
f(1)
118+
foo(1)
103119
"""
104120
self.check_compile_error(source, "name 'x' is used prior to nonlocal declaration", 5)
105121

@@ -114,7 +130,7 @@ class x: pass # assignment
114130
nonlocal x
115131
return x
116132
return bar()
117-
f(1)
133+
foo(1)
118134
"""
119135
self.check_compile_error(source, "name 'x' is assigned to before nonlocal declaration", 7)
120136

@@ -129,7 +145,7 @@ class x: pass # assignment
129145
nonlocal x
130146
return x
131147
return bar()
132-
f(1)
148+
foo(1)
133149
"""
134150
self.check_compile_error(source, "name 'x' is assigned to before nonlocal declaration", 7)
135151

@@ -144,7 +160,7 @@ class x: pass # assignment
144160
nonlocal x
145161
return x
146162
return bar()
147-
f(int)
163+
foo(int)
148164
"""
149165
if is_cli:
150166
self.check_compile_error(source, "name 'x' is assigned to before nonlocal declaration", 7)

0 commit comments

Comments
 (0)