mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-06-16 06:18:21 +00:00
check-miscaptures.py: Python doesn't really have declarations; report the topmost assignment. refs #1555
This commit is contained in:
@ -41,21 +41,22 @@ def check_loop(ast, results):
|
|||||||
# ), and that case they are not nested in the AST. But these
|
# ), and that case they are not nested in the AST. But these
|
||||||
# warts (nonobviously) happen not to matter for our analysis.
|
# 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()
|
nested = set()
|
||||||
collect_declared_and_nested(ast, declared, nested)
|
collect_assigned_and_nested(ast, assigned, nested)
|
||||||
|
|
||||||
# For each nested function...
|
# For each nested function...
|
||||||
for funcnode in nested:
|
for funcnode in nested:
|
||||||
# Check for captured variables in this function.
|
# Check for captured variables in this function.
|
||||||
captured = set()
|
captured = set()
|
||||||
collect_captured(funcnode, declared, captured)
|
collect_captured(funcnode, assigned, captured)
|
||||||
for name in captured:
|
for name in captured:
|
||||||
# We want to report the outermost capturing function
|
# We want to report the outermost capturing function
|
||||||
# (since that is where the workaround will need to be
|
# (since that is where the workaround will need to be
|
||||||
# added), and the variable declaration. Just one report
|
# added), and the topmost assignment to the variable.
|
||||||
# per capturing function per variable will do.
|
# Just one report per capturing function per variable
|
||||||
results.append(make_result(funcnode, name, declared[name]))
|
# will do.
|
||||||
|
results.append(make_result(funcnode, name, assigned[name]))
|
||||||
|
|
||||||
# Check each node in the function body in case it
|
# Check each node in the function body in case it
|
||||||
# contains another 'for' loop.
|
# contains another 'for' loop.
|
||||||
@ -63,14 +64,15 @@ def check_loop(ast, results):
|
|||||||
for child in childnodes:
|
for child in childnodes:
|
||||||
check_ast(funcnode, results)
|
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
|
Collect the names assigned in this loop, not including names
|
||||||
names declared in nested functions. Also collect the nodes of
|
assigned in nested functions. Also collect the nodes of functions
|
||||||
functions that are nested one level deep.
|
that are nested one level deep.
|
||||||
"""
|
"""
|
||||||
if isinstance(ast, AssName):
|
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:
|
else:
|
||||||
childnodes = ast.getChildNodes()
|
childnodes = ast.getChildNodes()
|
||||||
if isinstance(ast, (Lambda, Function)):
|
if isinstance(ast, (Lambda, Function)):
|
||||||
@ -83,24 +85,23 @@ def collect_declared_and_nested(ast, declared, nested):
|
|||||||
|
|
||||||
for child in childnodes:
|
for child in childnodes:
|
||||||
if isinstance(ast, Node):
|
if isinstance(ast, Node):
|
||||||
collect_declared_and_nested(child, declared, nested)
|
collect_assigned_and_nested(child, assigned, nested)
|
||||||
|
|
||||||
def collect_captured(ast, declared, captured):
|
def collect_captured(ast, assigned, captured):
|
||||||
"""Collect any captured variables that are also in declared."""
|
"""Collect any captured variables that are also in assigned."""
|
||||||
if isinstance(ast, Name):
|
if isinstance(ast, Name):
|
||||||
if ast.name in declared:
|
if ast.name in assigned:
|
||||||
captured.add(ast.name)
|
captured.add(ast.name)
|
||||||
else:
|
else:
|
||||||
childnodes = ast.getChildNodes()
|
childnodes = ast.getChildNodes()
|
||||||
|
|
||||||
if isinstance(ast, (Lambda, Function)):
|
if isinstance(ast, (Lambda, Function)):
|
||||||
# Formal parameters of the function are excluded from
|
# Formal parameters of the function are excluded from
|
||||||
# captures we care about in subnodes of the function body.
|
# captures we care about in subnodes of the function body.
|
||||||
new_declared = declared.copy()
|
new_assigned = assigned.copy()
|
||||||
remove_argnames(ast.argnames, new_declared)
|
remove_argnames(ast.argnames, new_assigned)
|
||||||
|
|
||||||
for child in childnodes[len(ast.defaults):]:
|
for child in childnodes[len(ast.defaults):]:
|
||||||
collect_captured(child, declared, captured)
|
collect_captured(child, assigned, captured)
|
||||||
|
|
||||||
# The default argument expressions are "outside" the
|
# The default argument expressions are "outside" the
|
||||||
# function, even though they are children of the
|
# function, even though they are children of the
|
||||||
@ -109,7 +110,7 @@ def collect_captured(ast, declared, captured):
|
|||||||
|
|
||||||
for child in childnodes:
|
for child in childnodes:
|
||||||
if isinstance(ast, Node):
|
if isinstance(ast, Node):
|
||||||
collect_captured(child, declared, captured)
|
collect_captured(child, assigned, captured)
|
||||||
|
|
||||||
|
|
||||||
def remove_argnames(names, fromset):
|
def remove_argnames(names, fromset):
|
||||||
@ -132,7 +133,7 @@ def report(out, path, results):
|
|||||||
if isinstance(r, SyntaxError):
|
if isinstance(r, SyntaxError):
|
||||||
print >>out, path + (" NOT ANALYSED due to syntax error: %s" % r)
|
print >>out, path + (" NOT ANALYSED due to syntax error: %s" % r)
|
||||||
else:
|
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):
|
def check(sources, out):
|
||||||
class Counts:
|
class Counts:
|
||||||
|
Reference in New Issue
Block a user