UNPKG

4.42 kBJavaScriptView Raw
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
12const astUtils = require("./utils/ast-utils");
13
14//------------------------------------------------------------------------------
15// Rule Definition
16//------------------------------------------------------------------------------
17
18module.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 *
53 * @param {Object} node The node
54 * @param {Object} parent Its parent
55 * @returns {boolean} If it is in a boolean context
56 */
57 function isInBooleanContext(node, parent) {
58 return (
59 (BOOLEAN_NODE_TYPES.indexOf(parent.type) !== -1 &&
60 node === parent.test) ||
61
62 // !<bool>
63 (parent.type === "UnaryExpression" &&
64 parent.operator === "!")
65 );
66 }
67
68
69 return {
70 UnaryExpression(node) {
71 const ancestors = context.getAncestors(),
72 parent = ancestors.pop(),
73 grandparent = ancestors.pop();
74
75 // Exit early if it's guaranteed not to match
76 if (node.operator !== "!" ||
77 parent.type !== "UnaryExpression" ||
78 parent.operator !== "!") {
79 return;
80 }
81
82 if (isInBooleanContext(parent, grandparent) ||
83
84 // Boolean(<bool>) and new Boolean(<bool>)
85 ((grandparent.type === "CallExpression" || grandparent.type === "NewExpression") &&
86 grandparent.callee.type === "Identifier" &&
87 grandparent.callee.name === "Boolean")
88 ) {
89 context.report({
90 node,
91 messageId: "unexpectedNegation",
92 fix: fixer => fixer.replaceText(parent, sourceCode.getText(node.argument))
93 });
94 }
95 },
96 CallExpression(node) {
97 const parent = node.parent;
98
99 if (node.callee.type !== "Identifier" || node.callee.name !== "Boolean") {
100 return;
101 }
102
103 if (isInBooleanContext(node, parent)) {
104 context.report({
105 node,
106 messageId: "unexpectedCall",
107 fix: fixer => {
108 if (!node.arguments.length) {
109 return fixer.replaceText(parent, "true");
110 }
111
112 if (node.arguments.length > 1 || node.arguments[0].type === "SpreadElement") {
113 return null;
114 }
115
116 const argument = node.arguments[0];
117
118 if (astUtils.getPrecedence(argument) < astUtils.getPrecedence(node.parent)) {
119 return fixer.replaceText(node, `(${sourceCode.getText(argument)})`);
120 }
121 return fixer.replaceText(node, sourceCode.getText(argument));
122 }
123 });
124 }
125 }
126 };
127
128 }
129};