Skip to content

Commit bcafe53

Browse files
authored
Merge pull request github#5944 from RasmusWL/async-api-graph-tests
Approved by tausbn
2 parents 9b84a8e + c4e244e commit bcafe53

File tree

5 files changed

+39
-2
lines changed

5 files changed

+39
-2
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm,codescanning
2+
* API graph nodes now contain a `getAwaited()` member predicate, for getting the result of awaiting an item, such as `await foo`.

python/ql/src/semmle/python/ApiGraphs.qll

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ module API {
9797
*/
9898
Node getASubclass() { result = getASuccessor(Label::subclass()) }
9999

100+
/**
101+
* Gets a node representing the result from awaiting this node.
102+
*/
103+
Node getAwaited() { result = getASuccessor(Label::await()) }
104+
100105
/**
101106
* Gets a string representation of the lexicographically least among all shortest access paths
102107
* from the root to this node.
@@ -469,6 +474,14 @@ module API {
469474
exists(DataFlow::Node superclass | pred.flowsTo(superclass) |
470475
ref.asExpr().(ClassExpr).getABase() = superclass.asExpr()
471476
)
477+
or
478+
// awaiting
479+
exists(Await await, DataFlow::Node awaitedValue |
480+
lbl = Label::await() and
481+
ref.asExpr() = await and
482+
await.getValue() = awaitedValue.asExpr() and
483+
pred.flowsTo(awaitedValue)
484+
)
472485
)
473486
or
474487
// Built-ins, treated as members of the module `builtins`
@@ -585,5 +598,9 @@ private module Label {
585598
/** Gets the `return` edge label. */
586599
string return() { result = "getReturn()" }
587600

601+
/** Gets the `subclass` edge label. */
588602
string subclass() { result = "getASubclass()" }
603+
604+
/** Gets the `await` edge label. */
605+
string await() { result = "getAwaited()" }
589606
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import pkg # $ use=moduleImport("pkg")
2+
3+
async def foo():
4+
coro = pkg.async_func() # $ use=moduleImport("pkg").getMember("async_func").getReturn()
5+
coro # $ use=moduleImport("pkg").getMember("async_func").getReturn()
6+
result = await coro # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited()
7+
result # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited()
8+
return result # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited()
9+
10+
async def bar():
11+
result = await pkg.async_func() # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited()
12+
return result # $ use=moduleImport("pkg").getMember("async_func").getReturn().getAwaited()
13+
14+
def check_annotations():
15+
# Just to make sure how annotations should look like :)
16+
result = pkg.sync_func() # $ use=moduleImport("pkg").getMember("sync_func").getReturn()
17+
return result # $ use=moduleImport("pkg").getMember("sync_func").getReturn()
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
semmle-extractor-options: --lang=3
1+
semmle-extractor-options: --lang=3 --max-import-depth=1

python/ql/test/experimental/dataflow/ApiGraphs/use.ql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ class ApiUseTest extends InlineExpectationsTest {
1313
l = n.getLocation() and
1414
// Module variable nodes have no suitable location, so it's best to simply exclude them entirely
1515
// from the inline tests.
16-
not n instanceof DataFlow::ModuleVariableNode
16+
not n instanceof DataFlow::ModuleVariableNode and
17+
exists(l.getFile().getRelativePath())
1718
}
1819

1920
override predicate hasActualResult(Location location, string element, string tag, string value) {

0 commit comments

Comments
 (0)