From 3e4e7f8d429dc70b78c0aefaa37f9c22a1e5fc0f Mon Sep 17 00:00:00 2001 From: Milos Djermanovic Date: Wed, 25 Mar 2020 19:50:36 +0100 Subject: [PATCH] Fix: incorrect logic for required parens in no-extra-boolean-cast fixer (#13061) * Fix: incorrect logic for required parens in no-extra-boolean-cast fixer * Specify assumptions for `needsParens` --- lib/rules/no-extra-boolean-cast.js | 125 ++- tests/lib/rules/no-extra-boolean-cast.js | 1023 ++++++++++++++++++++++ 2 files changed, 1125 insertions(+), 23 deletions(-) diff --git a/lib/rules/no-extra-boolean-cast.js b/lib/rules/no-extra-boolean-cast.js index 8ccd0bce906..aba8e63e086 100644 --- a/lib/rules/no-extra-boolean-cast.js +++ b/lib/rules/no-extra-boolean-cast.js @@ -10,6 +10,9 @@ //------------------------------------------------------------------------------ const astUtils = require("./utils/ast-utils"); +const eslintUtils = require("eslint-utils"); + +const precedence = astUtils.getPrecedence; //------------------------------------------------------------------------------ // Rule Definition @@ -126,6 +129,60 @@ module.exports = { return Boolean(sourceCode.getCommentsInside(node).length); } + /** + * Checks if the given node is wrapped in grouping parentheses. Parentheses for constructs such as if() don't count. + * @param {ASTNode} node The node to check. + * @returns {boolean} `true` if the node is parenthesized. + * @private + */ + function isParenthesized(node) { + return eslintUtils.isParenthesized(1, node, sourceCode); + } + + /** + * Determines whether the given node needs to be parenthesized when replacing the previous node. + * It assumes that `previousNode` is the node to be reported by this rule, so it has a limited list + * of possible parent node types. By the same assumption, the node's role in a particular parent is already known. + * For example, if the parent is `ConditionalExpression`, `previousNode` must be its `test` child. + * @param {ASTNode} previousNode Previous node. + * @param {ASTNode} node The node to check. + * @returns {boolean} `true` if the node needs to be parenthesized. + */ + function needsParens(previousNode, node) { + if (isParenthesized(previousNode)) { + + // parentheses around the previous node will stay, so there is no need for an additional pair + return false; + } + + // parent of the previous node will become parent of the replacement node + const parent = previousNode.parent; + + switch (parent.type) { + case "CallExpression": + case "NewExpression": + return node.type === "SequenceExpression"; + case "IfStatement": + case "DoWhileStatement": + case "WhileStatement": + case "ForStatement": + return false; + case "ConditionalExpression": + return precedence(node) <= precedence(parent); + case "UnaryExpression": + return precedence(node) < precedence(parent); + case "LogicalExpression": + if (previousNode === parent.left) { + return precedence(node) < precedence(parent); + } + return precedence(node) <= precedence(parent); + + /* istanbul ignore next */ + default: + throw new Error(`Unexpected parent type: ${parent.type}`); + } + } + return { UnaryExpression(node) { const parent = node.parent; @@ -143,32 +200,34 @@ module.exports = { context.report({ node: parent, messageId: "unexpectedNegation", - fix: fixer => { + fix(fixer) { if (hasCommentsInside(parent)) { return null; } + if (needsParens(parent, node.argument)) { + return fixer.replaceText(parent, `(${sourceCode.getText(node.argument)})`); + } + let prefix = ""; const tokenBefore = sourceCode.getTokenBefore(parent); const firstReplacementToken = sourceCode.getFirstToken(node.argument); - if (tokenBefore && tokenBefore.range[1] === parent.range[0] && - !astUtils.canTokensBeAdjacent(tokenBefore, firstReplacementToken)) { + if ( + tokenBefore && + tokenBefore.range[1] === parent.range[0] && + !astUtils.canTokensBeAdjacent(tokenBefore, firstReplacementToken) + ) { prefix = " "; } - if (astUtils.getPrecedence(node.argument) < astUtils.getPrecedence(parent.parent)) { - return fixer.replaceText(parent, `(${sourceCode.getText(node.argument)})`); - } - return fixer.replaceText(parent, prefix + sourceCode.getText(node.argument)); } }); } }, - CallExpression(node) { - const parent = node.parent; + CallExpression(node) { if (node.callee.type !== "Identifier" || node.callee.name !== "Boolean") { return; } @@ -177,11 +236,15 @@ module.exports = { context.report({ node, messageId: "unexpectedCall", - fix: fixer => { - if (!node.arguments.length) { + fix(fixer) { + const parent = node.parent; + + if (node.arguments.length === 0) { if (parent.type === "UnaryExpression" && parent.operator === "!") { - // !Boolean() -> true + /* + * !Boolean() -> true + */ if (hasCommentsInside(parent)) { return null; @@ -191,32 +254,48 @@ module.exports = { let prefix = ""; const tokenBefore = sourceCode.getTokenBefore(parent); - if (tokenBefore && tokenBefore.range[1] === parent.range[0] && - !astUtils.canTokensBeAdjacent(tokenBefore, replacement)) { + if ( + tokenBefore && + tokenBefore.range[1] === parent.range[0] && + !astUtils.canTokensBeAdjacent(tokenBefore, replacement) + ) { prefix = " "; } return fixer.replaceText(parent, prefix + replacement); } - // Boolean() -> false + /* + * Boolean() -> false + */ + if (hasCommentsInside(node)) { return null; } + return fixer.replaceText(node, "false"); } - if (node.arguments.length > 1 || node.arguments[0].type === "SpreadElement" || - hasCommentsInside(node)) { - return null; - } + if (node.arguments.length === 1) { + const argument = node.arguments[0]; + + if (argument.type === "SpreadElement" || hasCommentsInside(node)) { + return null; + } + + /* + * Boolean(expression) -> expression + */ - const argument = node.arguments[0]; + if (needsParens(node, argument)) { + return fixer.replaceText(node, `(${sourceCode.getText(argument)})`); + } - if (astUtils.getPrecedence(argument) < astUtils.getPrecedence(node.parent)) { - return fixer.replaceText(node, `(${sourceCode.getText(argument)})`); + return fixer.replaceText(node, sourceCode.getText(argument)); } - return fixer.replaceText(node, sourceCode.getText(argument)); + + // two or more arguments + return null; } }); } diff --git a/tests/lib/rules/no-extra-boolean-cast.js b/tests/lib/rules/no-extra-boolean-cast.js index 08605d15aa1..715fe91f5ba 100644 --- a/tests/lib/rules/no-extra-boolean-cast.js +++ b/tests/lib/rules/no-extra-boolean-cast.js @@ -1372,6 +1372,1029 @@ ruleTester.run("no-extra-boolean-cast", rule, { column: 24, endColumn: 27 }] + }, + + // test parentheses in autofix + { + code: "Boolean(!!(a, b))", + output: "Boolean((a, b))", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "Boolean(Boolean((a, b)))", + output: "Boolean((a, b))", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "Boolean((!!(a, b)))", + output: "Boolean((a, b))", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "Boolean((Boolean((a, b))))", + output: "Boolean((a, b))", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "Boolean(!(!(a, b)))", + output: "Boolean((a, b))", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "Boolean((!(!(a, b))))", + output: "Boolean((a, b))", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "Boolean(!!(a = b))", + output: "Boolean(a = b)", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "Boolean((!!(a = b)))", + output: "Boolean((a = b))", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "Boolean(Boolean(a = b))", + output: "Boolean(a = b)", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "Boolean(Boolean((a += b)))", + output: "Boolean(a += b)", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "Boolean(!!(a === b))", + output: "Boolean(a === b)", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "Boolean(!!((a !== b)))", + output: "Boolean(a !== b)", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "Boolean(!!a.b)", + output: "Boolean(a.b)", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "Boolean(Boolean((a)))", + output: "Boolean(a)", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "Boolean((!!(a)))", + output: "Boolean((a))", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + + { + code: "new Boolean(!!(a, b))", + output: "new Boolean((a, b))", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "new Boolean(Boolean((a, b)))", + output: "new Boolean((a, b))", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "new Boolean((!!(a, b)))", + output: "new Boolean((a, b))", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "new Boolean((Boolean((a, b))))", + output: "new Boolean((a, b))", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "new Boolean(!(!(a, b)))", + output: "new Boolean((a, b))", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "new Boolean((!(!(a, b))))", + output: "new Boolean((a, b))", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "new Boolean(!!(a = b))", + output: "new Boolean(a = b)", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "new Boolean((!!(a = b)))", + output: "new Boolean((a = b))", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "new Boolean(Boolean(a = b))", + output: "new Boolean(a = b)", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "new Boolean(Boolean((a += b)))", + output: "new Boolean(a += b)", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "new Boolean(!!(a === b))", + output: "new Boolean(a === b)", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "new Boolean(!!((a !== b)))", + output: "new Boolean(a !== b)", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "new Boolean(!!a.b)", + output: "new Boolean(a.b)", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "new Boolean(Boolean((a)))", + output: "new Boolean(a)", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "new Boolean((!!(a)))", + output: "new Boolean((a))", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "if (!!(a, b));", + output: "if (a, b);", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "if (Boolean((a, b)));", + output: "if (a, b);", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "if (!(!(a, b)));", + output: "if (a, b);", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "if (!!(a = b));", + output: "if (a = b);", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "if (Boolean(a = b));", + output: "if (a = b);", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "if (!!(a > b));", + output: "if (a > b);", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "if (Boolean(a === b));", + output: "if (a === b);", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "if (!!f(a));", + output: "if (f(a));", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "if (Boolean(f(a)));", + output: "if (f(a));", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "if (!!(f(a)));", + output: "if (f(a));", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "if ((!!f(a)));", + output: "if ((f(a)));", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "if ((Boolean(f(a))));", + output: "if ((f(a)));", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "if (!!a);", + output: "if (a);", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "if (Boolean(a));", + output: "if (a);", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "while (!!(a, b));", + output: "while (a, b);", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "while (Boolean((a, b)));", + output: "while (a, b);", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "while (!(!(a, b)));", + output: "while (a, b);", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "while (!!(a = b));", + output: "while (a = b);", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "while (Boolean(a = b));", + output: "while (a = b);", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "while (!!(a > b));", + output: "while (a > b);", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "while (Boolean(a === b));", + output: "while (a === b);", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "while (!!f(a));", + output: "while (f(a));", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "while (Boolean(f(a)));", + output: "while (f(a));", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "while (!!(f(a)));", + output: "while (f(a));", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "while ((!!f(a)));", + output: "while ((f(a)));", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "while ((Boolean(f(a))));", + output: "while ((f(a)));", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "while (!!a);", + output: "while (a);", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "while (Boolean(a));", + output: "while (a);", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "do {} while (!!(a, b));", + output: "do {} while (a, b);", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "do {} while (Boolean((a, b)));", + output: "do {} while (a, b);", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "do {} while (!(!(a, b)));", + output: "do {} while (a, b);", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "do {} while (!!(a = b));", + output: "do {} while (a = b);", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "do {} while (Boolean(a = b));", + output: "do {} while (a = b);", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "do {} while (!!(a > b));", + output: "do {} while (a > b);", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "do {} while (Boolean(a === b));", + output: "do {} while (a === b);", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "do {} while (!!f(a));", + output: "do {} while (f(a));", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "do {} while (Boolean(f(a)));", + output: "do {} while (f(a));", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "do {} while (!!(f(a)));", + output: "do {} while (f(a));", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "do {} while ((!!f(a)));", + output: "do {} while ((f(a)));", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "do {} while ((Boolean(f(a))));", + output: "do {} while ((f(a)));", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "do {} while (!!a);", + output: "do {} while (a);", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "do {} while (Boolean(a));", + output: "do {} while (a);", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "for (; !!(a, b););", + output: "for (; a, b;);", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "for (; Boolean((a, b)););", + output: "for (; a, b;);", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "for (; !(!(a, b)););", + output: "for (; a, b;);", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "for (; !!(a = b););", + output: "for (; a = b;);", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "for (; Boolean(a = b););", + output: "for (; a = b;);", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "for (; !!(a > b););", + output: "for (; a > b;);", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "for (; Boolean(a === b););", + output: "for (; a === b;);", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "for (; !!f(a););", + output: "for (; f(a););", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "for (; Boolean(f(a)););", + output: "for (; f(a););", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "for (; !!(f(a)););", + output: "for (; f(a););", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "for (; (!!f(a)););", + output: "for (; (f(a)););", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "for (; (Boolean(f(a))););", + output: "for (; (f(a)););", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "for (; !!a;);", + output: "for (; a;);", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "for (; Boolean(a););", + output: "for (; a;);", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "!!(a, b) ? c : d", + output: "(a, b) ? c : d", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "(!!(a, b)) ? c : d", + output: "(a, b) ? c : d", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "Boolean((a, b)) ? c : d", + output: "(a, b) ? c : d", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "!!(a = b) ? c : d", + output: "(a = b) ? c : d", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "Boolean(a -= b) ? c : d", + output: "(a -= b) ? c : d", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "(Boolean((a *= b))) ? c : d", + output: "(a *= b) ? c : d", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "!!(a ? b : c) ? d : e", + output: "(a ? b : c) ? d : e", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "Boolean(a ? b : c) ? d : e", + output: "(a ? b : c) ? d : e", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "!!(a || b) ? c : d", + output: "a || b ? c : d", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "Boolean(a && b) ? c : d", + output: "a && b ? c : d", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "!!(a === b) ? c : d", + output: "a === b ? c : d", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "Boolean(a < b) ? c : d", + output: "a < b ? c : d", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "!!((a !== b)) ? c : d", + output: "a !== b ? c : d", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "Boolean((a >= b)) ? c : d", + output: "a >= b ? c : d", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "!!+a ? b : c", + output: "+a ? b : c", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "!!+(a) ? b : c", + output: "+(a) ? b : c", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "Boolean(!a) ? b : c", + output: "!a ? b : c", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "!!f(a) ? b : c", + output: "f(a) ? b : c", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "(!!f(a)) ? b : c", + output: "(f(a)) ? b : c", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "Boolean(a.b) ? c : d", + output: "a.b ? c : d", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "!!a ? b : c", + output: "a ? b : c", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "Boolean(a) ? b : c", + output: "a ? b : c", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "!!!(a, b)", + output: "!(a, b)", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "!Boolean((a, b))", + output: "!(a, b)", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "!!!(a = b)", + output: "!(a = b)", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "!!(!(a += b))", + output: "!(a += b)", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "!(!!(a += b))", + output: "!(a += b)", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "!Boolean(a -= b)", + output: "!(a -= b)", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "!Boolean((a -= b))", + output: "!(a -= b)", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "!(Boolean(a -= b))", + output: "!(a -= b)", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "!!!(a || b)", + output: "!(a || b)", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "!Boolean(a || b)", + output: "!(a || b)", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "!!!(a && b)", + output: "!(a && b)", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "!Boolean(a && b)", + output: "!(a && b)", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "!!!(a != b)", + output: "!(a != b)", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "!!!(a === b)", + output: "!(a === b)", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "var x = !Boolean(a > b)", + output: "var x = !(a > b)", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "!!!(a - b)", + output: "!(a - b)", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "!!!(a ** b)", + output: "!(a ** b)", + parserOptions: { ecmaVersion: 2016 }, + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "!Boolean(a ** b)", + output: "!(a ** b)", + parserOptions: { ecmaVersion: 2016 }, + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "async function f() { !!!(await a) }", + output: "async function f() { !await a }", + parserOptions: { ecmaVersion: 2017 }, + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "async function f() { !Boolean(await a) }", + output: "async function f() { !await a }", + parserOptions: { ecmaVersion: 2017 }, + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "!!!!a", + output: "!!a", // Reports 2 errors. After the first fix, the second error will disappear. + errors: [ + { messageId: "unexpectedNegation", type: "UnaryExpression" }, + { messageId: "unexpectedNegation", type: "UnaryExpression" } + ] + }, + { + code: "!!(!(!a))", + output: "!!a", // Reports 2 errors. After the first fix, the second error will disappear. + errors: [ + { messageId: "unexpectedNegation", type: "UnaryExpression" }, + { messageId: "unexpectedNegation", type: "UnaryExpression" } + ] + }, + { + code: "!Boolean(!a)", + output: "!!a", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "!Boolean((!a))", + output: "!!a", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "!Boolean(!(a))", + output: "!!(a)", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "!(Boolean(!a))", + output: "!(!a)", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "!!!+a", + output: "!+a", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "!!!(+a)", + output: "!+a", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "!!(!+a)", + output: "!+a", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "!(!!+a)", + output: "!(+a)", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "!Boolean((-a))", + output: "!-a", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "!Boolean(-(a))", + output: "!-(a)", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "!!!(--a)", + output: "!--a", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "!Boolean(a++)", + output: "!a++", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "!!!f(a)", + output: "!f(a)", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "!!!(f(a))", + output: "!f(a)", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "!!!a", + output: "!a", + errors: [{ messageId: "unexpectedNegation", type: "UnaryExpression" }] + }, + { + code: "!Boolean(a)", + output: "!a", + errors: [{ messageId: "unexpectedCall", type: "CallExpression" }] + }, + { + code: "if (!!(a, b) || !!(c, d)) {}", + output: "if ((a, b) || (c, d)) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedNegation", type: "UnaryExpression" }, + { messageId: "unexpectedNegation", type: "UnaryExpression" } + ] + }, + { + code: "if (Boolean((a, b)) || Boolean((c, d))) {}", + output: "if ((a, b) || (c, d)) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedCall", type: "CallExpression" }, + { messageId: "unexpectedCall", type: "CallExpression" } + ] + }, + { + code: "if ((!!((a, b))) || (!!((c, d)))) {}", + output: "if ((a, b) || (c, d)) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedNegation", type: "UnaryExpression" }, + { messageId: "unexpectedNegation", type: "UnaryExpression" } + ] + }, + { + code: "if (!!(a, b) && !!(c, d)) {}", + output: "if ((a, b) && (c, d)) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedNegation", type: "UnaryExpression" }, + { messageId: "unexpectedNegation", type: "UnaryExpression" } + ] + }, + { + code: "if (Boolean((a, b)) && Boolean((c, d))) {}", + output: "if ((a, b) && (c, d)) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedCall", type: "CallExpression" }, + { messageId: "unexpectedCall", type: "CallExpression" } + ] + }, + { + code: "if ((!!((a, b))) && (!!((c, d)))) {}", + output: "if ((a, b) && (c, d)) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedNegation", type: "UnaryExpression" }, + { messageId: "unexpectedNegation", type: "UnaryExpression" } + ] + }, + { + code: "if (!!(a = b) || !!(c = d)) {}", + output: "if ((a = b) || (c = d)) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedNegation", type: "UnaryExpression" }, + { messageId: "unexpectedNegation", type: "UnaryExpression" } + ] + }, + { + code: "if (Boolean(a /= b) || Boolean(c /= d)) {}", + output: "if ((a /= b) || (c /= d)) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedCall", type: "CallExpression" }, + { messageId: "unexpectedCall", type: "CallExpression" } + ] + }, + { + code: "if (!!(a >>= b) && !!(c >>= d)) {}", + output: "if ((a >>= b) && (c >>= d)) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedNegation", type: "UnaryExpression" }, + { messageId: "unexpectedNegation", type: "UnaryExpression" } + ] + }, + { + code: "if (Boolean(a **= b) && Boolean(c **= d)) {}", + output: "if ((a **= b) && (c **= d)) {}", + options: [{ enforceForLogicalOperands: true }], + parserOptions: { ecmaVersion: 2016 }, + errors: [ + { messageId: "unexpectedCall", type: "CallExpression" }, + { messageId: "unexpectedCall", type: "CallExpression" } + ] + }, + { + code: "if (!!(a ? b : c) || !!(d ? e : f)) {}", + output: "if ((a ? b : c) || (d ? e : f)) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedNegation", type: "UnaryExpression" }, + { messageId: "unexpectedNegation", type: "UnaryExpression" } + ] + }, + { + code: "if (Boolean(a ? b : c) || Boolean(d ? e : f)) {}", + output: "if ((a ? b : c) || (d ? e : f)) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedCall", type: "CallExpression" }, + { messageId: "unexpectedCall", type: "CallExpression" } + ] + }, + { + code: "if (!!(a ? b : c) && !!(d ? e : f)) {}", + output: "if ((a ? b : c) && (d ? e : f)) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedNegation", type: "UnaryExpression" }, + { messageId: "unexpectedNegation", type: "UnaryExpression" } + ] + }, + { + code: "if (Boolean(a ? b : c) && Boolean(d ? e : f)) {}", + output: "if ((a ? b : c) && (d ? e : f)) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedCall", type: "CallExpression" }, + { messageId: "unexpectedCall", type: "CallExpression" } + ] + }, + { + code: "if (!!(a || b) || !!(c || d)) {}", + output: "if (a || b || (c || d)) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedNegation", type: "UnaryExpression" }, + { messageId: "unexpectedNegation", type: "UnaryExpression" } + ] + }, + { + code: "if (Boolean(a || b) || Boolean(c || d)) {}", + output: "if (a || b || (c || d)) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedCall", type: "CallExpression" }, + { messageId: "unexpectedCall", type: "CallExpression" } + ] + }, + { + code: "if (!!(a || b) && !!(c || d)) {}", + output: "if ((a || b) && (c || d)) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedNegation", type: "UnaryExpression" }, + { messageId: "unexpectedNegation", type: "UnaryExpression" } + ] + }, + { + code: "if (Boolean(a || b) && Boolean(c || d)) {}", + output: "if ((a || b) && (c || d)) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedCall", type: "CallExpression" }, + { messageId: "unexpectedCall", type: "CallExpression" } + ] + }, + { + code: "if (!!(a && b) || !!(c && d)) {}", + output: "if (a && b || c && d) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedNegation", type: "UnaryExpression" }, + { messageId: "unexpectedNegation", type: "UnaryExpression" } + ] + }, + { + code: "if (Boolean(a && b) || Boolean(c && d)) {}", + output: "if (a && b || c && d) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedCall", type: "CallExpression" }, + { messageId: "unexpectedCall", type: "CallExpression" } + ] + }, + { + code: "if (!!(a && b) && !!(c && d)) {}", + output: "if (a && b && (c && d)) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedNegation", type: "UnaryExpression" }, + { messageId: "unexpectedNegation", type: "UnaryExpression" } + ] + }, + { + code: "if (Boolean(a && b) && Boolean(c && d)) {}", + output: "if (a && b && (c && d)) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedCall", type: "CallExpression" }, + { messageId: "unexpectedCall", type: "CallExpression" } + ] + }, + { + code: "if (!!(a !== b) || !!(c !== d)) {}", + output: "if (a !== b || c !== d) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedNegation", type: "UnaryExpression" }, + { messageId: "unexpectedNegation", type: "UnaryExpression" } + ] + }, + { + code: "if (Boolean(a != b) || Boolean(c != d)) {}", + output: "if (a != b || c != d) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedCall", type: "CallExpression" }, + { messageId: "unexpectedCall", type: "CallExpression" } + ] + }, + { + code: "if (!!(a === b) && !!(c === d)) {}", + output: "if (a === b && c === d) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedNegation", type: "UnaryExpression" }, + { messageId: "unexpectedNegation", type: "UnaryExpression" } + ] + }, + { + code: "if (!!(a > b) || !!(c < d)) {}", + output: "if (a > b || c < d) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedNegation", type: "UnaryExpression" }, + { messageId: "unexpectedNegation", type: "UnaryExpression" } + ] + }, + { + code: "if (Boolean(!a) || Boolean(+b)) {}", + output: "if (!a || +b) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedCall", type: "CallExpression" }, + { messageId: "unexpectedCall", type: "CallExpression" } + ] + }, + { + code: "if (!!f(a) && !!b.c) {}", + output: "if (f(a) && b.c) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedNegation", type: "UnaryExpression" }, + { messageId: "unexpectedNegation", type: "UnaryExpression" } + ] + }, + { + code: "if (Boolean(a) || !!b) {}", + output: "if (a || b) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedCall", type: "CallExpression" }, + { messageId: "unexpectedNegation", type: "UnaryExpression" } + ] + }, + { + code: "if (!!a && Boolean(b)) {}", + output: "if (a && b) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedNegation", type: "UnaryExpression" }, + { messageId: "unexpectedCall", type: "CallExpression" } + ] + }, + { + code: "if ((!!a) || (Boolean(b))) {}", + output: "if ((a) || (b)) {}", + options: [{ enforceForLogicalOperands: true }], + errors: [ + { messageId: "unexpectedNegation", type: "UnaryExpression" }, + { messageId: "unexpectedCall", type: "CallExpression" } + ] } ] });