Skip to content

Commit 131c08e

Browse files
authored
Merge pull request github#5939 from max-schaefer/js/set-constructor-args
Approved by esbena
2 parents f4f2578 + 6e34784 commit 131c08e

9 files changed

+98
-0
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<p>
8+
The <code>Set</code> constructor accepts an arbitrary number of arguments, but only the first one
9+
is used to construct the set. The remaining arguments are ignored.
10+
Code that invokes the <code>Set</code> constructor with multiple arguments is therefore likely to
11+
be incorrect.
12+
</p>
13+
</overview>
14+
15+
<recommendation>
16+
<p>
17+
Only pass a single argument to the <code>Set</code> constructor, which should be an iterable object
18+
(such as an array).
19+
</p>
20+
</recommendation>
21+
22+
<example>
23+
<p>
24+
The following example creates a set containing the vowels in the English language, and defines
25+
a function that returns a boolean indicating whether a given character is a vowel:
26+
</p>
27+
<sample src="examples/MultipleArgumentsToSetConstructorBad.js"/>
28+
29+
<p>
30+
However, this code does not work as intended: the <code>Set</code> constructor ignores all but
31+
the first argument, so the <code>vowels</code> set only contains the letter <code>a</code>.
32+
</p>
33+
34+
<p>
35+
Instead, the list of vowels should be wrapped into an array:
36+
</p>
37+
<sample src="examples/MultipleArgumentsToSetConstructorGood.js"/>
38+
</example>
39+
40+
<references>
41+
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/Set">MDN Web Docs: Set() constructor</a></li>
42+
</references>
43+
</qhelp>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* @name Multiple arguments to `Set` constructor
3+
* @description The `Set` constructor ignores all but the first argument, so passing multiple
4+
* arguments may indicate a mistake.
5+
* @kind problem
6+
* @problem.severity warning
7+
* @precision high
8+
* @id js/multiple-arguments-to-set-constructor
9+
* @tags correctness
10+
*/
11+
12+
import javascript
13+
14+
from DataFlow::NewNode newSet, DataFlow::Node ignoredArg
15+
where
16+
newSet = DataFlow::globalVarRef("Set").getAnInstantiation() and
17+
(
18+
ignoredArg = newSet.getArgument(any(int n | n > 0))
19+
or
20+
ignoredArg = newSet.getASpreadArgument()
21+
)
22+
select ignoredArg, "All but the first argument to the Set constructor are ignored."
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const vowels = new Set('a', 'e', 'i', 'o', 'u');
2+
3+
function isVowel(char) {
4+
return vowels.has(char.toLowerCase());
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const vowels = new Set(['a', 'e', 'i', 'o', 'u']);
2+
3+
function isVowel(char) {
4+
return vowels.has(char.toLowerCase());
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
| MultipleArgumentsToSetConstructorBad.js:1:29:1:31 | 'e' | All but the first argument to the Set constructor are ignored. |
2+
| MultipleArgumentsToSetConstructorBad.js:1:34:1:36 | 'i' | All but the first argument to the Set constructor are ignored. |
3+
| MultipleArgumentsToSetConstructorBad.js:1:39:1:41 | 'o' | All but the first argument to the Set constructor are ignored. |
4+
| MultipleArgumentsToSetConstructorBad.js:1:44:1:46 | 'u' | All but the first argument to the Set constructor are ignored. |
5+
| tst.js:3:12:3:13 | xs | All but the first argument to the Set constructor are ignored. |
6+
| tst.js:3:19:3:20 | ys | All but the first argument to the Set constructor are ignored. |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
experimental/StandardLibrary/MultipleArgumentsToSetConstructor.ql
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const vowels = new Set('a', 'e', 'i', 'o', 'u');
2+
3+
function isVowel(char) {
4+
return vowels.has(char.toLowerCase());
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const vowels = new Set(['a', 'e', 'i', 'o', 'u']);
2+
3+
function isVowel(char) {
4+
return vowels.has(char.toLowerCase());
5+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
let xs = [1, 2, 3];
2+
let ys = [4, 5, 6];
3+
new Set(...xs, ...ys); // NOT OK
4+
new Set([...xs, ...ys]); // OK
5+
new Set(xs); // OK
6+
new Set(); // OK

0 commit comments

Comments
 (0)