Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a30f991

Browse files
committed
JS: Add query for missing await
1 parent 3d8c35e commit a30f991

5 files changed

Lines changed: 183 additions & 0 deletions

File tree

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>
7+
In JavaScript, <code>async</code> functions always return a Promise object.
8+
</p>
9+
10+
<p>
11+
PLACEHOLDER
12+
</p>
13+
14+
</overview>
15+
<recommendation>
16+
17+
<p>
18+
PLACEHOLDER
19+
</p>
20+
21+
</recommendation>
22+
<example>
23+
24+
<p>
25+
PLACEHOLDER
26+
</p>
27+
28+
<sample src="examples/ImplicitOperandConversion.js" />
29+
30+
<p>
31+
PLACEHOLDER
32+
</p>
33+
34+
<p>
35+
PLACEHOLDER
36+
</p>
37+
38+
<sample src="examples/ImplicitOperandConversionGood.js" />
39+
40+
<p>
41+
PLACEHOLDER
42+
</p>
43+
44+
<sample src="examples/ImplicitOperandConversion2.js" />
45+
46+
<p>
47+
PLACEHOLDER
48+
</p>
49+
50+
<sample src="examples/ImplicitOperandConversion2Good.js" />
51+
52+
<p>
53+
PLACEHOLDER
54+
</p>
55+
56+
<sample src="examples/ImplicitOperandConversion2Good2.js" />
57+
58+
</example>
59+
<references>
60+
61+
62+
<li>Ecma International, <i>ECMAScript Language Definition</i>, 5.1 Edition, Section 9. ECMA, 2011.</li>
63+
64+
</references>
65+
</qhelp>
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* @name Missing await
3+
* @description Using a promise without awaiting its result can cause unexpected behavior.
4+
* @kind problem
5+
* @problem.severity warning
6+
* @id js/missing-await
7+
* @tags correctness
8+
* @precision high
9+
*/
10+
11+
import javascript
12+
13+
predicate isAsyncCall(DataFlow::CallNode call) {
14+
forex(Function callee | call.getACallee() = callee | callee.isAsync())
15+
}
16+
17+
/**
18+
* Holds if `node` is always a promise.
19+
*/
20+
predicate isPromise(DataFlow::SourceNode node, boolean nullable) {
21+
isAsyncCall(node) and
22+
nullable = false
23+
or
24+
not isAsyncCall(node) and
25+
node.asExpr().getType() instanceof PromiseType and
26+
nullable = true
27+
}
28+
29+
/**
30+
* Holds the result of `e` is used in a way that doesn't make sense for Promise objects.
31+
*/
32+
predicate isBadPromiseContext(Expr expr) {
33+
exists(BinaryExpr binary |
34+
expr = binary.getAnOperand() and
35+
not binary instanceof LogicalExpr and
36+
not binary instanceof InstanceofExpr
37+
)
38+
or
39+
expr = any(LogicalBinaryExpr e).getLeftOperand()
40+
or
41+
expr = any(UnaryExpr e).getOperand()
42+
or
43+
expr = any(UpdateExpr e).getOperand()
44+
}
45+
46+
string tryGetPromiseExplanation(Expr e) {
47+
result = "The value '" + e.(VarAccess).getName() + "' is always a promise."
48+
or
49+
result = "The call to '" + e.(CallExpr).getCalleeName() + "' always returns a promise."
50+
}
51+
52+
string getPromiseExplanation(Expr e) {
53+
result = tryGetPromiseExplanation(e)
54+
or
55+
not exists(tryGetPromiseExplanation(e)) and
56+
result = "This value is always a promise."
57+
}
58+
59+
from Expr expr, boolean nullable
60+
where
61+
isBadPromiseContext(expr) and
62+
isPromise(expr.flow().getImmediatePredecessor*(), nullable) and
63+
(
64+
nullable = false
65+
or
66+
expr.inNullSensitiveContext()
67+
)
68+
select expr, "Missing await. " + getPromiseExplanation(expr)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
| tst.js:8:9:8:13 | thing | Missing await. The value 'thing' is always a promise. |
2+
| tst.js:32:12:32:16 | thing | Missing await. The value 'thing' is always a promise. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Expressions/MissingAwait.ql
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
async function getThing() {
2+
return something();
3+
}
4+
5+
function useThing() {
6+
let thing = getThing();
7+
8+
if (thing === undefined) {} // NOT OK
9+
10+
if (thing == null) {} // NOT OK
11+
12+
return thing + "bar"; // NOT OK
13+
}
14+
15+
async function useThingCorrectly() {
16+
let thing = await getThing();
17+
18+
if (thing === undefined) {} // OK
19+
20+
if (thing == null) {} // OK
21+
22+
return thing + "bar"; // OK
23+
}
24+
25+
async function useThingCorrectly2() {
26+
let thing = getThing();
27+
28+
if (await thing === undefined) {} // OK
29+
30+
if (await thing == null) {} // OK
31+
32+
return thing + "bar"; // NOT OK
33+
}
34+
35+
function getThingSync() {
36+
return something();
37+
}
38+
39+
function useThingPossiblySync(b) {
40+
let thing = b ? getThing() : getThingSync();
41+
42+
if (thing === undefined) {} // OK
43+
44+
if (thing == null) {} // OK
45+
46+
return thing + "bar"; // NOT OK - but we don't flag it
47+
}

0 commit comments

Comments
 (0)