2 * @fileoverview Rule to flag comparisons to the value NaN
3 * @author James Allardice
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 const astUtils = require("./utils/ast-utils");
14 //------------------------------------------------------------------------------
16 //------------------------------------------------------------------------------
19 * Determines if the given node is a NaN `Identifier` node.
20 * @param {ASTNode|null} node The node to check.
21 * @returns {boolean} `true` if the node is 'NaN' identifier.
23 function isNaNIdentifier(node) {
24 return Boolean(node) && (
25 astUtils.isSpecificId(node, "NaN") ||
26 astUtils.isSpecificMemberAccess(node, "Number", "NaN")
30 //------------------------------------------------------------------------------
32 //------------------------------------------------------------------------------
39 description: "require calls to `isNaN()` when checking for `NaN`",
40 category: "Possible Errors",
42 url: "https://eslint.org/docs/rules/use-isnan"
49 enforceForSwitchCase: {
58 additionalProperties: false
63 comparisonWithNaN: "Use the isNaN function to compare with NaN.",
64 switchNaN: "'switch(NaN)' can never match a case clause. Use Number.isNaN instead of the switch.",
65 caseNaN: "'case NaN' can never match. Use Number.isNaN before the switch.",
66 indexOfNaN: "Array prototype method '{{ methodName }}' cannot find NaN."
72 const enforceForSwitchCase = !context.options[0] || context.options[0].enforceForSwitchCase;
73 const enforceForIndexOf = context.options[0] && context.options[0].enforceForIndexOf;
76 * Checks the given `BinaryExpression` node for `foo === NaN` and other comparisons.
77 * @param {ASTNode} node The node to check.
80 function checkBinaryExpression(node) {
82 /^(?:[<>]|[!=]=)=?$/u.test(node.operator) &&
83 (isNaNIdentifier(node.left) || isNaNIdentifier(node.right))
85 context.report({ node, messageId: "comparisonWithNaN" });
90 * Checks the discriminant and all case clauses of the given `SwitchStatement` node for `switch(NaN)` and `case NaN:`
91 * @param {ASTNode} node The node to check.
94 function checkSwitchStatement(node) {
95 if (isNaNIdentifier(node.discriminant)) {
96 context.report({ node, messageId: "switchNaN" });
99 for (const switchCase of node.cases) {
100 if (isNaNIdentifier(switchCase.test)) {
101 context.report({ node: switchCase, messageId: "caseNaN" });
107 * Checks the given `CallExpression` node for `.indexOf(NaN)` and `.lastIndexOf(NaN)`.
108 * @param {ASTNode} node The node to check.
111 function checkCallExpression(node) {
112 const callee = astUtils.skipChainExpression(node.callee);
114 if (callee.type === "MemberExpression") {
115 const methodName = astUtils.getStaticPropertyName(callee);
118 (methodName === "indexOf" || methodName === "lastIndexOf") &&
119 node.arguments.length === 1 &&
120 isNaNIdentifier(node.arguments[0])
122 context.report({ node, messageId: "indexOfNaN", data: { methodName } });
128 BinaryExpression: checkBinaryExpression
131 if (enforceForSwitchCase) {
132 listeners.SwitchStatement = checkSwitchStatement;
135 if (enforceForIndexOf) {
136 listeners.CallExpression = checkCallExpression;