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) && node.type === "Identifier" && node.name === "NaN";
27 //------------------------------------------------------------------------------
29 //------------------------------------------------------------------------------
36 description: "require calls to `isNaN()` when checking for `NaN`",
37 category: "Possible Errors",
39 url: "https://eslint.org/docs/rules/use-isnan"
46 enforceForSwitchCase: {
55 additionalProperties: false
60 comparisonWithNaN: "Use the isNaN function to compare with NaN.",
61 switchNaN: "'switch(NaN)' can never match a case clause. Use Number.isNaN instead of the switch.",
62 caseNaN: "'case NaN' can never match. Use Number.isNaN before the switch.",
63 indexOfNaN: "Array prototype method '{{ methodName }}' cannot find NaN."
69 const enforceForSwitchCase = !context.options[0] || context.options[0].enforceForSwitchCase;
70 const enforceForIndexOf = context.options[0] && context.options[0].enforceForIndexOf;
73 * Checks the given `BinaryExpression` node for `foo === NaN` and other comparisons.
74 * @param {ASTNode} node The node to check.
77 function checkBinaryExpression(node) {
79 /^(?:[<>]|[!=]=)=?$/u.test(node.operator) &&
80 (isNaNIdentifier(node.left) || isNaNIdentifier(node.right))
82 context.report({ node, messageId: "comparisonWithNaN" });
87 * Checks the discriminant and all case clauses of the given `SwitchStatement` node for `switch(NaN)` and `case NaN:`
88 * @param {ASTNode} node The node to check.
91 function checkSwitchStatement(node) {
92 if (isNaNIdentifier(node.discriminant)) {
93 context.report({ node, messageId: "switchNaN" });
96 for (const switchCase of node.cases) {
97 if (isNaNIdentifier(switchCase.test)) {
98 context.report({ node: switchCase, messageId: "caseNaN" });
104 * Checks the given `CallExpression` node for `.indexOf(NaN)` and `.lastIndexOf(NaN)`.
105 * @param {ASTNode} node The node to check.
108 function checkCallExpression(node) {
109 const callee = astUtils.skipChainExpression(node.callee);
111 if (callee.type === "MemberExpression") {
112 const methodName = astUtils.getStaticPropertyName(callee);
115 (methodName === "indexOf" || methodName === "lastIndexOf") &&
116 node.arguments.length === 1 &&
117 isNaNIdentifier(node.arguments[0])
119 context.report({ node, messageId: "indexOfNaN", data: { methodName } });
125 BinaryExpression: checkBinaryExpression
128 if (enforceForSwitchCase) {
129 listeners.SwitchStatement = checkSwitchStatement;
132 if (enforceForIndexOf) {
133 listeners.CallExpression = checkCallExpression;