JS: add query for detecting ignored calls to Array.prototype.concat#2203
JS: add query for detecting ignored calls to Array.prototype.concat#2203semmle-qlci merged 11 commits intogithub:masterfrom
Conversation
|
Have you tried backtracking from the receiver of a |
max-schaefer
left a comment
There was a problem hiding this comment.
Taking a step back, would it perhaps make sense to generalise this query to look for any array method that is annotated with @nosideeffects in our externs definitions? I imagine, for instance, that developers might occasionally use array.slice where they meant array.splice (the former, of course, not having any side effects).
| 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 |
There was a problem hiding this comment.
What about the receiver being the empty array?
There was a problem hiding this comment.
The call does nothing if the input is literally the empty array, and I got tired of looking at tests like these.
There was a problem hiding this comment.
I'm not sure I understand your answer. I do agree with this whitelisting. I was just wondering about the symmetric case of [].concat(xs), which I saw in a few tests.
There was a problem hiding this comment.
Oh. I misread your comment, sorry. I was thinking about the way I wrote the query and thought you were talking about the argument not the receiver.
You have a good point with the receiver being the empty array. I think we should remove those.
| isArrayMethod(call) and | ||
| call.getCalleeName() = "concat" and |
There was a problem hiding this comment.
I think those two lines can be simplified to
| isArrayMethod(call) and | |
| call.getCalleeName() = "concat" and | |
| call = array().getAMethodCall("concat") |
(Though that won't work if you do type backtracking instead, which I think is a good idea.)
|
Is this PR ready for an editorial review or shall I hold off until all the technical discussions have taken place? |
Hold off for now. |
|
I implemented the backtracking instead, and it did indeed fix the performance of We risk a performance-blowup if the With the backtracking there was not pretty way to restrict the I'm doing a new performance evaluation on defaults right now. |
I added the I don't think methods like The remaining Array methods are |
max-schaefer
left a comment
There was a problem hiding this comment.
Apologies, I should have explained by suggestion about @nosideeffects a little more clearly: we include the externs definitions in every snapshot, so instead of hard-coding a list of methods that are known to be side-effect free in the query, you can check for a method name m whether there is an externs declaration for a method Array.prototype.m that is annotated as @nosideeffects.
| import Expressions.ExprHasNoEffect | ||
|
|
||
| DataFlow::SourceNode callsArray(DataFlow::TypeBackTracker t, DataFlow::MethodCallNode call) { | ||
| isIgnoredPureArrayCall(call) and |
There was a problem hiding this comment.
It may be better to distribute this into the first disjunct. It is implied by the recursive call in the second disjunct.
There was a problem hiding this comment.
It definitely makes it more readable, as it now follows the usual type-tracking pattern.
I did find that that part of the code, and decided not to use it. |
Co-Authored-By: Max Schaefer <54907921+max-schaefer@users.noreply.github.com>
max-schaefer
left a comment
There was a problem hiding this comment.
LGTM modulo one minor niggle and adding the query to a legacy suite.
@mchammer01, I think this is ready for doc review.
| | 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. | | ||
| | 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. | | ||
| | 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. | | ||
| | 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. | |
There was a problem hiding this comment.
Needs updating now that the query is more general.
|
Thanks @max-schaefer - I'll review this first thing on Thursday (not in tomorrow). |
mchammer01
left a comment
There was a problem hiding this comment.
@erik-krogh - editorial eview completed.
Lookgs good, I've made a few minor comments/suggestions.
| <overview> | ||
| <p> | ||
| The <code>concat</code>, <code>join</code> and <code>slice</code> methods are | ||
| pure and do not modify any of the inputs or the array the method was called |
There was a problem hiding this comment.
Just a question here. Are you deliberately using the past here? ….was called...
There was a problem hiding this comment.
no..
I'm changing it to is called.
| | 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. | | ||
| | 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. | | ||
| | 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. | | ||
| | 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. | |
There was a problem hiding this comment.
Shouldn't the name be Ignoring result from pure array method, and shouldn't the description mention the other two methods?
There was a problem hiding this comment.
Correct. I had forgot to change that when the scope of the query changed.
Co-Authored-By: mc <42146119+mchammer01@users.noreply.github.com>
|
Almost there! Could you add the query to https://github.com/Semmle/ql/blob/master/javascript/config/suites/javascript/correctness-core, please? |
|
@mchammer01 a final review? |
|
I did a final evaluation. Performance and results are still looking good. |
|
Thanks for the ping @erik-krogh - I have now approved this PR. |
A simple and precise query for detecting ignored calls to
Array.prototype.concat.These are the current results: https://lgtm.com/query/6200487140988633440/
It started out as a query for detecting ignored calls to pure functions, but that query had too many weird results.
Type-tracking is used to detect if the receiver is an array, otherwise the query would flag various
concatmethods from different libraries.Type-tracking is similarly used to detect if the argument to
Array.prototype.concatis an array.This might be a bit too restrictive, and I'm considering removing that restriction.
But it by keeping it the query doesn't flag a log of unit-tests of the
concatmethod itself.These are the results we remove from restricting the argument to be an array:
https://lgtm.com/query/1009789502976493454/
(An example of why I'm considering removing that restriction: Someone called
concatinstead ofpush.)Performance is good.
There is only a slight outlier with
closure-library, where the type-tracking of arrays take a while.