introduce backtracking, and also marking join/slice calls

This commit is contained in:
Erik Krogh Kristensen
2019-10-25 15:45:35 +02:00
parent 5489a80372
commit 5b26d03f1c
8 changed files with 55 additions and 48 deletions

View File

@@ -4,16 +4,18 @@
<qhelp> <qhelp>
<overview> <overview>
<p> <p>
The <code>concat</code> method on is pure and does not modify any of the input The <code>concat</code>, <code>join</code> and <code>slice</code> methods are
arrays. It is therefore generally an error to ignore the return value from a pure and does not modify any of the inputs or the array the method was called
call to <code>concat</code>. on. It is therefore generally an error to ignore the return value from a call
to one of these methods.
</p> </p>
</overview> </overview>
<recommendation> <recommendation>
<p> <p>
Use the returned value from the call to <code>concat</code>. Use the returned value from the calls to <code>concat</code>, <code>join</code>
or <code>slice</code>.
</p> </p>
</recommendation> </recommendation>
@@ -26,19 +28,21 @@ function uses the <code>concat</code> method to add elements to the
effect as the return value from <code>concat</code> is ignored. effect as the return value from <code>concat</code> is ignored.
</p> </p>
<sample src="examples/IgnoreConcat.js" /> <sample src="examples/IgnoreArrayResult.js" />
<p> <p>
Assigning the returned value from the call to <code>concat</code> to the Assigning the returned value from the call to <code>concat</code> to the
<code>arr</code> variable fixes the error. <code>arr</code> variable fixes the error.
</p> </p>
<sample src="examples/IgnoreConcatFixed.js" /> <sample src="examples/IgnoreArrayResultFixed.js" />
</example> </example>
<references> <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> <li>Mozilla Developer Network: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat">Array concat</a>.</li>
<li>Mozilla Developer Network: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice">Array slice</a>.</li>
<li>Mozilla Developer Network: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join">Array join</a>.</li>
</references> </references>
</qhelp> </qhelp>

View File

@@ -0,0 +1,45 @@
/**
* @name Ignoring result from pure array method
* @description The array methods do not modify the array, ignoring the result of such a call is therefore generally an error.
* @kind problem
* @problem.severity warning
* @id js/ignore-array-result
* @tags maintainability,
* correctness
* @precision high
*/
import javascript
import Expressions.ExprHasNoEffect
DataFlow::SourceNode callsArray(DataFlow::TypeBackTracker t, DataFlow::MethodCallNode call) {
isIgnoredPureArrayCall(call) and
(
t.start() and
result = call.getReceiver()
or
exists(DataFlow::TypeBackTracker t2 | result = callsArray(t2, call).backtrack(t2, t))
)
}
DataFlow::SourceNode callsArray(DataFlow::MethodCallNode call) {
result = callsArray(DataFlow::TypeBackTracker::end(), call)
}
predicate isIgnoredPureArrayCall(DataFlow::MethodCallNode call) {
inVoidContext(call.asExpr()) and
(
call.getMethodName() = "concat" and
call.getNumArgument() = 1
or
call.getMethodName() = "join" and
call.getNumArgument() < 2
or
call.getMethodName() = "slice" and
call.getNumArgument() < 3
)
}
from DataFlow::MethodCallNode call
where callsArray(call) instanceof DataFlow::ArrayCreationNode
select call, "Result from call to " + call.getMethodName() + " ignored."

View File

@@ -1,42 +0,0 @@
/**
* @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."