Skip to content
Permalink
Browse files

Update: Check extra Boolean calls in no-extra-boolean-cast (fixes #3650)

  • Loading branch information...
Met48 committed Dec 15, 2015
1 parent 0acbe57 commit 654e6e1f2e31850cafd9c2ca24ea88f8c8c14c96
Showing with 116 additions and 59 deletions.
  1. +19 −10 docs/rules/no-extra-boolean-cast.md
  2. +43 −36 lib/rules/no-extra-boolean-cast.js
  3. +54 −13 tests/lib/rules/no-extra-boolean-cast.js
@@ -1,47 +1,55 @@
# Disallow Extra Boolean Casts (no-extra-boolean-cast)

In contexts such as an `if` statement's test where the result of the expression will already be coerced to a Boolean, casting to a Boolean via double negation (`!!`) is unnecessary. For example, these `if` statements are equivalent:
In contexts such as an `if` statement's test where the result of the expression will already be coerced to a Boolean, casting to a Boolean via double negation (`!!`) or a `Boolean` call is unnecessary. For example, these `if` statements are equivalent:

```js
if (!!foo) {
// ...
}
if (Boolean(foo)) {
// ...
}
if (foo) {
// ...
}
```

## Rule Details

This rule aims to eliminate the use of double-negation Boolean casts in an already Boolean context.
This rule aims to eliminate the use of Boolean casts in an already Boolean context.

The following patterns are considered problems:

```js
/*eslint no-extra-boolean-cast: 2*/
var foo = !!!bar; /*error Redundant multiple negation.*/
var foo = !!!bar; /*error Redundant double negation.*/
var foo = !!bar ? baz : bat; /*error Redundant double negation in a ternary condition.*/
var foo = !!bar ? baz : bat; /*error Redundant double negation.*/
var foo = Boolean(!!bar); /*error Redundant double negation in call to Boolean().*/
var foo = Boolean(!!bar); /*error Redundant double negation.*/
var foo = new Boolean(!!bar); /*error Redundant double negation in Boolean constructor call.*/
var foo = new Boolean(!!bar); /*error Redundant double negation.*/
if (!!foo) { /*error Redundant double negation.*/
// ...
}
if (!!foo) { /*error Redundant double negation in an if statement condition.*/
if (Boolean(foo)) { /*error Redundant Boolean call.*/
// ...
}
while (!!foo) { /*error Redundant double negation in a while loop condition.*/
while (!!foo) { /*error Redundant double negation.*/
// ...
}
do {
// ...
} while (!!foo); /*error Redundant double negation in a do while loop condition.*/
} while (Boolean(foo)); /*error Redundant Boolean call.*/
for (; !!foo; ) { /*error Redundant double negation in a for loop condition.*/
for (; !!foo; ) { /*error Redundant double negation.*/
// ...
}
```
@@ -52,6 +60,7 @@ The following patterns are not considered problems:
/*eslint no-extra-boolean-cast: 2*/
var foo = !!bar;
var foo = Boolean(bar);
function foo() {
return !!bar;
@@ -11,6 +11,33 @@

module.exports = function(context) {

// Node types which have a test which will coerce values to booleans.
var BOOLEAN_NODE_TYPES = [
"IfStatement",
"DoWhileStatement",
"WhileStatement",
"ConditionalExpression",
"ForStatement"
];

/**
* Check if a node is in a context where its value would be coerced to a boolean at runtime.
*
* @param {Object} node The node
* @param {Object} parent Its parent
* @returns {Boolean} If it is in a boolean context
*/
function isInBooleanContext(node, parent) {
return (
(BOOLEAN_NODE_TYPES.indexOf(parent.type) !== -1 &&
node === parent.test) ||
// !<bool>
(parent.type === "UnaryExpression" &&
parent.operator === "!")
);
}


return {
"UnaryExpression": function(node) {
var ancestors = context.getAncestors(),
@@ -24,44 +51,24 @@ module.exports = function(context) {
return;
}

// if (<bool>) ...
if (grandparent.type === "IfStatement") {
context.report(node, "Redundant double negation in an if statement condition.");

// do ... while (<bool>)
} else if (grandparent.type === "DoWhileStatement") {
context.report(node, "Redundant double negation in a do while loop condition.");

// while (<bool>) ...
} else if (grandparent.type === "WhileStatement") {
context.report(node, "Redundant double negation in a while loop condition.");

// <bool> ? ... : ...
} else if ((grandparent.type === "ConditionalExpression" &&
parent === grandparent.test)) {
context.report(node, "Redundant double negation in a ternary condition.");

// for (...; <bool>; ...) ...
} else if ((grandparent.type === "ForStatement" &&
parent === grandparent.test)) {
context.report(node, "Redundant double negation in a for loop condition.");

// !<bool>
} else if ((grandparent.type === "UnaryExpression" &&
grandparent.operator === "!")) {
context.report(node, "Redundant multiple negation.");

// Boolean(<bool>)
} else if ((grandparent.type === "CallExpression" &&
if (isInBooleanContext(parent, grandparent) ||
// Boolean(<bool>) and new Boolean(<bool>)
((grandparent.type === "CallExpression" || grandparent.type === "NewExpression") &&
grandparent.callee.type === "Identifier" &&
grandparent.callee.name === "Boolean")) {
context.report(node, "Redundant double negation in call to Boolean().");
grandparent.callee.name === "Boolean")
) {
context.report(node, "Redundant double negation.");
}
},
"CallExpression": function(node) {
var parent = node.parent;

// new Boolean(<bool>)
} else if ((grandparent.type === "NewExpression" &&
grandparent.callee.type === "Identifier" &&
grandparent.callee.name === "Boolean")) {
context.report(node, "Redundant double negation in Boolean constructor call.");
if (node.callee.type !== "Identifier" || node.callee.name !== "Boolean") {
return;
}

if (isInBooleanContext(node, parent)) {
context.report(node, "Redundant Boolean call.");
}
}
};
@@ -24,71 +24,112 @@ ruleTester.run("no-extra-boolean-cast", rule, {
"function foo() { return !!bar; }",
"var foo = bar() ? !!baz : !!bat",
"for(!!foo;;) {}",
"for(;; !!foo) {}"
"for(;; !!foo) {}",
"var foo = Boolean(bar);",
"function foo() { return Boolean(bar); }",
"var foo = bar() ? Boolean(baz) : Boolean(bat)",
"for(Boolean(foo);;) {}",
"for(;; Boolean(foo)) {}",
"if (new Boolean(foo)) {}"
],

invalid: [
{
code: "if (!!foo) {}",
errors: [{
message: "Redundant double negation in an if statement condition.",
message: "Redundant double negation.",
type: "UnaryExpression"
}]
},
{
code: "do {} while (!!foo)",
errors: [{
message: "Redundant double negation in a do while loop condition.",
message: "Redundant double negation.",
type: "UnaryExpression"
}]
},
{
code: "while (!!foo) {}",
errors: [{
message: "Redundant double negation in a while loop condition.",
message: "Redundant double negation.",
type: "UnaryExpression"
}]
},
{
code: "!!foo ? bar : baz",
errors: [{
message: "Redundant double negation in a ternary condition.",
message: "Redundant double negation.",
type: "UnaryExpression"
}]
},
{
code: "for (; !!foo;) {}",
errors: [{
message: "Redundant double negation in a for loop condition.",
message: "Redundant double negation.",
type: "UnaryExpression"
}]
},
{
code: "!!!foo",
errors: [{
message: "Redundant multiple negation.",
message: "Redundant double negation.",
type: "UnaryExpression"
}]
},
{
code: "Boolean(!!foo)",
errors: [{
message: "Redundant double negation in call to Boolean().",
message: "Redundant double negation.",
type: "UnaryExpression"
}]
},
{
code: "Boolean(!!foo)",
code: "new Boolean(!!foo)",
errors: [{
message: "Redundant double negation in call to Boolean().",
message: "Redundant double negation.",
type: "UnaryExpression"
}]
},
{
code: "new Boolean(!!foo)",
code: "if (Boolean(foo)) {}",
errors: [{
message: "Redundant double negation in Boolean constructor call.",
type: "UnaryExpression"
message: "Redundant Boolean call.",
type: "CallExpression"
}]
},
{
code: "do {} while (Boolean(foo))",
errors: [{
message: "Redundant Boolean call.",
type: "CallExpression"
}]
},
{
code: "while (Boolean(foo)) {}",
errors: [{
message: "Redundant Boolean call.",
type: "CallExpression"
}]
},
{
code: "Boolean(foo) ? bar : baz",
errors: [{
message: "Redundant Boolean call.",
type: "CallExpression"
}]
},
{
code: "for (; Boolean(foo);) {}",
errors: [{
message: "Redundant Boolean call.",
type: "CallExpression"
}]
},
{
code: "!Boolean(foo)",
errors: [{
message: "Redundant Boolean call.",
type: "CallExpression"
}]
}
]

0 comments on commit 654e6e1

Please sign in to comment.
You can’t perform that action at this time.