mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-04-14 06:06:40 +00:00
check-miscaptures.py: Python doesn't really have declarations; report the topmost assignment. refs #1555
This commit is contained in:
parent
1c6fe1d230
commit
5359c24e99
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user