.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / no-extra-boolean-cast.js
diff --git a/.config/coc/extensions/node_modules/coc-prettier/node_modules/eslint/lib/rules/no-extra-boolean-cast.js b/.config/coc/extensions/node_modules/coc-prettier/node_modules/eslint/lib/rules/no-extra-boolean-cast.js
new file mode 100644 (file)
index 0000000..6ae3ea6
--- /dev/null
@@ -0,0 +1,316 @@
+/**
+ * @fileoverview Rule to flag unnecessary double negation in Boolean contexts
+ * @author Brandon Mills
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const astUtils = require("./utils/ast-utils");
+const eslintUtils = require("eslint-utils");
+
+const precedence = astUtils.getPrecedence;
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+    meta: {
+        type: "suggestion",
+
+        docs: {
+            description: "disallow unnecessary boolean casts",
+            category: "Possible Errors",
+            recommended: true,
+            url: "https://eslint.org/docs/rules/no-extra-boolean-cast"
+        },
+
+        schema: [{
+            type: "object",
+            properties: {
+                enforceForLogicalOperands: {
+                    type: "boolean",
+                    default: false
+                }
+            },
+            additionalProperties: false
+        }],
+        fixable: "code",
+
+        messages: {
+            unexpectedCall: "Redundant Boolean call.",
+            unexpectedNegation: "Redundant double negation."
+        }
+    },
+
+    create(context) {
+        const sourceCode = context.getSourceCode();
+
+        // Node types which have a test which will coerce values to booleans.
+        const BOOLEAN_NODE_TYPES = [
+            "IfStatement",
+            "DoWhileStatement",
+            "WhileStatement",
+            "ConditionalExpression",
+            "ForStatement"
+        ];
+
+        /**
+         * Check if a node is a Boolean function or constructor.
+         * @param {ASTNode} node the node
+         * @returns {boolean} If the node is Boolean function or constructor
+         */
+        function isBooleanFunctionOrConstructorCall(node) {
+
+            // Boolean(<bool>) and new Boolean(<bool>)
+            return (node.type === "CallExpression" || node.type === "NewExpression") &&
+                    node.callee.type === "Identifier" &&
+                        node.callee.name === "Boolean";
+        }
+
+        /**
+         * Checks whether the node is a logical expression and that the option is enabled
+         * @param {ASTNode} node the node
+         * @returns {boolean} if the node is a logical expression and option is enabled
+         */
+        function isLogicalContext(node) {
+            return node.type === "LogicalExpression" &&
+            (node.operator === "||" || node.operator === "&&") &&
+            (context.options.length && context.options[0].enforceForLogicalOperands === true);
+
+        }
+
+
+        /**
+         * Check if a node is in a context where its value would be coerced to a boolean at runtime.
+         * @param {ASTNode} node The node
+         * @returns {boolean} If it is in a boolean context
+         */
+        function isInBooleanContext(node) {
+            return (
+                (isBooleanFunctionOrConstructorCall(node.parent) &&
+                node === node.parent.arguments[0]) ||
+
+                (BOOLEAN_NODE_TYPES.indexOf(node.parent.type) !== -1 &&
+                    node === node.parent.test) ||
+
+                // !<bool>
+                (node.parent.type === "UnaryExpression" &&
+                    node.parent.operator === "!")
+            );
+        }
+
+        /**
+         * Checks whether the node is a context that should report an error
+         * Acts recursively if it is in a logical context
+         * @param {ASTNode} node the node
+         * @returns {boolean} If the node is in one of the flagged contexts
+         */
+        function isInFlaggedContext(node) {
+            if (node.parent.type === "ChainExpression") {
+                return isInFlaggedContext(node.parent);
+            }
+
+            return isInBooleanContext(node) ||
+            (isLogicalContext(node.parent) &&
+
+            // For nested logical statements
+            isInFlaggedContext(node.parent)
+            );
+        }
+
+
+        /**
+         * Check if a node has comments inside.
+         * @param {ASTNode} node The node to check.
+         * @returns {boolean} `true` if it has comments inside.
+         */
+        function hasCommentsInside(node) {
+            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 (previousNode.parent.type === "ChainExpression") {
+                return needsParens(previousNode.parent, 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 (astUtils.isMixedLogicalAndCoalesceExpressions(node, parent)) {
+                        return true;
+                    }
+                    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;
+
+
+                // Exit early if it's guaranteed not to match
+                if (node.operator !== "!" ||
+                          parent.type !== "UnaryExpression" ||
+                          parent.operator !== "!") {
+                    return;
+                }
+
+
+                if (isInFlaggedContext(parent)) {
+                    context.report({
+                        node: parent,
+                        messageId: "unexpectedNegation",
+                        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)
+                            ) {
+                                prefix = " ";
+                            }
+
+                            return fixer.replaceText(parent, prefix + sourceCode.getText(node.argument));
+                        }
+                    });
+                }
+            },
+
+            CallExpression(node) {
+                if (node.callee.type !== "Identifier" || node.callee.name !== "Boolean") {
+                    return;
+                }
+
+                if (isInFlaggedContext(node)) {
+                    context.report({
+                        node,
+                        messageId: "unexpectedCall",
+                        fix(fixer) {
+                            const parent = node.parent;
+
+                            if (node.arguments.length === 0) {
+                                if (parent.type === "UnaryExpression" && parent.operator === "!") {
+
+                                    /*
+                                     * !Boolean() -> true
+                                     */
+
+                                    if (hasCommentsInside(parent)) {
+                                        return null;
+                                    }
+
+                                    const replacement = "true";
+                                    let prefix = "";
+                                    const tokenBefore = sourceCode.getTokenBefore(parent);
+
+                                    if (
+                                        tokenBefore &&
+                                        tokenBefore.range[1] === parent.range[0] &&
+                                        !astUtils.canTokensBeAdjacent(tokenBefore, replacement)
+                                    ) {
+                                        prefix = " ";
+                                    }
+
+                                    return fixer.replaceText(parent, prefix + replacement);
+                                }
+
+                                /*
+                                 * Boolean() -> false
+                                 */
+
+                                if (hasCommentsInside(node)) {
+                                    return null;
+                                }
+
+                                return fixer.replaceText(node, "false");
+                            }
+
+                            if (node.arguments.length === 1) {
+                                const argument = node.arguments[0];
+
+                                if (argument.type === "SpreadElement" || hasCommentsInside(node)) {
+                                    return null;
+                                }
+
+                                /*
+                                 * Boolean(expression) -> expression
+                                 */
+
+                                if (needsParens(node, argument)) {
+                                    return fixer.replaceText(node, `(${sourceCode.getText(argument)})`);
+                                }
+
+                                return fixer.replaceText(node, sourceCode.getText(argument));
+                            }
+
+                            // two or more arguments
+                            return null;
+                        }
+                    });
+                }
+            }
+        };
+
+    }
+};