From 3023ae8ebeabdb57e6c015240571348f1fd8e661 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Tue, 15 Feb 2022 22:25:22 -0800 Subject: [PATCH] fix: Catch more cases in no-constant-condition Identify `undefined` and `Boolean(somethingConstant)` as both being constant. --- lib/rules/no-constant-condition.js | 14 +++++++++++--- tests/lib/rules/no-constant-condition.js | 20 ++++++++++++++++++-- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/lib/rules/no-constant-condition.js b/lib/rules/no-constant-condition.js index 8adc9bca4db6..e9a920025616 100644 --- a/lib/rules/no-constant-condition.js +++ b/lib/rules/no-constant-condition.js @@ -123,9 +123,10 @@ module.exports = { /** * Checks if a node has a constant truthiness value. * @param {ASTNode} node The AST node to check. - * @param {boolean} inBooleanPosition `false` if checking branch of a condition. - * `true` in all other cases. When `false`, checks if -- for both string and - * number -- if coerced to that type, the value will be constant. + * @param {boolean} inBooleanPosition `true` if checking the test of a + * condition. `false` in all other cases. When `false`, checks if -- for + * both string and number -- if coerced to that type, the value will + * be constant. * @returns {Bool} true when node's truthiness is constant * @private */ @@ -215,6 +216,13 @@ module.exports = { return isConstant(node.expressions[node.expressions.length - 1], inBooleanPosition); case "SpreadElement": return isConstant(node.argument, inBooleanPosition); + case "CallExpression": + if (node.callee.type === "Identifier" && node.callee.name === "Boolean") { + return node.arguments.length === 0 || isConstant(node.arguments[0], true); + } + return false; + case "Identifier": + return node.name === "undefined"; // no default } diff --git a/tests/lib/rules/no-constant-condition.js b/tests/lib/rules/no-constant-condition.js index 9cca27b904d2..d1ea47e21f96 100644 --- a/tests/lib/rules/no-constant-condition.js +++ b/tests/lib/rules/no-constant-condition.js @@ -185,7 +185,12 @@ ruleTester.run("no-constant-condition", rule, { "if (`${[a]}`) {}", "if (+[a]) {}", "if (0 - [a]) {}", - "if (1 * [a]) {}" + "if (1 * [a]) {}", + + // Boolean function + "if (Boolean(a)) {}", + "if (Boolean(...args)) {}", + "if (foo.Boolean(1)) {}" ], invalid: [ { code: "for(;true;);", errors: [{ messageId: "unexpected", type: "Literal" }] }, @@ -396,6 +401,17 @@ ruleTester.run("no-constant-condition", rule, { { code: "if(new Number(foo)) {}", errors: [{ messageId: "unexpected" }] }, // Spreading a constant array - { code: "if(`${[...['a']]}`) {}", errors: [{ messageId: "unexpected" }] } + { code: "if(`${[...['a']]}`) {}", errors: [{ messageId: "unexpected" }] }, + + /* + * undefined is always falsy (except in old browsers that let you + * re-assign, but that's an abscure enough edge case to not worry about) + */ + { code: "if (undefined) {}", errors: [{ messageId: "unexpected" }] }, + + // Coercion to boolean via Boolean function + { code: "if (Boolean(1)) {}", errors: [{ messageId: "unexpected" }] }, + { code: "if (Boolean()) {}", errors: [{ messageId: "unexpected" }] }, + { code: "if (Boolean([a])) {}", errors: [{ messageId: "unexpected" }] } ] });