Skip to content

Commit eb82b17

Browse files
committed
JS: QHelp and a bit of qldoc
1 parent f398247 commit eb82b17

File tree

4 files changed

+50
-34
lines changed

4 files changed

+50
-34
lines changed

javascript/ql/src/Expressions/MissingAwait.qhelp

Lines changed: 16 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,62 +4,45 @@
44
<qhelp>
55
<overview>
66
<p>
7-
In JavaScript, <code>async</code> functions always return a Promise object.
8-
</p>
9-
10-
<p>
11-
PLACEHOLDER
7+
In JavaScript, <code>async</code> functions always return a promise object.
8+
To obtain the underlying value of the promise, the `await` operator or a call to `then` should be used.
9+
Attempting to use a Promise object instead of its underlying value can lead to expected behavior.
1210
</p>
1311

1412
</overview>
1513
<recommendation>
1614

1715
<p>
18-
PLACEHOLDER
16+
Use the `await` operator to get the value contained in the promise.
17+
Alternatively, call `then` on the promise and use the value passed to the callback.
1918
</p>
2019

2120
</recommendation>
2221
<example>
2322

2423
<p>
25-
PLACEHOLDER
26-
</p>
27-
28-
<sample src="examples/ImplicitOperandConversion.js" />
29-
30-
<p>
31-
PLACEHOLDER
32-
</p>
33-
34-
<p>
35-
PLACEHOLDER
24+
In the following example, the <code>getData</code> function returns a promise,
25+
and the caller checks if the returned promise is <code>null</code>:
3626
</p>
3727

38-
<sample src="examples/ImplicitOperandConversionGood.js" />
28+
<sample src="examples/MissingAwait.js" />
3929

4030
<p>
41-
PLACEHOLDER
31+
However, the null check does not work as expected. The <code>return null</code> statement
32+
on line 2 actually returns a <em>promise</em> containing the <code>null</code> value.
33+
Since the promise object itself is not equal to <code>null</code>, the error check is bypassed.
4234
</p>
4335

44-
<sample src="examples/ImplicitOperandConversion2.js" />
45-
4636
<p>
47-
PLACEHOLDER
37+
The issue can be corrected by inserting <code>await</code> before the promise:
4838
</p>
4939

50-
<sample src="examples/ImplicitOperandConversion2Good.js" />
51-
52-
<p>
53-
PLACEHOLDER
54-
</p>
55-
56-
<sample src="examples/ImplicitOperandConversion2Good2.js" />
40+
<sample src="examples/MissingAwaitGood.js" />
5741

5842
</example>
5943
<references>
60-
61-
62-
<li>Ecma International, <i>ECMAScript Language Definition</i>, 5.1 Edition, Section 9. ECMA, 2011.</li>
63-
44+
<li>MDN: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises">Using promises</a></li>
45+
<li>MDN: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function">Async functions</a></li>
46+
<li>MDN: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await">Await operator</a></li>
6447
</references>
6548
</qhelp>

javascript/ql/src/Expressions/MissingAwait.ql

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* @name Missing await
3-
* @description Using a promise without awaiting its result can cause unexpected behavior.
3+
* @description Using a promise without awaiting its result can lead to unexpected behavior.
44
* @kind problem
55
* @problem.severity warning
66
* @id js/missing-await
@@ -10,7 +10,12 @@
1010

1111
import javascript
1212

13+
/**
14+
* Holds if `call` is a call to an `async` function.
15+
*/
1316
predicate isAsyncCall(DataFlow::CallNode call) {
17+
// If a callee is known, and all known callees are async, assume all
18+
// possible callees are async.
1419
forex(Function callee | call.getACallee() = callee | callee.isAsync())
1520
}
1621

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
async function getData(id) {
2+
let req = await fetch(`https://example.com/data?id=${id}`);
3+
if (!req.ok) return null;
4+
return req.json();
5+
}
6+
7+
async function showData(id) {
8+
let data = getData(id);
9+
if (data == null) {
10+
console.warn("No data for: " + id);
11+
return;
12+
}
13+
// ...
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
async function getData(id) {
2+
let req = await fetch(`https://example.com/data?id=${id}`);
3+
if (!req.ok) return null;
4+
return req.json();
5+
}
6+
7+
async function showData(id) {
8+
let data = await getData(id);
9+
if (data == null) {
10+
console.warn("No data for: " + id);
11+
return;
12+
}
13+
// ...
14+
}

0 commit comments

Comments
 (0)