Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: no-extra-boolean-cast invalid autofix for Boolean() without args #12076

Merged
merged 3 commits into from Aug 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
49 changes: 44 additions & 5 deletions lib/rules/no-extra-boolean-cast.js
Expand Up @@ -50,8 +50,8 @@ module.exports = {
/**
* 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
* @param {ASTNode} node The node
* @param {ASTNode} parent Its parent
* @returns {boolean} If it is in a boolean context
*/
function isInBooleanContext(node, parent) {
Expand All @@ -65,6 +65,15 @@ module.exports = {
);
}

/**
* 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);
}

return {
UnaryExpression(node) {
Expand All @@ -89,7 +98,12 @@ module.exports = {
context.report({
node,
messageId: "unexpectedNegation",
fix: fixer => fixer.replaceText(parent, sourceCode.getText(node.argument))
fix: fixer => {
if (hasCommentsInside(parent)) {
return null;
}
return fixer.replaceText(parent, sourceCode.getText(node.argument));
}
});
}
},
Expand All @@ -106,10 +120,35 @@ module.exports = {
messageId: "unexpectedCall",
fix: fixer => {
if (!node.arguments.length) {
return fixer.replaceText(parent, "true");
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 || node.arguments[0].type === "SpreadElement") {
if (node.arguments.length > 1 || node.arguments[0].type === "SpreadElement" ||
hasCommentsInside(node)) {
return null;
}

Expand Down
308 changes: 308 additions & 0 deletions tests/lib/rules/no-extra-boolean-cast.js
Expand Up @@ -227,6 +227,314 @@ ruleTester.run("no-extra-boolean-cast", rule, {
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "if (!Boolean()) { foo() }",
output: "if (true) { foo() }",
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "while (!Boolean()) { foo() }",
output: "while (true) { foo() }",
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "var foo = Boolean() ? bar() : baz()",
output: "var foo = false ? bar() : baz()",
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "if (Boolean()) { foo() }",
output: "if (false) { foo() }",
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "while (Boolean()) { foo() }",
output: "while (false) { foo() }",
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},

// Adjacent tokens tests
{
code: "void!Boolean()",
output: "void true",
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "void! Boolean()",
output: "void true",
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "typeof!Boolean()",
output: "typeof true",
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "(!Boolean())",
output: "(true)",
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "+!Boolean()",
output: "+true",
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "void !Boolean()",
output: "void true",
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "void(!Boolean())",
output: "void(true)",
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "void/**/!Boolean()",
output: "void/**/true",
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},

// Comments tests
{
code: "!/**/!!foo",
output: "!/**/foo",
errors: [{
messageId: "unexpectedNegation",
type: "UnaryExpression"
}]
},
{
code: "!!/**/!foo",
output: null,
errors: [{
messageId: "unexpectedNegation",
type: "UnaryExpression"
}]
},
{
code: "!!!/**/foo",
output: null,
errors: [{
messageId: "unexpectedNegation",
type: "UnaryExpression"
}]
},
{
code: "!!!foo/**/",
output: "!foo/**/",
errors: [{
messageId: "unexpectedNegation",
type: "UnaryExpression"
}]
},
{
code: "if(!/**/!foo);",
output: null,
errors: [{
messageId: "unexpectedNegation",
type: "UnaryExpression"
}]
},
{
code: "(!!/**/foo ? 1 : 2)",
output: null,
errors: [{
messageId: "unexpectedNegation",
type: "UnaryExpression"
}]
},
{
code: "!/**/Boolean(foo)",
output: "!/**/foo",
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "!Boolean/**/(foo)",
output: null,
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "!Boolean(/**/foo)",
output: null,
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "!Boolean(foo/**/)",
output: null,
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "!Boolean(foo)/**/",
output: "!foo/**/",
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "if(Boolean/**/(foo));",
output: null,
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "(Boolean(foo/**/) ? 1 : 2)",
output: null,
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "/**/!Boolean()",
output: "/**/true",
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "!/**/Boolean()",
output: null,
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "!Boolean/**/()",
output: null,
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "!Boolean(/**/)",
output: null,
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "!Boolean()/**/",
output: "true/**/",
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "if(!/**/Boolean());",
output: null,
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "(!Boolean(/**/) ? 1 : 2)",
output: null,
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "if(/**/Boolean());",
output: "if(/**/false);",
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "if(Boolean/**/());",
output: null,
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "if(Boolean(/**/));",
output: null,
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "if(Boolean()/**/);",
output: "if(false/**/);",
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
},
{
code: "(Boolean/**/() ? 1 : 2)",
output: null,
errors: [{
messageId: "unexpectedCall",
type: "CallExpression"
}]
}
]
});