diff --git a/lib/rules/no-constant-condition.js b/lib/rules/no-constant-condition.js index 0bcb31931e42..a2c8d7fa3c7d 100644 --- a/lib/rules/no-constant-condition.js +++ b/lib/rules/no-constant-condition.js @@ -124,7 +124,8 @@ 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 + * `true` 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 */ @@ -138,15 +139,28 @@ module.exports = { case "Literal": case "ArrowFunctionExpression": case "FunctionExpression": - case "ObjectExpression": case "ClassExpression": return true; + case "ObjectExpression": + if (!inBooleanPosition) { + return node.properties.every(prop => { + if (prop.type === "SpreadElement") { + return isConstant(prop, false); + } + if (prop.type === "Property") { + return prop.key.name !== "toString" && prop.key.name !== "valueOf"; + } + + return false; + }); + } + return true; case "TemplateLiteral": return (inBooleanPosition && node.quasis.some(quasi => quasi.value.cooked.length)) || - node.expressions.every(exp => isConstant(exp, inBooleanPosition)); + node.expressions.every(exp => isConstant(exp, false)); case "ArrayExpression": { - if (node.parent.type === "BinaryExpression" && node.parent.operator === "+") { + if (!inBooleanPosition) { return node.elements.every(element => isConstant(element, false)); } return true; @@ -196,6 +210,8 @@ module.exports = { case "SequenceExpression": return isConstant(node.expressions[node.expressions.length - 1], inBooleanPosition); + case "SpreadElement": + return isConstant(node.argument, inBooleanPosition); // no default } diff --git a/tests/lib/rules/no-constant-condition.js b/tests/lib/rules/no-constant-condition.js index 0b5f3c5a1dc9..b29a502ee3e2 100644 --- a/tests/lib/rules/no-constant-condition.js +++ b/tests/lib/rules/no-constant-condition.js @@ -175,7 +175,22 @@ ruleTester.run("no-constant-condition", rule, { "function* foo() {while (true) {function* foo() {yield;}yield;}}", "function* foo() { for (let x = yield; x < 10; x++) {yield;}yield;}", "function* foo() { for (let x = yield; ; x++) { yield; }}", - "if (new Number(x) + 1 === 2) {}" + "if (new Number(x) + 1 === 2) {}", + + // #15467 + "if([a]==[b]) {}", + "if (+[...a]) {}", + "if (+[...[...a]]) {}", + "if (`${[...a]}`) {}", + "if (`${[{toString: () => a}]}`) {}", + "if ('' + {toString: () => a}) {}", + "if ('' + {...{toString: () => a}}) {}", + "if ('' + [{toString: () => a}]) {}", + "if (+[{toString: () => a}]) {}", + "if (+[a]) {}", + "if (+{valueOf: () => a}) {}", + "if (0 - [a]) {}", + "if (1 * [a]) {}" ], invalid: [ { code: "for(;true;);", errors: [{ messageId: "unexpected", type: "Literal" }] }, @@ -262,8 +277,6 @@ ruleTester.run("no-constant-condition", rule, { // #5228 , typeof conditions { code: "if(typeof x){}", errors: [{ messageId: "unexpected", type: "UnaryExpression" }] }, - { code: "if(`${typeof x}`){}", errors: [{ messageId: "unexpected", type: "TemplateLiteral" }] }, - { code: "if(`${''}${typeof x}`){}", errors: [{ messageId: "unexpected", type: "TemplateLiteral" }] }, { code: "if(typeof 'abc' === 'string'){}", errors: [{ messageId: "unexpected", type: "BinaryExpression" }] }, { code: "if(a = typeof b){}", errors: [{ messageId: "unexpected", type: "AssignmentExpression" }] }, { code: "if(a, typeof b){}", errors: [{ messageId: "unexpected", type: "SequenceExpression" }] }, @@ -358,18 +371,6 @@ ruleTester.run("no-constant-condition", rule, { code: "if(''+[]) {}", errors: [{ messageId: "unexpected", type: "BinaryExpression" }] }, - { - code: "if([a]==[a]) {}", - errors: [{ messageId: "unexpected", type: "BinaryExpression" }] - }, - { - code: "if([a] - '') {}", - errors: [{ messageId: "unexpected", type: "BinaryExpression" }] - }, - { - code: "if(+[a]) {}", - errors: [{ messageId: "unexpected", type: "UnaryExpression" }] - }, { code: "if(+1) {}", errors: [{ messageId: "unexpected", type: "UnaryExpression" }] @@ -397,7 +398,10 @@ ruleTester.run("no-constant-condition", rule, { // Boxed primitives are always truthy { code: "if(new Boolean(foo)) {}", errors: [{ messageId: "unexpected" }] }, { code: "if(new String(foo)) {}", errors: [{ messageId: "unexpected" }] }, - { code: "if(new Number(foo)) {}", errors: [{ messageId: "unexpected" }] } + { code: "if(new Number(foo)) {}", errors: [{ messageId: "unexpected" }] }, + // Spreading a literal + { code: "if(`${{...{}}}`) {}", errors: [{ messageId: "unexpected" }] }, + { code: "if(`${[...['a']]}`) {}", errors: [{ messageId: "unexpected" }] } ] });