diff --git a/docs/src/rules/use-isnan.md b/docs/src/rules/use-isnan.md index d6eda9d5d91..f3de5919d18 100644 --- a/docs/src/rules/use-isnan.md +++ b/docs/src/rules/use-isnan.md @@ -224,6 +224,8 @@ var hasNaN = myArray.indexOf(NaN) >= 0; var firstIndex = myArray.indexOf(NaN); var lastIndex = myArray.lastIndexOf(NaN); + +var indexWithSequenceExpression = myArray.indexOf((doStuff(), NaN)); ``` ::: diff --git a/lib/rules/use-isnan.js b/lib/rules/use-isnan.js index b00a701c6bd..357c51d13d6 100644 --- a/lib/rules/use-isnan.js +++ b/lib/rules/use-isnan.js @@ -21,9 +21,17 @@ const astUtils = require("./utils/ast-utils"); * @returns {boolean} `true` if the node is 'NaN' identifier. */ function isNaNIdentifier(node) { - return Boolean(node) && ( - astUtils.isSpecificId(node, "NaN") || - astUtils.isSpecificMemberAccess(node, "Number", "NaN") + if (!node) { + return false; + } + + const nodeToCheck = node.type === "SequenceExpression" + ? node.expressions.at(-1) + : node; + + return ( + astUtils.isSpecificId(nodeToCheck, "NaN") || + astUtils.isSpecificMemberAccess(nodeToCheck, "Number", "NaN") ); } @@ -115,7 +123,10 @@ module.exports = { (isNaNIdentifier(node.left) || isNaNIdentifier(node.right)) ) { const suggestedFixes = []; - const isFixable = fixableOperators.has(node.operator); + const NaNNode = isNaNIdentifier(node.left) ? node.left : node.right; + + const isSequenceExpression = NaNNode.type === "SequenceExpression"; + const isFixable = fixableOperators.has(node.operator) && !isSequenceExpression; const isCastable = castableOperators.has(node.operator); if (isFixable) { @@ -123,13 +134,13 @@ module.exports = { messageId: "replaceWithIsNaN", fix: getBinaryExpressionFixer(node, value => `Number.isNaN(${value})`) }); - } - if (isCastable) { - suggestedFixes.push({ - messageId: "replaceWithCastingAndIsNaN", - fix: getBinaryExpressionFixer(node, value => `Number.isNaN(Number(${value}))`) - }); + if (isCastable) { + suggestedFixes.push({ + messageId: "replaceWithCastingAndIsNaN", + fix: getBinaryExpressionFixer(node, value => `Number.isNaN(Number(${value}))`) + }); + } } context.report({ diff --git a/tests/lib/rules/use-isnan.js b/tests/lib/rules/use-isnan.js index 26c887a1975..337f1f4d146 100644 --- a/tests/lib/rules/use-isnan.js +++ b/tests/lib/rules/use-isnan.js @@ -49,6 +49,9 @@ ruleTester.run("use-isnan", rule, { "foo(2 / Number.NaN)", "var x; if (x = Number.NaN) { }", "x === Number[NaN];", + "x === (NaN, 1)", + "x === (doStuff(), NaN, 1)", + "x === (doStuff(), Number.NaN, 1)", //------------------------------------------------------------------------------ // enforceForSwitchCase @@ -174,6 +177,14 @@ ruleTester.run("use-isnan", rule, { code: "switch(foo) { case foo.Number.NaN: break }", options: [{ enforceForSwitchCase: true }] }, + { + code: "switch((NaN, doStuff(), 1)) {}", + options: [{ enforceForSwitchCase: true }] + }, + { + code: "switch((Number.NaN, doStuff(), 1)) {}", + options: [{ enforceForSwitchCase: true }] + }, //------------------------------------------------------------------------------ // enforceForIndexOf @@ -344,6 +355,22 @@ ruleTester.run("use-isnan", rule, { { code: "foo.lastIndexOf(Number.NaN())", options: [{ enforceForIndexOf: true }] + }, + { + code: "foo.indexOf((NaN, 1))", + options: [{ enforceForIndexOf: true }] + }, + { + code: "foo.lastIndexOf((NaN, 1))", + options: [{ enforceForIndexOf: true }] + }, + { + code: "foo.indexOf((Number.NaN, 1))", + options: [{ enforceForIndexOf: true }] + }, + { + code: "foo.lastIndexOf((Number.NaN, 1))", + options: [{ enforceForIndexOf: true }] } ], invalid: [ @@ -747,6 +774,34 @@ ruleTester.run("use-isnan", rule, { ] }] }, + { + code: "x === (doStuff(), NaN);", + errors: [{ + ...comparisonError, + suggestions: [] + }] + }, + { + code: "x === (doStuff(), Number.NaN);", + errors: [{ + ...comparisonError, + suggestions: [] + }] + }, + { + code: "x == (doStuff(), NaN);", + errors: [{ + ...comparisonError, + suggestions: [] + }] + }, + { + code: "x == (doStuff(), Number.NaN);", + errors: [{ + ...comparisonError, + suggestions: [] + }] + }, //------------------------------------------------------------------------------ // enforceForSwitchCase @@ -910,6 +965,20 @@ ruleTester.run("use-isnan", rule, { { messageId: "caseNaN", type: "SwitchCase", column: 22 } ] }, + { + code: "switch((doStuff(), NaN)) {}", + options: [{ enforceForSwitchCase: true }], + errors: [ + { messageId: "switchNaN", type: "SwitchStatement", column: 1 } + ] + }, + { + code: "switch((doStuff(), Number.NaN)) {}", + options: [{ enforceForSwitchCase: true }], + errors: [ + { messageId: "switchNaN", type: "SwitchStatement", column: 1 } + ] + }, //------------------------------------------------------------------------------ // enforceForIndexOf @@ -1010,6 +1079,30 @@ ruleTester.run("use-isnan", rule, { options: [{ enforceForIndexOf: true }], languageOptions: { ecmaVersion: 2020 }, errors: [{ messageId: "indexOfNaN", data: { methodName: "indexOf" } }] + }, + { + code: "foo.indexOf((1, NaN))", + options: [{ enforceForIndexOf: true }], + languageOptions: { ecmaVersion: 2020 }, + errors: [{ messageId: "indexOfNaN", data: { methodName: "indexOf" } }] + }, + { + code: "foo.indexOf((1, Number.NaN))", + options: [{ enforceForIndexOf: true }], + languageOptions: { ecmaVersion: 2020 }, + errors: [{ messageId: "indexOfNaN", data: { methodName: "indexOf" } }] + }, + { + code: "foo.lastIndexOf((1, NaN))", + options: [{ enforceForIndexOf: true }], + languageOptions: { ecmaVersion: 2020 }, + errors: [{ messageId: "indexOfNaN", data: { methodName: "lastIndexOf" } }] + }, + { + code: "foo.lastIndexOf((1, Number.NaN))", + options: [{ enforceForIndexOf: true }], + languageOptions: { ecmaVersion: 2020 }, + errors: [{ messageId: "indexOfNaN", data: { methodName: "lastIndexOf" } }] } ] });