add query for detecting ignored calls to Array.prototype.concat

This commit is contained in:
Erik Krogh Kristensen
2019-10-10 16:20:27 +02:00
parent 4b27b2ac05
commit 5489a80372
8 changed files with 113 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
The <code>concat</code> method on is pure and does not modify any of the input
arrays. It is therefore generally an error to ignore the return value from a
call to <code>concat</code>.
</p>
</overview>
<recommendation>
<p>
Use the returned value from the call to <code>concat</code>.
</p>
</recommendation>
<example>
<p>
In the following example a function <code>extend</code> is defined. The
function uses the <code>concat</code> method to add elements to the
<code>arr</code> array. However, the <code>extend</code> function has no
effect as the return value from <code>concat</code> is ignored.
</p>
<sample src="examples/IgnoreConcat.js" />
<p>
Assigning the returned value from the call to <code>concat</code> to the
<code>arr</code> variable fixes the error.
</p>
<sample src="examples/IgnoreConcatFixed.js" />
</example>
<references>
<li>Mozilla Developer Network: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat">Array concat</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,42 @@
/**
* @name Ignoring return from concat
* @description The concat method does not modify an array, ignoring the result of a call to concat is therefore generally an error.
* @kind problem
* @problem.severity warning
* @id js/ignore-result-from-concat
* @tags maintainability,
* correctness
* @precision high
*/
import javascript
import Expressions.ExprHasNoEffect
DataFlow::SourceNode array(DataFlow::TypeTracker t) {
t.start() and
result instanceof DataFlow::ArrayCreationNode
or
exists (DataFlow::TypeTracker t2 |
result = array(t2).track(t2, t)
)
}
DataFlow::SourceNode array() { result = array(DataFlow::TypeTracker::end()) }
predicate isArrayMethod(DataFlow::MethodCallNode call) {
call.getReceiver().getALocalSource() = array()
}
predicate isIncomplete(DataFlow::Node node) {
any(DataFlow::Incompleteness cause | node.analyze().getAValue().isIndefinite(cause)) != "global"
}
from DataFlow::CallNode call
where
isArrayMethod(call) and
call.getCalleeName() = "concat" and
call.getNumArgument() = 1 and
(call.getArgument(0).getALocalSource() = array() or isIncomplete(call.getArgument(0))) and
not call.getArgument(0).asExpr().(ArrayExpr).getSize() = 0 and
inVoidContext(call.asExpr())
select call, "Return value from call to concat ignored."

View File

@@ -0,0 +1,5 @@
var arr = [1,2,3];
function extend(others) {
arr.concat(others);
}

View File

@@ -0,0 +1,5 @@
var arr = [1,2,3];
function extend(others) {
arr = arr.concat(others);
}