-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Python: Modernize the Loop Variable Capture query #19165
Python: Modernize the Loop Variable Capture query #19165
Conversation
8eb7c36
to
c37809a
Compare
QHelp previews: python/ql/src/Variables/LoopVariableCapture/LoopVariableCapture.qhelpLoop variable captureIn Python, a nested function or lambda expression that captures a variable from its surrounding scope is a late-binding closure, meaning that the value of the variable is determined when the closure is called, not when it is created. Care must be taken when the captured variable is a loop variable. If the closure is called after the loop ends, it will use the value of the variable on the last iteration of the loop, rather than the value at the iteration at which it was created. RecommendationEnsure that closures that capture loop variables aren't used outside of a single iteration of the loop. To capture the value of a loop variable at the time the closure is created, use a default parameter, or ExampleIn the following (BAD) example, a # BAD: The loop variable `i` is captured.
tasks = []
for i in range(5):
tasks.append(lambda: print(i))
# This will print `4,4,4,4,4`, rather than `0,1,2,3,4` as likely intended.
for t in tasks:
t() In the following (GOOD) example, each closure has an # GOOD: A default parameter is used, so the variable `i` is not being captured.
tasks = []
for i in range(5):
tasks.append(lambda i=i: print(i))
# This will print `0,1,2,3,4``.
for t in tasks:
t() In the following (GOOD) example, import functools
# GOOD: `functools.partial` takes care of capturing the _value_ of `i`.
tasks = []
for i in range(5):
tasks.append(functools.partial(lambda i: print(i), i))
# This will print `0,1,2,3,4``.
for t in tasks:
t() References
|
if capturing instanceof Lambda then descr = "lambda" else descr = "function" | ||
select capturing, source, sink, | ||
"This " + descr + " captures the loop variable $@, and may escape the loop by being stored at $@.", | ||
loop, var.getId(), sink, "this location" |
Check warning
Code scanning / CodeQL
Alert message style violation Warning
3112a14
to
e08072d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other than a small suggestion, this looks good to me. Good use of data-flow! 👍
Co-authored-by: Taus <tausbn@github.com>
Since I made significant changes to the qhelp, I will also request a docs reveiw. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@joefarebrother 👋🏻 - approving this on behalf of Docs 👍🏻
I tried to improve the query description slightly and I've made a suggestion. Let me know what you think.
python/ql/src/Variables/LoopVariableCapture/LoopVariableCapture.ql
Outdated
Show resolved
Hide resolved
Co-authored-by: mc <42146119+mchammer01@users.noreply.github.com>
Rewrites the Loop variable capture query to use dataflow rather than PointsTo.