mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
fixed scoping issue for nonlocals - now also indirect outer scopes are supported
This commit is contained in:
1 parent
fd2e0aa41e
commit
1773d2bc14
3 files changed
+41
-16
No files matched your search
@@ -166,7 +166,7 @@ class NamedExpressionInvalidTest(unittest.TestCase):
|
||||
with self.assertRaisesRegex(SyntaxError, msg):
|
||||
exec(f"lambda: {code}", {}) # Function scope
|
||||
|
||||
@unittest.expectedFailure # TODO RustPython
|
||||
@unittest.expectedFailure # TODO RustPythonskip
|
||||
def test_named_expression_invalid_comprehension_iterable_expression(self):
|
||||
cases = [
|
||||
("Top level", "[i for i in (i := range(5))]"),
|
||||
@@ -394,21 +394,21 @@ print(a)"""
|
||||
self.assertEqual(b, [1, 1])
|
||||
self.assertEqual(a, 1)
|
||||
|
||||
# the following test is not from CPyrgon and just as refernce for a common scoping problem of RustPython
|
||||
@unittest.skip # needs skipping due to weired behaviour
|
||||
# the following test is not from CPython and just as refernce for a common scoping problem of RustPython
|
||||
def test_named_expression_scop_10_rp_scope_prob(self):
|
||||
def foo():
|
||||
rr=0
|
||||
rr=36
|
||||
def foo0():
|
||||
nonlocal rr
|
||||
def foo1():
|
||||
nonlocal rr
|
||||
self.assertEqual(rr,36)
|
||||
rr+=42
|
||||
foo1()
|
||||
foo0()
|
||||
return rr
|
||||
|
||||
self.assertEqual(foo(), 42)
|
||||
self.assertEqual(foo(), 78)
|
||||
|
||||
|
||||
def test_named_expression_scope_11(self):
|
||||
@@ -550,7 +550,6 @@ spam()"""
|
||||
f()
|
||||
self.assertEqual(GLOBAL_VAR, None)
|
||||
|
||||
@unittest.expectedFailure # TODO RustPython
|
||||
def test_named_expression_nonlocal_scope(self):
|
||||
sentinel = object()
|
||||
def f():
|
||||
|
||||
@@ -221,11 +221,19 @@ impl<'a> SymbolTableAnalyzer<'a> {
|
||||
} else {
|
||||
match symbol.scope {
|
||||
SymbolScope::Nonlocal => {
|
||||
// check if name is defined in parent table!
|
||||
let parent_symbol_table = self.tables.last();
|
||||
if let Some((symbols, _)) = parent_symbol_table {
|
||||
let scope_depth = self.tables.len();
|
||||
if !symbols.contains_key(&symbol.name) || scope_depth < 2 {
|
||||
let scope_depth = self.tables.len();
|
||||
if scope_depth > 0 {
|
||||
// check if the name is already defined in any outer scope
|
||||
// therefore
|
||||
if scope_depth < 2
|
||||
|| !self
|
||||
.tables
|
||||
.iter()
|
||||
.skip(1) // omit the global scope as it is not non-local
|
||||
.rev() // revert the order for better performance
|
||||
.any(|t| t.0.contains_key(&symbol.name))
|
||||
// true when any of symbol tables contains the name -> then negate
|
||||
{
|
||||
return Err(SymbolTableError {
|
||||
error: format!("no binding for nonlocal '{}' found", symbol.name),
|
||||
location: Default::default(),
|
||||
|
||||
+23
-5
@@ -113,11 +113,29 @@ impl NameProtocol for Scope {
|
||||
}
|
||||
|
||||
fn store_cell(&self, vm: &VirtualMachine, name: &str, value: PyObjectRef) {
|
||||
self.locals
|
||||
.get(1)
|
||||
.expect("no outer scope for non-local")
|
||||
.set_item(name, value, vm)
|
||||
.unwrap();
|
||||
// find the innermost outer scope that contains the symbol name
|
||||
if let Some(locals) = self
|
||||
.locals
|
||||
.iter()
|
||||
.rev()
|
||||
.filter(|l| l.contains_key(name, vm))
|
||||
.nth(0)
|
||||
{
|
||||
// add to the symbol
|
||||
locals.set_item(name, value, vm).unwrap();
|
||||
} else {
|
||||
// somewhat limited solution -> fallback to the old rustpython strategy
|
||||
// and store the next outer scope
|
||||
// This case is usually considered as a failure case, but kept for the moment
|
||||
// to support the scope propagation for named expression assignments to so far
|
||||
// unknown names in comprehensions. We need to consider here more context
|
||||
// information for correct handling.
|
||||
self.locals
|
||||
.get(1)
|
||||
.expect("no outer scope for non-local")
|
||||
.set_item(name, value, vm)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn store_name(&self, vm: &VirtualMachine, key: &str, value: PyObjectRef) {
|
||||
|
||||
Reference in new issue
Block a user