diff --git a/lib/rules/constructor-super.js b/lib/rules/constructor-super.js index 8787fc569a4..dfec18fb65a 100644 --- a/lib/rules/constructor-super.js +++ b/lib/rules/constructor-super.js @@ -79,6 +79,17 @@ function isPossibleConstructor(node) { return false; case "LogicalExpression": + + /* + * If the && operator short-circuits, the left side was falsy and therefore not a constructor, and if + * it doesn't short-circuit, it takes the value from the right side, so the right side must always be a + * possible constructor. A future improvement could verify that the left side could be truthy by + * excluding falsy literals. + */ + if (node.operator === "&&") { + return isPossibleConstructor(node.right); + } + return ( isPossibleConstructor(node.left) || isPossibleConstructor(node.right) diff --git a/lib/rules/utils/ast-utils.js b/lib/rules/utils/ast-utils.js index fb8beb25211..cff5a1ec693 100644 --- a/lib/rules/utils/ast-utils.js +++ b/lib/rules/utils/ast-utils.js @@ -1611,6 +1611,17 @@ module.exports = { } case "LogicalExpression": + + /* + * If the && operator short-circuits, the left side was falsy and therefore not an error, and if it + * doesn't short-circuit, it takes the value from the right side, so the right side must always be + * a plausible error. A future improvement could verify that the left side could be truthy by + * excluding falsy literals. + */ + if (node.operator === "&&") { + return module.exports.couldBeError(node.right); + } + return module.exports.couldBeError(node.left) || module.exports.couldBeError(node.right); case "ConditionalExpression": diff --git a/tests/lib/rules/constructor-super.js b/tests/lib/rules/constructor-super.js index bfde6a85508..85e4471acc8 100644 --- a/tests/lib/rules/constructor-super.js +++ b/tests/lib/rules/constructor-super.js @@ -43,8 +43,10 @@ ruleTester.run("constructor-super", rule, { "class A extends (B ||= 5) { constructor() { super(); } }", "class A extends (B ??= 5) { constructor() { super(); } }", "class A extends (B || C) { constructor() { super(); } }", - "class A extends (B && 5) { constructor() { super(); } }", "class A extends (5 && B) { constructor() { super(); } }", + + // A future improvement could detect the left side as statically falsy, making this invalid. + "class A extends (false && B) { constructor() { super(); } }", "class A extends (B || 5) { constructor() { super(); } }", "class A extends (B ?? 5) { constructor() { super(); } }", @@ -126,6 +128,10 @@ ruleTester.run("constructor-super", rule, { code: "class A extends (B = 5) { constructor() { super(); } }", errors: [{ messageId: "badSuper", type: "CallExpression" }] }, + { + code: "class A extends (B && 5) { constructor() { super(); } }", + errors: [{ messageId: "badSuper", type: "CallExpression" }] + }, { // `B &&= 5` evaluates either to a falsy value of `B` (which, then, cannot be a constructor), or to '5' diff --git a/tests/lib/rules/no-throw-literal.js b/tests/lib/rules/no-throw-literal.js index 745044abe47..3855b58fdfb 100644 --- a/tests/lib/rules/no-throw-literal.js +++ b/tests/lib/rules/no-throw-literal.js @@ -152,6 +152,13 @@ ruleTester.run("no-throw-literal", rule, { type: "ThrowStatement" }] }, + { + code: "throw foo && 'literal'", // evaluates either to a falsy value of `foo` (which, then, cannot be an Error object), or to 'literal' + errors: [{ + messageId: "object", + type: "ThrowStatement" + }] + }, // ConditionalExpression { diff --git a/tests/lib/rules/prefer-promise-reject-errors.js b/tests/lib/rules/prefer-promise-reject-errors.js index 23e299bc983..b31ec33622b 100644 --- a/tests/lib/rules/prefer-promise-reject-errors.js +++ b/tests/lib/rules/prefer-promise-reject-errors.js @@ -117,6 +117,7 @@ ruleTester.run("prefer-promise-reject-errors", rule, { "Promise.reject(foo &= new Error())", // evaluates either to a falsy value of `foo` (which, then, cannot be an Error object), or to `5` + "Promise.reject(foo && 5)", "Promise.reject(foo &&= 5)" ].map(invalidCase => { diff --git a/tests/lib/rules/utils/ast-utils.js b/tests/lib/rules/utils/ast-utils.js index 8d60a11d70c..fb38dc9ccfb 100644 --- a/tests/lib/rules/utils/ast-utils.js +++ b/tests/lib/rules/utils/ast-utils.js @@ -1099,7 +1099,10 @@ describe("ast-utils", () => { "(1, 2, foo)": true, "1 && 2": false, "1 && foo": true, - "foo && 2": true, + "foo && 2": false, + + // A future improvement could detect the left side as statically falsy, making this false. + "false && foo": true, "foo &&= 2": false, "foo.bar ??= 2": true, "foo[bar] ||= 2": true,