Add no binding found for nonlocal issue

This commit is contained in:
Dave Halter
2017-07-30 21:33:26 +02:00
parent aa0225f63f
commit 0906c0b634
4 changed files with 49 additions and 12 deletions

View File

@@ -100,6 +100,7 @@ class Context(object):
self._used_name_dict = {}
self._global_names = []
self._nonlocal_names = []
self._nonlocal_names_in_subscopes = []
self._add_syntax_error = add_syntax_error
def is_async_funcdef(self):
@@ -125,14 +126,17 @@ class Context(object):
self._used_name_dict.setdefault(name.value, []).append(name)
def finalize(self):
"""
Returns a list of nonlocal names that need to be part of that scope.
"""
self._analyze_names(self._global_names, 'global')
self._analyze_names(self._nonlocal_names, 'nonlocal')
# Python2.6 doesn't have dict comprehensions.
nonlocal_name_strs = dict((n.value, n) for n in self._nonlocal_names)
for global_name in self._global_names:
global_name_strs = dict((n.value, n) for n in self._global_names)
for nonlocal_name in self._nonlocal_names:
try:
nonlocal_name = nonlocal_name_strs[global_name.value]
global_name = global_name_strs[nonlocal_name.value]
except KeyError:
continue
@@ -143,6 +147,17 @@ class Context(object):
error_name = nonlocal_name
self._add_syntax_error(message, error_name)
nonlocals_not_handled = []
for nonlocal_name in self._nonlocal_names_in_subscopes:
search = nonlocal_name.value
if search in global_name_strs or self.parent_context is None:
message = "no binding for nonlocal '%s' found" % nonlocal_name.value
self._add_syntax_error(message, nonlocal_name)
elif not self.is_function() or \
nonlocal_name.value not in self._used_name_dict:
nonlocals_not_handled.append(nonlocal_name)
return self._nonlocal_names + nonlocals_not_handled
def _analyze_names(self, globals_or_nonlocals, type_):
def raise_(message):
self._add_syntax_error(message % (base_name.value, type_), base_name)
@@ -198,7 +213,7 @@ class Context(object):
def add_context(self, node):
new_context = Context(node, self._add_syntax_error, parent_context=self)
yield new_context
new_context.finalize()
self._nonlocal_names_in_subscopes += new_context.finalize()
class ErrorFinder(Normalizer):
@@ -627,6 +642,8 @@ class ErrorFinder(Normalizer):
self._error_dict.setdefault(line, (code, message, node))
def finalize(self):
self._context.finalize()
for code, message, node in self._error_dict.values():
self.issues.append(Issue(node, code, message))

View File

@@ -71,6 +71,7 @@ def _all_string_prefixes(version_info):
if version_info <= (2, 7):
# TODO this is actually not 100% valid. ur is valid in Python 2.7,
# while ru is not.
# TODO rb is also not valid.
_valid_string_prefixes.append('ur')
# if we add binary f-strings, add: ['fb', 'fbr']

View File

@@ -47,11 +47,10 @@ except ZeroDivisionError:
pass
class X():
nonlocal a
def c():
a = 3
def d():
class X():
nonlocal a

View File

@@ -175,10 +175,30 @@ FAILING_EXAMPLES = [
'''),
dedent('''
def x():
a = 4
def y():
global a
nonlocal a
'''),
# Missing binding of nonlocal
dedent('''
def x():
nonlocal a
'''),
dedent('''
def x():
def y():
nonlocal a
'''),
dedent('''
def x():
a = 4
def y():
global a
print(a)
def z():
nonlocal a
'''),