1 | /**
|
2 | * @fileoverview Rule to flag unnecessary double negation in Boolean contexts
|
3 | * @author Brandon Mills
|
4 | */
|
5 |
|
6 | ;
|
7 |
|
8 | //------------------------------------------------------------------------------
|
9 | // Rule Definition
|
10 | //------------------------------------------------------------------------------
|
11 |
|
12 | module.exports = {
|
13 | meta: {
|
14 | docs: {
|
15 | description: "disallow unnecessary boolean casts",
|
16 | category: "Possible Errors",
|
17 | recommended: true
|
18 | },
|
19 |
|
20 | schema: []
|
21 | },
|
22 |
|
23 | create(context) {
|
24 |
|
25 | // Node types which have a test which will coerce values to booleans.
|
26 | const BOOLEAN_NODE_TYPES = [
|
27 | "IfStatement",
|
28 | "DoWhileStatement",
|
29 | "WhileStatement",
|
30 | "ConditionalExpression",
|
31 | "ForStatement"
|
32 | ];
|
33 |
|
34 | /**
|
35 | * Check if a node is in a context where its value would be coerced to a boolean at runtime.
|
36 | *
|
37 | * @param {Object} node The node
|
38 | * @param {Object} parent Its parent
|
39 | * @returns {boolean} If it is in a boolean context
|
40 | */
|
41 | function isInBooleanContext(node, parent) {
|
42 | return (
|
43 | (BOOLEAN_NODE_TYPES.indexOf(parent.type) !== -1 &&
|
44 | node === parent.test) ||
|
45 |
|
46 | // !<bool>
|
47 | (parent.type === "UnaryExpression" &&
|
48 | parent.operator === "!")
|
49 | );
|
50 | }
|
51 |
|
52 |
|
53 | return {
|
54 | UnaryExpression(node) {
|
55 | const ancestors = context.getAncestors(),
|
56 | parent = ancestors.pop(),
|
57 | grandparent = ancestors.pop();
|
58 |
|
59 | // Exit early if it's guaranteed not to match
|
60 | if (node.operator !== "!" ||
|
61 | parent.type !== "UnaryExpression" ||
|
62 | parent.operator !== "!") {
|
63 | return;
|
64 | }
|
65 |
|
66 | if (isInBooleanContext(parent, grandparent) ||
|
67 |
|
68 | // Boolean(<bool>) and new Boolean(<bool>)
|
69 | ((grandparent.type === "CallExpression" || grandparent.type === "NewExpression") &&
|
70 | grandparent.callee.type === "Identifier" &&
|
71 | grandparent.callee.name === "Boolean")
|
72 | ) {
|
73 | context.report(node, "Redundant double negation.");
|
74 | }
|
75 | },
|
76 | CallExpression(node) {
|
77 | const parent = node.parent;
|
78 |
|
79 | if (node.callee.type !== "Identifier" || node.callee.name !== "Boolean") {
|
80 | return;
|
81 | }
|
82 |
|
83 | if (isInBooleanContext(node, parent)) {
|
84 | context.report(node, "Redundant Boolean call.");
|
85 | }
|
86 | }
|
87 | };
|
88 |
|
89 | }
|
90 | };
|