Skip to content

Commit a8a181a

Browse files
committed
Python: adjust logic and add tests
Due to the way paths a re printed, the tests look surprising
1 parent 149b235 commit a8a181a

File tree

3 files changed

+26
-11
lines changed

3 files changed

+26
-11
lines changed

python/ql/lib/semmle/python/ApiGraphs.qll

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -502,20 +502,23 @@ module API {
502502
// - `awaitedValue` is `l`
503503
// - `result` is `l` (should perhaps be `x`, but that should really be a read)
504504
exists(AsyncFor asyncFor |
505-
result.asExpr() = asyncFor.getTarget() and
506-
// Morally, we should perhaps use asyncFor.getIter() = awaitedValue.asExpr(),
507-
// but that is actually behind a read step rather than a flow step.
508-
asyncFor.getTarget() = awaitedValue.asExpr()
505+
result.asExpr() = asyncFor.getIter() and
506+
// To consider `x` the result of awaiting, we would use asyncFor.getTarget() = awaitedValue.asExpr(),
507+
// but that is behind a read step rather than a flow step.
508+
asyncFor.getIter() = awaitedValue.asExpr()
509509
)
510510
or
511511
// `async with x as y`
512512
// - `awaitedValue` is `x`
513-
// - `result` is `x` (should probably be `y` but it might not exist)
513+
// - `result` is `x` and `y` if it exists
514514
exists(AsyncWith asyncWith |
515515
result.asExpr() = asyncWith.getContextExpr() and
516-
// Morally, we should perhaps use asyncWith.getOptionalVars() = awaitedValue.asExpr(),
517-
// but that might not exist.
518-
asyncWith.getContextExpr() = awaitedValue.asExpr()
516+
awaitedValue.asExpr() in [
517+
// `x`
518+
asyncWith.getContextExpr(),
519+
// `y`, if it exists
520+
asyncWith.getOptionalVars()
521+
]
519522
)
520523
}
521524

python/ql/lib/semmle/python/frameworks/Asyncpg.qll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,17 +83,17 @@ private module Asyncpg {
8383
// - `awaitedValue` is local source of `l`
8484
// - `result` is `l`
8585
exists(AsyncFor asyncFor, DataFlow::Node awaited |
86-
result.asExpr() = asyncFor.getTarget() and
86+
result.asExpr() = asyncFor.getIter() and
8787
asyncFor.getIter() = awaited.asExpr() and
8888
awaited.getALocalSource() = awaitedValue
8989
)
9090
or
9191
// `async with x as y`
9292
// - `awaitedValue` is local source of `x`
93-
// - `result` is `x`
93+
// - `result` is `x` and `y`
9494
exists(AsyncWith asyncWith, DataFlow::Node awaited |
9595
result.asExpr() = asyncWith.getContextExpr() and
96-
asyncWith.getOptionalVars() = awaited.asExpr() and
96+
awaited.asExpr() in [asyncWith.getContextExpr(), asyncWith.getOptionalVars()] and
9797
awaited.getALocalSource() = awaitedValue
9898
)
9999
}

python/ql/test/experimental/dataflow/ApiGraphs/async_test.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,18 @@ async def bar():
1111
result = await pkg.async_func() # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited()
1212
return result # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited()
1313

14+
async def test_async_with():
15+
async with pkg.async_func() as result: # $ use=moduleImport("pkg").getMember("async_func").getReturn()
16+
return result # $ use=moduleImport("pkg").getMember("async_func").getReturn()
17+
18+
async def test_async_for():
19+
async for _ in pkg.async_func(): # $ use=moduleImport("pkg").getMember("async_func").getReturn()
20+
pass
21+
22+
coro = pkg.async_func() # $ use=moduleImport("pkg").getMember("async_func").getReturn()
23+
async for _ in coro: # $ use=moduleImport("pkg").getMember("async_func").getReturn()
24+
pass
25+
1426
def check_annotations():
1527
# Just to make sure how annotations should look like :)
1628
result = pkg.sync_func() # $ use=moduleImport("pkg").getMember("sync_func").getReturn()

0 commit comments

Comments
 (0)