minor adjustment to readme
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / no-extra-boolean-cast.js
1 /**
2  * @fileoverview Rule to flag unnecessary double negation in Boolean contexts
3  * @author Brandon Mills
4  */
5
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Requirements
10 //------------------------------------------------------------------------------
11
12 const astUtils = require("./utils/ast-utils");
13
14 //------------------------------------------------------------------------------
15 // Rule Definition
16 //------------------------------------------------------------------------------
17
18 module.exports = {
19     meta: {
20         type: "suggestion",
21
22         docs: {
23             description: "disallow unnecessary boolean casts",
24             category: "Possible Errors",
25             recommended: true,
26             url: "https://eslint.org/docs/rules/no-extra-boolean-cast"
27         },
28
29         schema: [],
30         fixable: "code",
31
32         messages: {
33             unexpectedCall: "Redundant Boolean call.",
34             unexpectedNegation: "Redundant double negation."
35         }
36     },
37
38     create(context) {
39         const sourceCode = context.getSourceCode();
40
41         // Node types which have a test which will coerce values to booleans.
42         const BOOLEAN_NODE_TYPES = [
43             "IfStatement",
44             "DoWhileStatement",
45             "WhileStatement",
46             "ConditionalExpression",
47             "ForStatement"
48         ];
49
50         /**
51          * Check if a node is in a context where its value would be coerced to a boolean at runtime.
52          * @param {ASTNode} node The node
53          * @param {ASTNode} parent Its parent
54          * @returns {boolean} If it is in a boolean context
55          */
56         function isInBooleanContext(node, parent) {
57             return (
58                 (BOOLEAN_NODE_TYPES.indexOf(parent.type) !== -1 &&
59                     node === parent.test) ||
60
61                 // !<bool>
62                 (parent.type === "UnaryExpression" &&
63                     parent.operator === "!")
64             );
65         }
66
67         /**
68          * Check if a node has comments inside.
69          * @param {ASTNode} node The node to check.
70          * @returns {boolean} `true` if it has comments inside.
71          */
72         function hasCommentsInside(node) {
73             return Boolean(sourceCode.getCommentsInside(node).length);
74         }
75
76         return {
77             UnaryExpression(node) {
78                 const ancestors = context.getAncestors(),
79                     parent = ancestors.pop(),
80                     grandparent = ancestors.pop();
81
82                 // Exit early if it's guaranteed not to match
83                 if (node.operator !== "!" ||
84                         parent.type !== "UnaryExpression" ||
85                         parent.operator !== "!") {
86                     return;
87                 }
88
89                 if (isInBooleanContext(parent, grandparent) ||
90
91                     // Boolean(<bool>) and new Boolean(<bool>)
92                     ((grandparent.type === "CallExpression" || grandparent.type === "NewExpression") &&
93                         grandparent.callee.type === "Identifier" &&
94                         grandparent.callee.name === "Boolean")
95                 ) {
96                     context.report({
97                         node: parent,
98                         messageId: "unexpectedNegation",
99                         fix: fixer => {
100                             if (hasCommentsInside(parent)) {
101                                 return null;
102                             }
103
104                             let prefix = "";
105                             const tokenBefore = sourceCode.getTokenBefore(parent);
106                             const firstReplacementToken = sourceCode.getFirstToken(node.argument);
107
108                             if (tokenBefore && tokenBefore.range[1] === parent.range[0] &&
109                                     !astUtils.canTokensBeAdjacent(tokenBefore, firstReplacementToken)) {
110                                 prefix = " ";
111                             }
112
113                             return fixer.replaceText(parent, prefix + sourceCode.getText(node.argument));
114                         }
115                     });
116                 }
117             },
118             CallExpression(node) {
119                 const parent = node.parent;
120
121                 if (node.callee.type !== "Identifier" || node.callee.name !== "Boolean") {
122                     return;
123                 }
124
125                 if (isInBooleanContext(node, parent)) {
126                     context.report({
127                         node,
128                         messageId: "unexpectedCall",
129                         fix: fixer => {
130                             if (!node.arguments.length) {
131                                 if (parent.type === "UnaryExpression" && parent.operator === "!") {
132
133                                     // !Boolean() -> true
134
135                                     if (hasCommentsInside(parent)) {
136                                         return null;
137                                     }
138
139                                     const replacement = "true";
140                                     let prefix = "";
141                                     const tokenBefore = sourceCode.getTokenBefore(parent);
142
143                                     if (tokenBefore && tokenBefore.range[1] === parent.range[0] &&
144                                             !astUtils.canTokensBeAdjacent(tokenBefore, replacement)) {
145                                         prefix = " ";
146                                     }
147
148                                     return fixer.replaceText(parent, prefix + replacement);
149                                 }
150
151                                 // Boolean() -> false
152                                 if (hasCommentsInside(node)) {
153                                     return null;
154                                 }
155                                 return fixer.replaceText(node, "false");
156                             }
157
158                             if (node.arguments.length > 1 || node.arguments[0].type === "SpreadElement" ||
159                                     hasCommentsInside(node)) {
160                                 return null;
161                             }
162
163                             const argument = node.arguments[0];
164
165                             if (astUtils.getPrecedence(argument) < astUtils.getPrecedence(node.parent)) {
166                                 return fixer.replaceText(node, `(${sourceCode.getText(argument)})`);
167                             }
168                             return fixer.replaceText(node, sourceCode.getText(argument));
169                         }
170                     });
171                 }
172             }
173         };
174
175     }
176 };