Technologicat / pyan

Static call graph generator. The official Python 3 version. Development repo.
GNU General Public License v2.0
323 stars 56 forks source link

Crash on lambda parameter #61

Open CypherGrue opened 3 years ago

CypherGrue commented 3 years ago

Running pyan 1.1.1 on following trivial example def func(x=lambda a,b:a): return x(1,2)

Gives following error

File "/usr/local/lib/python3.9/site-packages/pyan/analyzer.py", line 468, in visit_Lambda with ExecuteInInnerScope(self, "lambda"): File "/usr/local/lib/python3.9/site-packages/pyan/anutils.py", line 224, in enter raise ValueError("Unknown scope '%s'" % (inner_ns)) ValueError: Unknown scope 'test2.func.lambda'

Technologicat commented 3 years ago

Hi,

It seems I've missed a corner case, and it's analyzing the lambda in the wrong scope. Good catch!

The error manifests in this particular way, because the scope analysis (see analyze_scopes), which uses the symtable stdlib module, populates the scope hierarchy correctly, but the analyzer proper makes a mistake when it thinks the lambda is inside the function scope.

The actual problem is that in visit_FunctionDef, the call to analyze_arguments occurs after the analyzer enters the function scope.

Fixing this is a bit complicated due to how the analyzer is implemented. The call to generate_args_nodes must run inside the function scope, so that the names being defined as function parameters will be scoped correctly. However, the values should be analyzed in the context of the surrounding scope. Currently there's no easy way to make the analyzer do this.

There's also a related old (but still live) bug that Pyan doesn't currently distinguish between different lambdas in the same parent scope, because everything needs a name for the call graph, and all lambdas are named "lambda". Right now I don't recall if symtable names the scopes like that, or if Pyan itself does. If the latter, we should be able to use the lineno attribute from the AST node to uniqify the names. But in the worst case, we need to reimplement what symtable is already doing. Scope analysis in Python is rather painful.

So, in summary: thanks for reporting! Fixing this requires a bit more work than I have time for right now, but this is definitely something to fix in a future release.