@@ -250,10 +250,11 @@ class Binding:
250
250
the node that this binding was last used.
251
251
"""
252
252
253
- def __init__ (self , name , source ):
253
+ def __init__ (self , name , source , * , assigned = True ):
254
254
self .name = name
255
255
self .source = source
256
256
self .used = False
257
+ self .assigned = assigned
257
258
258
259
def __str__ (self ):
259
260
return self .name
@@ -1007,6 +1008,12 @@ def addBinding(self, node, value):
1007
1008
break
1008
1009
existing = scope .get (value .name )
1009
1010
1011
+ global_scope = self .scopeStack [- 1 ]
1012
+ if (existing and global_scope .get (value .name ) == existing and
1013
+ not existing .assigned ):
1014
+ # make sure the variable is in the global scope before setting as assigned
1015
+ existing .assigned = True
1016
+
1010
1017
if (existing and not isinstance (existing , Builtin ) and
1011
1018
not self .differentForks (node , existing .source )):
1012
1019
@@ -1089,6 +1096,10 @@ def handleNodeLoad(self, node):
1089
1096
continue
1090
1097
1091
1098
binding = scope .get (name , None )
1099
+
1100
+ if binding and binding .assigned is False :
1101
+ self .report (messages .UndefinedName , node , name )
1102
+
1092
1103
if isinstance (binding , Annotation ) and not self ._in_postponed_annotation :
1093
1104
scope [name ].used = True
1094
1105
continue
@@ -1159,12 +1170,19 @@ def handleNodeStore(self, node):
1159
1170
continue
1160
1171
# if the name was defined in that scope, and the name has
1161
1172
# been accessed already in the current scope, and hasn't
1162
- # been declared global
1173
+ # been assigned globally
1163
1174
used = name in scope and scope [name ].used
1164
1175
if used and used [0 ] is self .scope and name not in self .scope .globals :
1165
1176
# then it's probably a mistake
1166
1177
self .report (messages .UndefinedLocal ,
1167
1178
scope [name ].used [1 ], name , scope [name ].source )
1179
+
1180
+ # and remove UndefinedName messages already reported for this name
1181
+ self .messages = [
1182
+ m for m in self .messages if not
1183
+ isinstance (m , messages .UndefinedName ) or
1184
+ m .message_args [0 ] != name ]
1185
+
1168
1186
break
1169
1187
1170
1188
parent_stmt = self .getParent (node )
@@ -1836,7 +1854,7 @@ def ASSERT(self, node):
1836
1854
self .report (messages .AssertTuple , node )
1837
1855
self .handleChildren (node )
1838
1856
1839
- def GLOBAL (self , node ):
1857
+ def GLOBAL (self , node , assign_by_default = False ):
1840
1858
"""
1841
1859
Keep track of globals declarations.
1842
1860
"""
@@ -1848,11 +1866,9 @@ def GLOBAL(self, node):
1848
1866
1849
1867
# One 'global' statement can bind multiple (comma-delimited) names.
1850
1868
for node_name in node .names :
1851
- node_value = Assignment (node_name , node )
1869
+ node_value = Assignment (node_name , node , assigned = assign_by_default )
1852
1870
1853
1871
# Remove UndefinedName messages already reported for this name.
1854
- # TODO: if the global is not used in this scope, it does not
1855
- # become a globally defined name. See test_unused_global.
1856
1872
self .messages = [
1857
1873
m for m in self .messages if not
1858
1874
isinstance (m , messages .UndefinedName ) or
@@ -1866,7 +1882,11 @@ def GLOBAL(self, node):
1866
1882
for scope in self .scopeStack [global_scope_index + 1 :]:
1867
1883
scope [node_name ] = node_value
1868
1884
1869
- NONLOCAL = GLOBAL
1885
+ def NONLOCAL (self , node ):
1886
+ for node_name in node .names :
1887
+ if not any (node_name in scope for scope in self .scopeStack [:- 1 ]):
1888
+ self .report (messages .NoBindingForNonlocal , node , node_name )
1889
+ self .GLOBAL (node , assign_by_default = True )
1870
1890
1871
1891
def GENERATOREXP (self , node ):
1872
1892
self .pushScope (GeneratorScope )
0 commit comments