Skip to content

Commit 5489a80

Browse files
committed
add query for detecting ignored calls to Array.prototype.concat
1 parent 4b27b2a commit 5489a80

File tree

8 files changed

+113
-0
lines changed

8 files changed

+113
-0
lines changed

change-notes/1.23/analysis-javascript.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
| Use of returnless function (`js/use-of-returnless-function`) | maintainability, correctness | Highlights calls where the return value is used, but the callee never returns a value. Results are shown on LGTM by default. |
2424
| Useless regular expression character escape (`js/useless-regexp-character-escape`) | correctness, security, external/cwe/cwe-20 | Highlights regular expression strings with useless character escapes, indicating a possible violation of [CWE-20](https://cwe.mitre.org/data/definitions/20.html). Results are shown on LGTM by default. |
2525
| Unreachable method overloads (`js/unreachable-method-overloads`) | correctness, typescript | Highlights method overloads that are impossible to use from client code. Results are shown on LGTM by default. |
26+
| Ignoring return from concat (`js/ignore-return-from-concat`) | maintainability, correctness | Highlights calls to the concat method on array where the return value is ignored. Results are shown on LGTM by default. |
2627

2728
## Changes to existing queries
2829

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>
7+
The <code>concat</code> method on is pure and does not modify any of the input
8+
arrays. It is therefore generally an error to ignore the return value from a
9+
call to <code>concat</code>.
10+
</p>
11+
12+
</overview>
13+
<recommendation>
14+
15+
<p>
16+
Use the returned value from the call to <code>concat</code>.
17+
</p>
18+
19+
</recommendation>
20+
<example>
21+
22+
<p>
23+
In the following example a function <code>extend</code> is defined. The
24+
function uses the <code>concat</code> method to add elements to the
25+
<code>arr</code> array. However, the <code>extend</code> function has no
26+
effect as the return value from <code>concat</code> is ignored.
27+
</p>
28+
29+
<sample src="examples/IgnoreConcat.js" />
30+
31+
<p>
32+
Assigning the returned value from the call to <code>concat</code> to the
33+
<code>arr</code> variable fixes the error.
34+
</p>
35+
36+
<sample src="examples/IgnoreConcatFixed.js" />
37+
38+
</example>
39+
<references>
40+
41+
<li>Mozilla Developer Network: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat">Array concat</a>.</li>
42+
43+
</references>
44+
</qhelp>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* @name Ignoring return from concat
3+
* @description The concat method does not modify an array, ignoring the result of a call to concat is therefore generally an error.
4+
* @kind problem
5+
* @problem.severity warning
6+
* @id js/ignore-result-from-concat
7+
* @tags maintainability,
8+
* correctness
9+
* @precision high
10+
*/
11+
12+
import javascript
13+
import Expressions.ExprHasNoEffect
14+
15+
DataFlow::SourceNode array(DataFlow::TypeTracker t) {
16+
t.start() and
17+
result instanceof DataFlow::ArrayCreationNode
18+
or
19+
exists (DataFlow::TypeTracker t2 |
20+
result = array(t2).track(t2, t)
21+
)
22+
}
23+
24+
DataFlow::SourceNode array() { result = array(DataFlow::TypeTracker::end()) }
25+
26+
predicate isArrayMethod(DataFlow::MethodCallNode call) {
27+
call.getReceiver().getALocalSource() = array()
28+
}
29+
30+
predicate isIncomplete(DataFlow::Node node) {
31+
any(DataFlow::Incompleteness cause | node.analyze().getAValue().isIndefinite(cause)) != "global"
32+
}
33+
34+
from DataFlow::CallNode call
35+
where
36+
isArrayMethod(call) and
37+
call.getCalleeName() = "concat" and
38+
call.getNumArgument() = 1 and
39+
(call.getArgument(0).getALocalSource() = array() or isIncomplete(call.getArgument(0))) and
40+
not call.getArgument(0).asExpr().(ArrayExpr).getSize() = 0 and
41+
inVoidContext(call.asExpr())
42+
select call, "Return value from call to concat ignored."
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
var arr = [1,2,3];
2+
3+
function extend(others) {
4+
arr.concat(others);
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
var arr = [1,2,3];
2+
3+
function extend(others) {
4+
arr = arr.concat(others);
5+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
| tst.js:3:1:3:19 | arr.concat([1,2,3]) | Return value from call to concat ignored. |
2+
| tst.js:5:1:5:15 | arr.concat(arr) | Return value from call to concat ignored. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Statements/IgnoreConcatReturn.ql
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
var arr = [1,2,3];
2+
3+
arr.concat([1,2,3]); // NOT OK!
4+
5+
arr.concat(arr); // NOT OK!
6+
7+
console.log(arr.concat([1,2,3]));
8+
9+
arr.concat(null);
10+
arr.concat();
11+
arr.concat([]);
12+
13+
({concat: Array.prototype.concat}.concat(arr));

0 commit comments

Comments
 (0)