Skip to content

Commit c4d26fd

Browse files
StyleShitamareshsm
andauthored
fix: use-isnan doesn't report on SequenceExpressions (#18059)
* fix: `use-isnan` doesn't report on `SequenceExpression`s Closes #18058 * kinda docs? * fix suggestions * more cases * fix tests --------- Co-authored-by: Amaresh S M <[email protected]>
1 parent 6ea339e commit c4d26fd

3 files changed

Lines changed: 116 additions & 10 deletions

File tree

docs/src/rules/use-isnan.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,8 @@ var hasNaN = myArray.indexOf(NaN) >= 0;
224224
var firstIndex = myArray.indexOf(NaN);
225225

226226
var lastIndex = myArray.lastIndexOf(NaN);
227+
228+
var indexWithSequenceExpression = myArray.indexOf((doStuff(), NaN));
227229
```
228230

229231
:::

lib/rules/use-isnan.js

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,17 @@ const astUtils = require("./utils/ast-utils");
2121
* @returns {boolean} `true` if the node is 'NaN' identifier.
2222
*/
2323
function isNaNIdentifier(node) {
24-
return Boolean(node) && (
25-
astUtils.isSpecificId(node, "NaN") ||
26-
astUtils.isSpecificMemberAccess(node, "Number", "NaN")
24+
if (!node) {
25+
return false;
26+
}
27+
28+
const nodeToCheck = node.type === "SequenceExpression"
29+
? node.expressions.at(-1)
30+
: node;
31+
32+
return (
33+
astUtils.isSpecificId(nodeToCheck, "NaN") ||
34+
astUtils.isSpecificMemberAccess(nodeToCheck, "Number", "NaN")
2735
);
2836
}
2937

@@ -115,21 +123,24 @@ module.exports = {
115123
(isNaNIdentifier(node.left) || isNaNIdentifier(node.right))
116124
) {
117125
const suggestedFixes = [];
118-
const isFixable = fixableOperators.has(node.operator);
126+
const NaNNode = isNaNIdentifier(node.left) ? node.left : node.right;
127+
128+
const isSequenceExpression = NaNNode.type === "SequenceExpression";
129+
const isFixable = fixableOperators.has(node.operator) && !isSequenceExpression;
119130
const isCastable = castableOperators.has(node.operator);
120131

121132
if (isFixable) {
122133
suggestedFixes.push({
123134
messageId: "replaceWithIsNaN",
124135
fix: getBinaryExpressionFixer(node, value => `Number.isNaN(${value})`)
125136
});
126-
}
127137

128-
if (isCastable) {
129-
suggestedFixes.push({
130-
messageId: "replaceWithCastingAndIsNaN",
131-
fix: getBinaryExpressionFixer(node, value => `Number.isNaN(Number(${value}))`)
132-
});
138+
if (isCastable) {
139+
suggestedFixes.push({
140+
messageId: "replaceWithCastingAndIsNaN",
141+
fix: getBinaryExpressionFixer(node, value => `Number.isNaN(Number(${value}))`)
142+
});
143+
}
133144
}
134145

135146
context.report({

tests/lib/rules/use-isnan.js

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ ruleTester.run("use-isnan", rule, {
4949
"foo(2 / Number.NaN)",
5050
"var x; if (x = Number.NaN) { }",
5151
"x === Number[NaN];",
52+
"x === (NaN, 1)",
53+
"x === (doStuff(), NaN, 1)",
54+
"x === (doStuff(), Number.NaN, 1)",
5255

5356
//------------------------------------------------------------------------------
5457
// enforceForSwitchCase
@@ -174,6 +177,14 @@ ruleTester.run("use-isnan", rule, {
174177
code: "switch(foo) { case foo.Number.NaN: break }",
175178
options: [{ enforceForSwitchCase: true }]
176179
},
180+
{
181+
code: "switch((NaN, doStuff(), 1)) {}",
182+
options: [{ enforceForSwitchCase: true }]
183+
},
184+
{
185+
code: "switch((Number.NaN, doStuff(), 1)) {}",
186+
options: [{ enforceForSwitchCase: true }]
187+
},
177188

178189
//------------------------------------------------------------------------------
179190
// enforceForIndexOf
@@ -344,6 +355,22 @@ ruleTester.run("use-isnan", rule, {
344355
{
345356
code: "foo.lastIndexOf(Number.NaN())",
346357
options: [{ enforceForIndexOf: true }]
358+
},
359+
{
360+
code: "foo.indexOf((NaN, 1))",
361+
options: [{ enforceForIndexOf: true }]
362+
},
363+
{
364+
code: "foo.lastIndexOf((NaN, 1))",
365+
options: [{ enforceForIndexOf: true }]
366+
},
367+
{
368+
code: "foo.indexOf((Number.NaN, 1))",
369+
options: [{ enforceForIndexOf: true }]
370+
},
371+
{
372+
code: "foo.lastIndexOf((Number.NaN, 1))",
373+
options: [{ enforceForIndexOf: true }]
347374
}
348375
],
349376
invalid: [
@@ -747,6 +774,34 @@ ruleTester.run("use-isnan", rule, {
747774
]
748775
}]
749776
},
777+
{
778+
code: "x === (doStuff(), NaN);",
779+
errors: [{
780+
...comparisonError,
781+
suggestions: []
782+
}]
783+
},
784+
{
785+
code: "x === (doStuff(), Number.NaN);",
786+
errors: [{
787+
...comparisonError,
788+
suggestions: []
789+
}]
790+
},
791+
{
792+
code: "x == (doStuff(), NaN);",
793+
errors: [{
794+
...comparisonError,
795+
suggestions: []
796+
}]
797+
},
798+
{
799+
code: "x == (doStuff(), Number.NaN);",
800+
errors: [{
801+
...comparisonError,
802+
suggestions: []
803+
}]
804+
},
750805

751806
//------------------------------------------------------------------------------
752807
// enforceForSwitchCase
@@ -910,6 +965,20 @@ ruleTester.run("use-isnan", rule, {
910965
{ messageId: "caseNaN", type: "SwitchCase", column: 22 }
911966
]
912967
},
968+
{
969+
code: "switch((doStuff(), NaN)) {}",
970+
options: [{ enforceForSwitchCase: true }],
971+
errors: [
972+
{ messageId: "switchNaN", type: "SwitchStatement", column: 1 }
973+
]
974+
},
975+
{
976+
code: "switch((doStuff(), Number.NaN)) {}",
977+
options: [{ enforceForSwitchCase: true }],
978+
errors: [
979+
{ messageId: "switchNaN", type: "SwitchStatement", column: 1 }
980+
]
981+
},
913982

914983
//------------------------------------------------------------------------------
915984
// enforceForIndexOf
@@ -1010,6 +1079,30 @@ ruleTester.run("use-isnan", rule, {
10101079
options: [{ enforceForIndexOf: true }],
10111080
languageOptions: { ecmaVersion: 2020 },
10121081
errors: [{ messageId: "indexOfNaN", data: { methodName: "indexOf" } }]
1082+
},
1083+
{
1084+
code: "foo.indexOf((1, NaN))",
1085+
options: [{ enforceForIndexOf: true }],
1086+
languageOptions: { ecmaVersion: 2020 },
1087+
errors: [{ messageId: "indexOfNaN", data: { methodName: "indexOf" } }]
1088+
},
1089+
{
1090+
code: "foo.indexOf((1, Number.NaN))",
1091+
options: [{ enforceForIndexOf: true }],
1092+
languageOptions: { ecmaVersion: 2020 },
1093+
errors: [{ messageId: "indexOfNaN", data: { methodName: "indexOf" } }]
1094+
},
1095+
{
1096+
code: "foo.lastIndexOf((1, NaN))",
1097+
options: [{ enforceForIndexOf: true }],
1098+
languageOptions: { ecmaVersion: 2020 },
1099+
errors: [{ messageId: "indexOfNaN", data: { methodName: "lastIndexOf" } }]
1100+
},
1101+
{
1102+
code: "foo.lastIndexOf((1, Number.NaN))",
1103+
options: [{ enforceForIndexOf: true }],
1104+
languageOptions: { ecmaVersion: 2020 },
1105+
errors: [{ messageId: "indexOfNaN", data: { methodName: "lastIndexOf" } }]
10131106
}
10141107
]
10151108
});

0 commit comments

Comments
 (0)