check-miscaptures.py: Python doesn't really have declarations; report the topmost...
authordavid-sarah <david-sarah@jacaranda.org>
Sun, 9 Oct 2011 04:48:00 +0000 (04:48 +0000)
committerdavid-sarah <david-sarah@jacaranda.org>
Sun, 9 Oct 2011 04:48:00 +0000 (04:48 +0000)
misc/coding_tools/check-miscaptures.py

index c1ced6c3f1369675cdb16015d9f74d59bdab1936..b6c9f71e92571ff82aea3254e2c867bbc7f60c62 100644 (file)
@@ -41,21 +41,22 @@ def check_loop(ast, results):
     # ), and that case they are not nested in the AST. But these
     # warts (nonobviously) happen not to matter for our analysis.
 
-    declared = {}  # maps name to lineno of declaration
+    assigned = {}  # maps name to lineno of topmost assignment
     nested = set()
-    collect_declared_and_nested(ast, declared, nested)
+    collect_assigned_and_nested(ast, assigned, nested)
 
     # For each nested function...
     for funcnode in nested:
         # Check for captured variables in this function.
         captured = set()
-        collect_captured(funcnode, declared, captured)
+        collect_captured(funcnode, assigned, captured)
         for name in captured:
             # We want to report the outermost capturing function
             # (since that is where the workaround will need to be
-            # added), and the variable declaration. Just one report
-            # per capturing function per variable will do.
-            results.append(make_result(funcnode, name, declared[name]))
+            # added), and the topmost assignment to the variable.
+            # Just one report per capturing function per variable
+            # will do.
+            results.append(make_result(funcnode, name, assigned[name]))
 
         # Check each node in the function body in case it
         # contains another 'for' loop.
@@ -63,14 +64,15 @@ def check_loop(ast, results):
         for child in childnodes:
             check_ast(funcnode, results)
 
-def collect_declared_and_nested(ast, declared, nested):
+def collect_assigned_and_nested(ast, assigned, nested):
     """
-    Collect the names declared in this 'for' loop, not including
-    names declared in nested functions. Also collect the nodes of
-    functions that are nested one level deep.
+    Collect the names assigned in this loop, not including names
+    assigned in nested functions. Also collect the nodes of functions
+    that are nested one level deep.
     """
     if isinstance(ast, AssName):
-        declared[ast.name] = ast.lineno
+        if ast.name not in assigned or assigned[ast.name] > ast.lineno:
+            assigned[ast.name] = ast.lineno
     else:
         childnodes = ast.getChildNodes()
         if isinstance(ast, (Lambda, Function)):
@@ -83,24 +85,23 @@ def collect_declared_and_nested(ast, declared, nested):
 
         for child in childnodes:
             if isinstance(ast, Node):
-                collect_declared_and_nested(child, declared, nested)
+                collect_assigned_and_nested(child, assigned, nested)
 
-def collect_captured(ast, declared, captured):
-    """Collect any captured variables that are also in declared."""
+def collect_captured(ast, assigned, captured):
+    """Collect any captured variables that are also in assigned."""
     if isinstance(ast, Name):
-        if ast.name in declared:
+        if ast.name in assigned:
             captured.add(ast.name)
     else:
         childnodes = ast.getChildNodes()
-
         if isinstance(ast, (Lambda, Function)):
             # Formal parameters of the function are excluded from
             # captures we care about in subnodes of the function body.
-            new_declared = declared.copy()
-            remove_argnames(ast.argnames, new_declared)
+            new_assigned = assigned.copy()
+            remove_argnames(ast.argnames, new_assigned)
 
             for child in childnodes[len(ast.defaults):]:
-                collect_captured(child, declared, captured)
+                collect_captured(child, assigned, captured)
 
             # The default argument expressions are "outside" the
             # function, even though they are children of the
@@ -109,7 +110,7 @@ def collect_captured(ast, declared, captured):
 
         for child in childnodes:
             if isinstance(ast, Node):
-                collect_captured(child, declared, captured)
+                collect_captured(child, assigned, captured)
 
 
 def remove_argnames(names, fromset):
@@ -132,7 +133,7 @@ def report(out, path, results):
         if isinstance(r, SyntaxError):
             print >>out, path + (" NOT ANALYSED due to syntax error: %s" % r)
         else:
-            print >>out, path + (":%r %s captures %r declared at line %d" % r)
+            print >>out, path + (":%r %s captures %r assigned at line %d" % r)
 
 def check(sources, out):
     class Counts: